Private/Get-Utils.ps1
# # (c) Copyright 2018-2021 Hewlett Packard Enterprise Development LP # All Rights Reserved # $JSON_HEADER = 'application/vnd.simplivity.v1.10+json' $JSON_API15_HEADER = 'application/vnd.simplivity.v1.15+json' $JSON_API16_HEADER = 'application/vnd.simplivity.v1.16+json' $JSON_API17_HEADER = 'application/vnd.simplivity.v1.17+json' $BEGIN_CERTIFICATE = '-----BEGIN CERTIFICATE-----' $END_CERTIFICATE = '-----END CERTIFICATE-----' $CRED_XML_PATH = Join-Path $HOME HPESvtCertificate.ps1.credential $VALID_RBAC_ROLES = 'Administrator', 'BackupUser' function SetCredXml { param( $configObject ) if (!(Get-IsPowerShellCore)) { #Export/Import-Clixml with encrypted content not supported on PowerShell Core $configObject.AccessToken = ConvertTo-SecureString $configObject.AccessToken -AsPlainText -Force } $configObject | Export-Clixml $CRED_XML_PATH -Force return $CRED_XML_PATH } function GetCredXml { if (![System.IO.File]::Exists($CRED_XML_PATH)) { throw "Could not authenticate. Run Get-HPESvtAuthToken to authenticate." } $configObject = Import-Clixml $CRED_XML_PATH if (!(Get-IsPowerShellCore)) { #Export/Import-Clixml with encrypted content not supported on PowerShell Core $configObject.AccessToken = (New-Object PSCredential "user",$configObject.AccessToken).GetNetworkCredential().Password } return $configObject } function Get-IsPowerShellCore { $PSVersionTable.PSEdition -and $PsVersionTable.PSEdition -eq 'Core' } function Get-BaseUrl { param( [string] $hostname ) 'https://' + $hostname + '/api' } function Get-CertificateUrl { param( [string] $hostname ) $baseUrl = Get-BaseUrl $hostname return $baseUrl + '/certificates' } function Get-RbacUrl { param( [string] $hostname ) $baseUrl = Get-BaseUrl $hostname return $baseUrl + '/security/rbac' } function Get-RbacRoleAssignmentUrl { param( [string] $hostname ) $baseUrl = Get-RbacUrl $hostname return $baseUrl + '/policies/role_assignments' } function Get-RbacRoleRemovalUrl { param( [string] $hostname ) $baseUrl = Get-RbacUrl $hostname return $baseUrl + '/policies/role_assignments/remove' } function Get-Header { param( [string] $accessToken ) @{ 'Authorization' = ("Bearer {0}" -f $accessToken); 'Accept'=$JSON_HEADER } } # Add -SkipCertificateCheck flag if on PowerShell Core. This flag only exists in PowerShell 6.0 or later. # There is an alternate way to do this on earlier versions of PowerShell, so just set this for PowerShell Core. function Get-SkipCertificateFlag { $skipCertificateFlag = @{} if (Get-IsPowerShellCore) { $skipCertificateFlag.add("SkipCertificateCheck", $true) } $skipCertificateFlag } # Skip certificate checking for 5.1. For some reason the standard way of disabling certificate checking does not # work when connecting to the VmWare HMS, but this snippet of C# does. # Force Tls version to 1.2. This fixes a defect when connecting to a system with Tls 1.0 disabled. function Skip-CertificateCheck { Add-Type @" using System; using System.Net; using System.Net.Security; using System.Security.Cryptography.X509Certificates; public class ServerCertificateValidationCallback { public static void Ignore() { ServicePointManager.ServerCertificateValidationCallback += delegate ( Object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors ) { return true; }; } } "@ [ServerCertificateValidationCallback]::Ignore(); [System.Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 } # Get X509Certificate2 from base64 encoded string from the server. # This will include Begin/End Certificate strings and newlines that need to be trimmed. function Get-CertificateFromBase64String { param( [string] $base64String ) $base64String = $base64String.Trim() $base64String = $base64String.Trim($BEGIN_CERTIFICATE) $base64String = $base64String.Trim($END_CERTIFICATE) $byteArray = [System.Convert]::FromBase64String($base64String) $cert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::New($byteArray) Write-Output $cert } # see table at https://docs.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed function Get-IsRunningNet47 { # Core has everything we need for 4.7 interop if (Get-IsPowerShellCore) { return; } $running = Get-CheckDotNetVersion -version 460805 if ($running -eq $false) { throw "This cmdlet requires .net 4.7" } } function Get-CheckDotNetVersion { param( [int] $version ) try { $installed = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full') if ($version -gt $installed.Release) { return $false } return $true } catch { Write-Error "Error while determining .NET version" } return $false } function Copy-Hashtable { param($DeepCopyObject) $memStream = new-object IO.MemoryStream $formatter = new-object Runtime.Serialization.Formatters.Binary.BinaryFormatter $formatter.Serialize($memStream,$DeepCopyObject) $memStream.Position=0 $formatter.Deserialize($memStream) } function Approve-HostOAuthToken { param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [HashTable] $HostOAuthToken ) # Validate the 'Hostname' portion of the Host OAuth Token composite. $hostname = $HostOAuthToken['Hostname'] if (-not $hostname) { throw "HostOAuthToken hash table missing 'Hostname' key" } if ([string]::IsNullOrWhiteSpace($hostname)) { throw "HostOAuthToken hash table 'Hostname' value contains only white space" } # Validate the 'OAuthHeader' portion of the Host OAuth Token composite. if (-not $HostOAuthToken['OAuthHeader']) { throw "HostOAuthToken hash table missing 'OAuthHeader' key" } if ($HostOAuthToken['OAuthHeader'].GetType().FullName -ne 'System.Collections.Hashtable') { throw "HostOAuthToken hash table 'OAuthHeader' value is not a hash table" } if ($HostOAuthToken['OAuthHeader'].Count -lt 1) { throw "HostOAuthToken hash table 'OAuthHeader' value is expected to have at least one entry" } if (-not ($HostOAuthToken['OAuthHeader']['Authorization'])) { throw "HostOAuthToken hash table 'OAuthHeader' value is missing the 'Authorization' key" } if ([string]::IsNullOrWhiteSpace($HostOAuthToken['OAuthHeader']['Authorization'])) { throw "HostOAuthToken hash table 'OAuthHeader' value hash table 'Authorization' value contains only white space" } if (-not ($HostOAuthToken['OAuthHeader']['Authorization']).StartsWith("Bearer ")) { throw "HostOAuthToken hash table 'OAuthHeader' value hash table 'Authorization' value is invalid" } } function Get-PkiUrl { param( [string] $hostname ) $baseUrl = Get-BaseUrl $hostname return $baseUrl + '/security/pki/ca' } function Get-HostsUrl { param( [string] $hostname ) $baseUrl = Get-BaseUrl $hostname return $baseUrl + '/hosts' } function Get-VersionUrl { param( [string] $hostname ) $baseUrl = Get-BaseUrl $hostname return $baseUrl + '/version' } function Get-TokenUrl { param( [string] $hostname ) $baseUrl = Get-BaseUrl $hostname return $baseUrl + '/oauth/token' } function Get-TokenRevokeUrl { param( [string] $hostname ) $baseUrl = Get-BaseUrl $hostname return $baseUrl + '/oauth/revoke' } function Get-MvaNodeUrl { param( [string] $hostname ) $baseUrl = Get-BaseUrl $hostname return $baseUrl + '/configuration/mva/nodes' } # Helper function to return the embedded error message in the body of the response from the API, rather # than a generic runtime (404) error. function Get-SvtError { [CmdletBinding()] Param ( [Parameter(Mandatory = $true, Position = 0)] [System.Object]$Err ) #$VerbosePreference = 'Continue' if (-not (Get-IsPowerShellCore)) { if ($Err.Exception.Response) { $Result = $Err.Exception.Response.GetResponseStream() $Reader = New-Object System.IO.StreamReader($Result) $Reader.BaseStream.Position = 0 $Reader.DiscardBufferedData() $ResponseBody = $Reader.ReadToEnd() Write-Verbose $ResponseBody if ($ResponseBody.StartsWith('{')) { $ResponseBody = $ResponseBody | ConvertFrom-Json } return $ResponseBody.Message } else { return $Err.Exception.Message #This return incase there is no response or host is not not reachable } } else { # PowerShell V6 doesn't support GetResponseStream(), so return the generic runtime error return $Err.Exception.Message } } # Helper function for Invoke-RestMethod to handle REST errors in one place. The calling function # then re-throws the error, generated here. This cmdlet either outputs a custom task object if the # REST API response is a task object, or otherwise the raw JSON. function Invoke-SvtRestMethod { [CmdletBinding()] param ( [ValidateNotNullOrEmpty()] [Parameter(Mandatory=$true, Position = 0, HelpMessage='Input a valid host OAuth token composite')] [HashTable] $HostOAuthToken, [Parameter(Mandatory = $true, Position = 1)] [System.Object]$Uri, [Parameter(Mandatory = $false, Position = 2)] [System.Collections.IDictionary]$Header, [Parameter(Mandatory = $true, Position = 3)] [ValidateSet('get', 'post', 'delete', 'put')] [System.String]$Method, [Parameter(Mandatory = $false, Position = 4)] [System.Object]$Body ) [System.int32]$Retrycount = 0 [bool]$Stoploop = $false do { try { if ($PSBoundParameters.ContainsKey('Body')) { $Response = Invoke-RestMethod -Uri $Uri -Headers $Header -Body $Body -Method $Method -ErrorAction Stop } else { $Response = Invoke-RestMethod -Uri $Uri -Headers $Header -Method $Method -ErrorAction Stop } $Stoploop = $true } catch [System.Management.Automation.RuntimeException] { if ($_.Exception.Message -match "Unauthorized") { if ($Retrycount -ge 3) { # Exit after 3 retries throw "Runtime error: Session expired and could not reconnect" } else { $Retrycount += 1 Write-Verbose "Session expired, reconnecting..." Renew-SvtSession -HostOAuthToken $HostOAuthToken $Header.Authorization = "Bearer " + $HostOAuthToken.AccessToken } } else { throw "Runtime error: $(Get-SvtError($_))" } } catch { throw "An unexpected error occurred: $($_.Exception.Message)" } } until ($Stoploop -eq $true) # If the JSON output is a task, convert it to a custom object of type 'HPE.SimpliVity.Task' and pass this # back to the calling cmdlet. A lot of cmdlets produce task object types, so this cuts out repetition # in the module. # Note: $Response.task is incorrectly true with /api/omnistack_clusters/throughput, so added a check for this. if ($Response.task -and $URI -notmatch '/api/omnistack_clusters/throughput') { $LocalFormat = Get-SvtLocalDateFormat $Response.task | ForEach-Object { if ($_.start_time -as [datetime]) { $StartTime = Get-Date -Date $_.start_time -Format $LocalFormat } else { $StartTime = $null } if ($_.end_time -as [datetime]) { $EndTime = Get-Date -Date $_.end_time -Format $LocalFormat } else { $EndTime = $null } [PSCustomObject]@{ StartTime = $StartTime TaskResult = $_.task_results AffectedObjects = $_.affected_objects OwnerId = $_.owner_id DestinationId = $_.destination_id Name = $_.name EndTime = $EndTime ErrorCode = $_.error_code ErrorMessage = $_.error_message State = $_.state TaskId = $_.id Type = $_.type PercentComplete = $_.percent_complete SubTasks = $_.sub_tasks } } } else { # For all other object types, return the raw JSON output for the calling cmdlet to deal with $Response } } # Helper function that returns the local date format. Used by cmdlets that return date properties function Get-SvtLocalDateFormat { $Culture = (Get-Culture).DateTimeFormat $LocalDate = "$($Culture.ShortDatePattern)" -creplace '^d/', 'dd/' -creplace '^M/', 'MM/' -creplace '/d/', '/dd/' $LocalTime = "$($Culture.LongTimePattern)" -creplace '^h:mm', 'hh:mm' -creplace '^H:mm', 'HH:mm' return "$LocalDate $LocalTime" } #Helper function to identify the IP/Hostname provided is an MVA or SVA function Identify-Host { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string] $Hostname ) $response = Get-SvtVersion -Hostname $Hostname $response | ForEach-Object { if ($_.Aggregator_Version) { return "MVA" } elseif ($_.SVTFS_Version) { return "SVA" } else { throw "The provided IP/hostname is not an SVA/MVA" } } } #Helper function to get the version information from the SVA/MVA IP/hostname provided. function Get-SvtVersion { [CmdletBinding()] param ( [Parameter(Mandatory = $true, HelpMessage = "Input the MVA/SVA Hostname/IP")] [Alias("mva", "ovc")] [string] $Hostname ) $url = Get-VersionUrl -hostname "$Hostname" try { $response = Invoke-RestMethod -Uri $url -Method Get -ErrorAction Stop } catch { throw "The host $Hostname is not reachable or not a MVA or SVA" + $_.Exception.Message } return $response } function Get-SvtVersionUrl { param( [string] $hostname ) $baseUrl = Get-BaseUrl $hostname return $baseUrl + '/version' } function Get-SvtPolicyUrl { param( [string] $hostname ) $baseUrl = Get-BaseUrl $hostname return $baseUrl + '/policies' } function Check-ClusterTopology { param( [ValidateNotNullOrEmpty()] [Parameter(Mandatory=$true, HelpMessage='Input a valid host OAuth token composite')] [HashTable] $HostOAuthToken ) $Url = Get-MvaNodeUrl -Hostname $HostOAuthToken.Hostname $Header = @{ 'Authorization' = "Bearer " + $HostOAuthToken.AccessToken 'Accept' = 'application/json' } #Multifed check try { $Response = Invoke-SvtRestMethod -HostOAuthToken $HostOAuthToken -Uri $Url -Header $Header -Method Get -ErrorAction Stop } catch { throw ($_) } if ($Response.cluster_topology -eq "MULTI_FED") { return $true } return $false } #Function to pick a single cluster from a list of Cluster objects. #This will be used to select a cluster where there are more than one cluster with same name function Select-Cluster { param ( [Parameter(Mandatory = $true)] $ClusterList #List of HPE Simplivity cluster object from Get-Omnistack cluster list ) $ClusterCount = ($ClusterList | Measure-Object).count Write-Warning ("There are multiple HPE SimpliVity clusters with the Name $ClusterName") -WarningAction "Continue" foreach ($Count in 0..($ClusterCount - 1)) { Write-host (($Count + 1).ToString() + ". " + $ClusterList[$Count].HypervisorManagementName + "/" + $ClusterList[$Count].DataCenterName + "/" + $ClusterList[$Count].ClusterName) } if ($ClusterCount -gt 9) { throw("There are more than nine HPE SimpliVity clusters with the name $ClusterName. Use HPE SimpliVity cluster id instead of cluster name in the command line") } Write-host ("Select a HPE SimpliVity cluster from the above list to apply configuration changes (Select between 1 to $ClusterCount)") do { [Int]$Selection = Get-KeyStroke if (($Selection -lt 1) -or ($Selection -gt $ClusterCount)) { Write-Warning ("Input a valid number from 1 to $ClusterCount") } } while (($Selection -lt 1) -or ($Selection -gt $ClusterCount)) Write-Host ("Selected Cluster is : " + $ClusterList[($Selection - 1)].HypervisorManagementName + "/" + $ClusterList[($Selection - 1)].DataCenterName + "/" + $ClusterList[($Selection - 1)].ClusterName) return $ClusterList[($Selection - 1)] } #Helper function to take the key stroke from the command line with default timeout of 60 seconds. function Get-KeyStroke { param ( [Int]$Timeout = 60, [Int]$SleepTime = 1 ) [int]$KeyOut = 0 $Timer = [Diagnostics.Stopwatch]::StartNew() do { $TotalSecs = [Math]::Round($Timer.Elapsed.TotalSeconds, 0) if ([Console]::KeyAvailable){ $Key = $Host.UI.RawUI.ReadKey("IncludeKeyDown") Write-host("`n") if ([int]::TryParse($Key.Character, [ref]$KeyOut)) { break } else { Write-Warning ("Invalid input") } } Start-Sleep $SleepTime } while($Timer.Elapsed.TotalSeconds -lt $Timeout) $Timer.Stop() if($Timer.Elapsed.TotalSeconds -gt $Timeout) { throw ("Timed out for the user input") } return $KeyOut } function Invoke-SvtRestMethodWithRetry { [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 1)] [ValidateSet('get', 'post', 'delete', 'put')] [String]$Method, [Parameter(Mandatory = $true, Position = 2)] [String]$Uri, [Parameter(Mandatory = $false, Position = 3)] [Hashtable]$Headers, [Parameter(Mandatory = $false, Position = 4)] [Object]$Body, [Parameter(Mandatory = $false, Position = 5)] [Int32]$MaximumRetries = 12, [Parameter(Mandatory = $false, Position = 6)] [Int32]$SleepIntervalInSecs = 10, [Parameter(Mandatory = $false, Position = 7)] [Int32[]]$ErrorCodesOnRetry = @(503) ) [Int32]$Retrycount = 0 [bool]$Stoploop = $false do { try { $skipCertParam = Get-SkipCertificateFlag $Response = Invoke-RestMethod -Uri $Uri -Headers $Headers -Body $Body -Method $Method @skipCertParam $Stoploop = $true Write-Output $Response } catch { $Stoploop = $true if ($Retrycount -lt $MaximumRetries) { if ($_.Exception.Response) { $errorCode = $_.Exception.Response.StatusCode.value__; if ($ErrorCodesOnRetry.contains($errorCode)) { $Retrycount = $Retrycount + 1 Write-Host "Server ($Method $Uri) unavailable! will reconnect after $SleepIntervalInSecs seconds, attempt #$Retrycount/$MaximumRetries" Start-Sleep -Seconds $SleepIntervalInSecs $Stoploop = $false } else { throw $_.Exception } } else { throw $_.Exception } } else { throw $_.Exception } } } While ($Stoploop -eq $false) } function Get-GenerateCSRUrl { param( [string] $hostname ) $baseUrl = Get-BaseUrl $hostname return $baseUrl + '/security/pki/generate_csr' } function Get-TLSCertChainUrl { param( [string] $hostname ) $baseUrl = Get-BaseUrl $hostname return $baseUrl + '/security/pki/import_tls_cert_chain' } function Get-VerifyCRLUrl { param( [string] $hostname ) $baseUrl = Get-BaseUrl $hostname return $baseUrl + '/security/pki/verify_crl' } # SIG # Begin signature block # MIIlSAYJKoZIhvcNAQcCoIIlOTCCJTUCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDKKm64NMb58ETX # OD+D3a1lJgwOhOPYwvBRwftqrFrFxKCCEKwwggUqMIIEEqADAgECAhEAh0L0QnA4 # GzyXlRYB5/Z3IjANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJHQjEbMBkGA1UE # CBMSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRgwFgYDVQQK # Ew9TZWN0aWdvIExpbWl0ZWQxJDAiBgNVBAMTG1NlY3RpZ28gUlNBIENvZGUgU2ln # bmluZyBDQTAeFw0yMTA1MjgwMDAwMDBaFw0yMjA1MjgyMzU5NTlaMIGQMQswCQYD # VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJUGFsbyBBbHRv # MSswKQYDVQQKDCJIZXdsZXR0IFBhY2thcmQgRW50ZXJwcmlzZSBDb21wYW55MSsw # KQYDVQQDDCJIZXdsZXR0IFBhY2thcmQgRW50ZXJwcmlzZSBDb21wYW55MIIBIjAN # BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuy98a4/YGCzF1KvXZeoHxDtk6qL4 # PSuU32Ob9Fzpxq36JZIs6oezkvBnNVlf4iuaWSk3UyqK/JpKpbdz1dB/cuB2s+PV # aVR9goUC494Qi7HVDeFmomdvH2Xt0r+R6VUDtpxnR/XmZrZjXKP5T2Yk+XDMl78R # XT9ayu3ZZncwTdYauSBk1sJq1Vid3gHnNrrA5DmidVwxmu5Y9XvIjcrEtECPm4hx # sV2ISbMnnuzM0bvisxwAAho74TfswOyBSy182sFGZPbmwkyJvW1RhPV+2POqSaOR # CXvSad2KJUWtxgqfOGOU+hAP+sHvg5MbXDj6I+RSx9dJAu5WKcffyp506QIDAQAB # o4IBkDCCAYwwHwYDVR0jBBgwFoAUDuE6qFM6MdWKvsG7rWcaA4WtNA4wHQYDVR0O # BBYEFE32P36Oqh0//kySCDZQvL8TnjDgMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMB # Af8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMBEGCWCGSAGG+EIBAQQEAwIEEDBK # BgNVHSAEQzBBMDUGDCsGAQQBsjEBAgEDAjAlMCMGCCsGAQUFBwIBFhdodHRwczov # L3NlY3RpZ28uY29tL0NQUzAIBgZngQwBBAEwQwYDVR0fBDwwOjA4oDagNIYyaHR0 # cDovL2NybC5zZWN0aWdvLmNvbS9TZWN0aWdvUlNBQ29kZVNpZ25pbmdDQS5jcmww # cwYIKwYBBQUHAQEEZzBlMD4GCCsGAQUFBzAChjJodHRwOi8vY3J0LnNlY3RpZ28u # Y29tL1NlY3RpZ29SU0FDb2RlU2lnbmluZ0NBLmNydDAjBggrBgEFBQcwAYYXaHR0 # cDovL29jc3Auc2VjdGlnby5jb20wDQYJKoZIhvcNAQELBQADggEBABmxgISN4Ehp # rP/zvy+4wqQItJDH30z1qv02yZXsA988VQ8uq3Iof7x1tvTsNo06hcNhqXWx547s # p3DU/FmteG8LTT4iATHZNc6OHdZ+A4IYOAmUNhIPCMT/DLgunRjnLJPHPVYkRiKu # a0Ggzwa2a4CsV2z9DB1erpbX9jsOLS21ENqRIYcgipSVNo1cJYVdtWi7INWwuqcy # juJcy0ZOfKlPMr7Mn4i3L1HPzcoWAkZPBqefO/KrDAhM5qW07+YYZAH7pcBdQcLu # m4+LlAT51Ha9gi1mcr2WcJEOnUtazO/TVcr7T/0RlUMuLRfSkCdQymUDx0TGwSME # elGewxW1JUYwggWBMIIEaaADAgECAhA5ckQ6+SK3UdfTbBDdMTWVMA0GCSqGSIb3 # DQEBDAUAMHsxCzAJBgNVBAYTAkdCMRswGQYDVQQIDBJHcmVhdGVyIE1hbmNoZXN0 # ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoMEUNvbW9kbyBDQSBMaW1pdGVk # MSEwHwYDVQQDDBhBQUEgQ2VydGlmaWNhdGUgU2VydmljZXMwHhcNMTkwMzEyMDAw # MDAwWhcNMjgxMjMxMjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5l # dyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNF # UlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNh # dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCA # EmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B3PHTsdZ7 # NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkYtJHUYmTb # f6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/Fp0YvVGO # NaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2VN3I5xI6 # Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT79uq/nRO # acdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6c0Plfg6l # ZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmTYo61Zs8l # iM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97lc6wjOy0A # vzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4eeUB9XVKg+ # /XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeEHg9j1uli # utZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo4HyMIHvMB8GA1Ud # IwQYMBaAFKARCiM+lvEH7OKvKe+CpX/QMKS0MB0GA1UdDgQWBBRTeb9aqitKz1SA # 4dibwJ3ysgNmyzAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zARBgNV # HSAECjAIMAYGBFUdIAAwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21v # ZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEE # KDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZI # hvcNAQEMBQADggEBABiHUdx0IT2ciuAntzPQLszs8ObLXhHeIm+bdY6ecv7k1v6q # H5yWLe8DSn6u9I1vcjxDO8A/67jfXKqpxq7y/Njuo3tD9oY2fBTgzfT3P/7euLSK # 8JGW/v1DZH79zNIBoX19+BkZyUIrE79Yi7qkomYEdoiRTgyJFM6iTckys7roFBq8 # cfFb8EELmAAKIgMQ5Qyx+c2SNxntO/HkOrb5RRMmda+7qu8/e3c70sQCkT0ZANMX # XDnbP3sYDUXNk4WWL13fWRZPP1G91UUYP+1KjugGYXQjFrUNUHMnREd/EF2JKmuF # MRTE6KlqTIC8anjPuH+OdnKZDJ3+15EIFqGjX5UwggX1MIID3aADAgECAhAdokgw # b5smGNCC4JZ9M9NqMA0GCSqGSIb3DQEBDAUAMIGIMQswCQYDVQQGEwJVUzETMBEG # A1UECBMKTmV3IEplcnNleTEUMBIGA1UEBxMLSmVyc2V5IENpdHkxHjAcBgNVBAoT # FVRoZSBVU0VSVFJVU1QgTmV0d29yazEuMCwGA1UEAxMlVVNFUlRydXN0IFJTQSBD # ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xODExMDIwMDAwMDBaFw0zMDEyMzEy # MzU5NTlaMHwxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0 # ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDEk # MCIGA1UEAxMbU2VjdGlnbyBSU0EgQ29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG # 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhiKNMoV6GJ9J8JYvYwgeLdx8nxTP4ya2JWYp # QIZURnQxYsUQ7bKHJ6aZy5UwwFb1pHXGqQ5QYqVRkRBq4Etirv3w+Bisp//uLjMg # +gwZiahse60Aw2Gh3GllbR9uJ5bXl1GGpvQn5Xxqi5UeW2DVftcWkpwAL2j3l+1q # cr44O2Pej79uTEFdEiAIWeg5zY/S1s8GtFcFtk6hPldrH5i8xGLWGwuNx2YbSp+d # gcRyQLXiX+8LRf+jzhemLVWwt7C8VGqdvI1WU8bwunlQSSz3A7n+L2U18iLqLAev # Rtn5RhzcjHxxKPP+p8YU3VWRbooRDd8GJJV9D6ehfDrahjVh0wIDAQABo4IBZDCC # AWAwHwYDVR0jBBgwFoAUU3m/WqorSs9UgOHYm8Cd8rIDZsswHQYDVR0OBBYEFA7h # OqhTOjHVir7Bu61nGgOFrTQOMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAG # AQH/AgEAMB0GA1UdJQQWMBQGCCsGAQUFBwMDBggrBgEFBQcDCDARBgNVHSAECjAI # MAYGBFUdIAAwUAYDVR0fBEkwRzBFoEOgQYY/aHR0cDovL2NybC51c2VydHJ1c3Qu # Y29tL1VTRVJUcnVzdFJTQUNlcnRpZmljYXRpb25BdXRob3JpdHkuY3JsMHYGCCsG # AQUFBwEBBGowaDA/BggrBgEFBQcwAoYzaHR0cDovL2NydC51c2VydHJ1c3QuY29t # L1VTRVJUcnVzdFJTQUFkZFRydXN0Q0EuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8v # b2NzcC51c2VydHJ1c3QuY29tMA0GCSqGSIb3DQEBDAUAA4ICAQBNY1DtRzRKYaTb # 3moqjJvxAAAeHWJ7Otcywvaz4GOz+2EAiJobbRAHBE++uOqJeCLrD0bs80ZeQEaJ # EvQLd1qcKkE6/Nb06+f3FZUzw6GDKLfeL+SU94Uzgy1KQEi/msJPSrGPJPSzgTfT # t2SwpiNqWWhSQl//BOvhdGV5CPWpk95rcUCZlrp48bnI4sMIFrGrY1rIFYBtdF5K # dX6luMNstc/fSnmHXMdATWM19jDTz7UKDgsEf6BLrrujpdCEAJM+U100pQA1aWy+ # nyAlEA0Z+1CQYb45j3qOTfafDh7+B1ESZoMmGUiVzkrJwX/zOgWb+W/fiH/AI57S # HkN6RTHBnE2p8FmyWRnoao0pBAJ3fEtLzXC+OrJVWng+vLtvAxAldxU0ivk2zEOS # 5LpP8WKTKCVXKftRGcehJUBqhFfGsp2xvBwK2nxnfn0u6ShMGH7EezFBcZpLKewL # PVdQ0srd/Z4FUeVEeN0B3rF1mA1UJP3wTuPi+IO9crrLPTru8F4XkmhtyGH5pvEq # CgulufSe7pgyBYWe6/mDKdPGLH29OncuizdCoGqC7TtKqpQQpOEN+BfFtlp5MxiS # 47V1+KHpjgolHuQe8Z9ahyP/n6RRnvs5gBHN27XEp6iAb+VT1ODjosLSWxr6MiYt # aldwHDykWC6j81tLB9wyWfOHpxptWDGCE/IwghPuAgEBMIGRMHwxCzAJBgNVBAYT # AkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZv # cmQxGDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDEkMCIGA1UEAxMbU2VjdGlnbyBS # U0EgQ29kZSBTaWduaW5nIENBAhEAh0L0QnA4GzyXlRYB5/Z3IjANBglghkgBZQME # AgEFAKB8MBAGCisGAQQBgjcCAQwxAjAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3 # AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEi # BCBNnxB7ZXCUCoepOdqkiSBOWC4IDDayt7sTpifaJOHpEDANBgkqhkiG9w0BAQEF # AASCAQAZpSr/Xvdu9SwxnI4iQZ5yVGIQSu6oMpkh06Bfe8DpvLcoI4OJZUaHIHiY # Io5aSrHpj1L7Np/FKvP9/9cM3fNAmOuQ58xO4Xse4JW2I78/NptqymCca+YlXG6h # Ug6lbvOW/yMIt7zDcnmLE3vQGySaegGZTOl26Wu5YbCqhYUojusnP5EHp3H+oMg4 # 9qsiL+z5jHsaaYnGjSxQOFkDuhdFAvZ8el36nAcUM8VYhISGT9InZzlZ0uttU1tA # eoJExIfSwJFZpqxTcTJWXkUpIfNwL5Pg5e80CtEU2oEj4MKdxs72lQ64jWYHqhWV # RXtyonv4CIkwFeDJmWNP/Fv5JbWSoYIRszCCEa8GCisGAQQBgjcDAwExghGfMIIR # mwYJKoZIhvcNAQcCoIIRjDCCEYgCAQMxDzANBglghkgBZQMEAgEFADB4BgsqhkiG # 9w0BCRABBKBpBGcwZQIBAQYJYIZIAYb9bAcBMDEwDQYJYIZIAWUDBAIBBQAEIAri # iVSESrRBbwRfJri73TF5BhUSXisE4BRGNueNbjd/AhEA7wm9ggCoNaps4Qrook7e # pRgPMjAyMjA0MDcwNjE0NTlaoIINfDCCBsYwggSuoAMCAQICEAp6SoieyZlCkAZj # OE2Gl50wDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRp # Z2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQw # OTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQTAeFw0yMjAzMjkwMDAwMDBaFw0zMzAz # MTQyMzU5NTlaMEwxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5j # LjEkMCIGA1UEAxMbRGlnaUNlcnQgVGltZXN0YW1wIDIwMjIgLSAyMIICIjANBgkq # hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuSqWI6ZcvF/WSfAVghj0M+7MXGzj4CUu # 0jHkPECu+6vE43hdflw26vUljUOjges4Y/k8iGnePNIwUQ0xB7pGbumjS0joiUF/ # DbLW+YTxmD4LvwqEEnFsoWImAdPOw2z9rDt+3Cocqb0wxhbY2rzrsvGD0Z/NCcW5 # QWpFQiNBWvhg02UsPn5evZan8Pyx9PQoz0J5HzvHkwdoaOVENFJfD1De1FksRHTA # MkcZW+KYLo/Qyj//xmfPPJOVToTpdhiYmREUxSsMoDPbTSSF6IKU4S8D7n+FAsmG # 4dUYFLcERfPgOL2ivXpxmOwV5/0u7NKbAIqsHY07gGj+0FmYJs7g7a5/KC7CnuAL # S8gI0TK7g/ojPNn/0oy790Mj3+fDWgVifnAs5SuyPWPqyK6BIGtDich+X7Aa3Rm9 # n3RBCq+5jgnTdKEvsFR2wZBPlOyGYf/bES+SAzDOMLeLD11Es0MdI1DNkdcvnfv8 # zbHBp8QOxO9APhk6AtQxqWmgSfl14ZvoaORqDI/r5LEhe4ZnWH5/H+gr5BSyFtaB # ocraMJBr7m91wLA2JrIIO/+9vn9sExjfxm2keUmti39hhwVo99Rw40KV6J67m0uy # 4rZBPeevpxooya1hsKBBGBlO7UebYZXtPgthWuo+epiSUc0/yUTngIspQnL3ebLd # hOon7v59emsCAwEAAaOCAYswggGHMA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8E # AjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMCAGA1UdIAQZMBcwCAYGZ4EMAQQC # MAsGCWCGSAGG/WwHATAfBgNVHSMEGDAWgBS6FtltTYUvcyl2mi91jGogj57IbzAd # BgNVHQ4EFgQUjWS3iSH+VlhEhGGn6m8cNo/drw0wWgYDVR0fBFMwUTBPoE2gS4ZJ # aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0UlNBNDA5 # NlNIQTI1NlRpbWVTdGFtcGluZ0NBLmNybDCBkAYIKwYBBQUHAQEEgYMwgYAwJAYI # KwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBYBggrBgEFBQcwAoZM # aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0UlNB # NDA5NlNIQTI1NlRpbWVTdGFtcGluZ0NBLmNydDANBgkqhkiG9w0BAQsFAAOCAgEA # DS0jdKbR9fjqS5k/AeT2DOSvFp3Zs4yXgimcQ28BLas4tXARv4QZiz9d5YZPvpM6 # 3io5WjlO2IRZpbwbmKrobO/RSGkZOFvPiTkdcHDZTt8jImzV3/ZZy6HC6kx2yqHc # oSuWuJtVqRprfdH1AglPgtalc4jEmIDf7kmVt7PMxafuDuHvHjiKn+8RyTFKWLbf # OHzL+lz35FO/bgp8ftfemNUpZYkPopzAZfQBImXH6l50pls1klB89Bemh2RPPkaJ # FmMga8vye9A140pwSKm25x1gvQQiFSVwBnKpRDtpRxHT7unHoD5PELkwNuTzqmkJ # qIt+ZKJllBH7bjLx9bs4rc3AkxHVMnhKSzcqTPNc3LaFwLtwMFV41pj+VG1/calI # GnjdRncuG3rAM4r4SiiMEqhzzy350yPynhngDZQooOvbGlGglYKOKGukzp123qlz # qkhqWUOuX+r4DwZCnd8GaJb+KqB0W2Nm3mssuHiqTXBt8CzxBxV+NbTmtQyimaXX # FWs1DoXW4CzM4AwkuHxSCx6ZfO/IyMWMWGmvqz3hz8x9Fa4Uv4px38qXsdhH6hyF # 4EVOEhwUKVjMb9N/y77BDkpvIJyu2XMyWQjnLZKhGhH+MpimXSuX4IvTnMxttQ2u # R2M4RxdbbxPaahBuH0m3RFu0CAqHWlkEdhGhp3cCExwwggauMIIElqADAgECAhAH # Nje3JFR82Ees/ShmKl5bMA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUw # EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x # ITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yMjAzMjMwMDAw # MDBaFw0zNzAzMjIyMzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdp # Q2VydCwgSW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2 # IFNIQTI1NiBUaW1lU3RhbXBpbmcgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw # ggIKAoICAQDGhjUGSbPBPXJJUVXHJQPE8pE3qZdRodbSg9GeTKJtoLDMg/la9hGh # RBVCX6SI82j6ffOciQt/nR+eDzMfUBMLJnOWbfhXqAJ9/UO0hNoR8XOxs+4rgISK # Ihjf69o9xBd/qxkrPkLcZ47qUT3w1lbU5ygt69OxtXXnHwZljZQp09nsad/ZkIdG # AHvbREGJ3HxqV3rwN3mfXazL6IRktFLydkf3YYMZ3V+0VAshaG43IbtArF+y3kp9 # zvU5EmfvDqVjbOSmxR3NNg1c1eYbqMFkdECnwHLFuk4fsbVYTXn+149zk6wsOeKl # SNbwsDETqVcplicu9Yemj052FVUmcJgmf6AaRyBD40NjgHt1biclkJg6OBGz9vae # 5jtb7IHeIhTZgirHkr+g3uM+onP65x9abJTyUpURK1h0QCirc0PO30qhHGs4xSnz # yqqWc0Jon7ZGs506o9UD4L/wojzKQtwYSH8UNM/STKvvmz3+DrhkKvp1KCRB7UK/ # BZxmSVJQ9FHzNklNiyDSLFc1eSuo80VgvCONWPfcYd6T/jnA+bIwpUzX6ZhKWD7T # A4j+s4/TXkt2ElGTyYwMO1uKIqjBJgj5FBASA31fI7tk42PgpuE+9sJ0sj8eCXbs # q11GdeJgo1gJASgADoRU7s7pXcheMBK9Rp6103a50g5rmQzSM7TNsQIDAQABo4IB # XTCCAVkwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUuhbZbU2FL3Mpdpov # dYxqII+eyG8wHwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0P # AQH/BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAk # BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAC # hjVodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9v # dEc0LmNydDBDBgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5j # b20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEE # AjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBAH1ZjsCTtm+YqUQiAX5m # 1tghQuGwGC4QTRPPMFPOvxj7x1Bd4ksp+3CKDaopafxpwc8dB+k+YMjYC+VcW9dt # h/qEICU0MWfNthKWb8RQTGIdDAiCqBa9qVbPFXONASIlzpVpP0d3+3J0FNf/q0+K # LHqrhc1DX+1gtqpPkWaeLJ7giqzl/Yy8ZCaHbJK9nXzQcAp876i8dU+6WvepELJd # 6f8oVInw1YpxdmXazPByoyP6wCeCRK6ZJxurJB4mwbfeKuv2nrF5mYGjVoarCkXJ # 38SNoOeY+/umnXKvxMfBwWpx2cYTgAnEtp/Nh4cku0+jSbl3ZpHxcpzpSwJSpzd+ # k1OsOx0ISQ+UzTl63f8lY5knLD0/a6fxZsNBzU+2QJshIUDQtxMkzdwdeDrknq3l # NHGS1yZr5Dhzq6YBT70/O3itTK37xJV77QpfMzmHQXh6OOmc4d0j/R0o08f56PGY # X/sr2H7yRp11LB4nLCbbbxV7HhmLNriT1ObyF5lZynDwN7+YAN8gFk8n+2BnFqFm # ut1VwDophrCYoCvtlUG3OtUVmDG0YgkPCr2B2RP+v6TR81fZvAT6gt4y3wSJ8ADN # XcL50CN/AAvkdgIm2fBldkKmKYcJRyvmfxqkhQ/8mJb2VVQrH4D6wPIOK+XW+6kv # RBVK5xMOHds3OBqhK/bt1nz8MYIDdjCCA3ICAQEwdzBjMQswCQYDVQQGEwJVUzEX # MBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFRydXN0 # ZWQgRzQgUlNBNDA5NiBTSEEyNTYgVGltZVN0YW1waW5nIENBAhAKekqInsmZQpAG # YzhNhpedMA0GCWCGSAFlAwQCAQUAoIHRMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0B # CRABBDAcBgkqhkiG9w0BCQUxDxcNMjIwNDA3MDYxNDU5WjArBgsqhkiG9w0BCRAC # DDEcMBowGDAWBBSFCPOGUVyz0wd9trS3wH8bSl5B3jAvBgkqhkiG9w0BCQQxIgQg # VIIsdkg+u8l/LpdtKutgUahsxLQctv5eZhzB0C1KJv8wNwYLKoZIhvcNAQkQAi8x # KDAmMCQwIgQgnaaQFcNJxsGJeEW6NYKtcMiPpCk722q+nCvSU5J55jswDQYJKoZI # hvcNAQEBBQAEggIATvmscJ1QxoCY0oc/q8tTKGt+y767djteEuJOK+89cIArEEHb # T3KYGPccW5vP7r5jpG6tj14gEuBCnqy0q3K7ohVyWM3mGSUQDDYABc/TWqulqvk2 # EtKSrGphK3SXNilD3ifpZF63k9HHE+2C7B1iMjr2GwjTU2yQ9yFc7ylBkNOsu95Y # af9MYB1Xc22lFaiN6rO3GZ+7T6hA/yeUBPU6l9w6doYf4XOMk3y1nct3UDk4ZHjn # 2xAIzUlCKWYGb7jb9D3nKtxd9ZT1XSf4Xp6JFp9LdJ7FEekzAAYXC6jJEo5Ex0Vu # 664GAw3HlArYgdp93Y2+9F66qXfq2ayGM77kpcKgKJw0SV+oPLmBg/bWE28LGy8Q # S2aeFQl4jXIPjQ398/qByXlEBTyNYoteWun47H+kwhDtYL4trr30CDchrLtHQOTv # b5UCEi9jdncrKn4sO3j9M69cke5+7QuUcNCg0x4p3LeOY4rUXSy9LtHXMSl10LFY # zTPSyP6mcaT0wPn5+N8YKnz/7X2Cek3o34vDDSRmx/Oq24kI1gPfQGlURvXJzpEd # wcUA3dCQzAXPQOEcS9zTDO+1i/ZnLpu1XFAEHqFOSVg8aCvEAkdAf7OCsUlkn2rc # YukXlI9cqV/jB9gN9+qCq1dhg/3NxO0g4XOu27t4Z5uG1XH4P3ZysvREiog= # SIG # End signature block |