CommonLibrary/AzureLocal.EnvValidator.CommonLibrary.Network.psm1
|
<#############################################################
# # # Copyright (C) Microsoft Corporation. All rights reserved. # # # #############################################################> ################################################ ####### Main functions below, exported ####### ################################################ function EnvValidatorNwkLibCheckTcpConnectionWithRetries { <# Checks TCP connection to the given destination and port. #> param( [System.String] $SourceIp, [System.String] $DestinationIp, [System.Int16] $PortToCheck = 53, [System.Int16] $RetryTimes = 10, [System.Int16] $IntervalBetweenRetry = 3 ) [System.Boolean] $retVal = $false $retry = 1 while ((-not $retVal) -and ($retry -le $RetryTimes)) { try { $src = [System.Net.IPEndPoint]::new([ipaddress]::Parse($SourceIp),0) $tc = [System.Net.Sockets.TcpClient]::new($src) $tc.Connect($DestinationIp, $PortToCheck) if ($tc.Connected) { Log-Info " == TCP connection ESTABLISHED from $($SourceIp) to $($DestinationIp) port $($PortToCheck) on attempt $($retry)" $retVal = $true break } else { Log-Info " ?? FAILED TCP connection from $($SourceIp) to $($DestinationIp) port $($PortToCheck) on attempt $($retry)" } } catch { Log-Info " ?? FAILED! Got exception while checking TCP connection from $($SourceIp) to $($DestinationIp) port $($PortToCheck) on attempt ($($retry))!" } finally { if ($tc) { $tc.Dispose() } } Start-Sleep -Seconds $IntervalBetweenRetry $retry++ } return $retVal } function EnvValidatorNwkLibConfigureVMSwitchForTesting { [CmdletBinding()] param ( [System.String[]] $SwitchAdapterNames, [System.String] $MgmtIntentName = "", [System.String] $ExpectedVMSwitchName = "ConvergedSwitch($($MgmtIntentName))", [System.String] $ExpectedMgmtVNicName = "vManagement($($MgmtIntentName))" ) Import-Module -Name Hyper-V -Force -Verbose:$false -ErrorAction SilentlyContinue | Out-Null Import-Module -Name NetAdapter -Force -Verbose:$false -ErrorAction SilentlyContinue | Out-Null Import-Module -Name NetTCPIP -Force -Verbose:$false -ErrorAction SilentlyContinue | Out-Null [PSObject] $retVal = New-Object PSObject -Property @{ VMSwitchInfo = $null MgmtVlanId = 0 NeedCleanUp = $false IPReady = $false } # Make sure VMMS service is running [System.Boolean] $vmmsRunning = $false $vmmsStopWatch = [System.diagnostics.stopwatch]::StartNew() while (-not $vmmsRunning -and ($vmmsStopWatch.Elapsed.TotalSeconds -lt 60)) { [PSObject[]] $vmmsService = @() try { $vmmsService = Get-Service -Name "vmms" -ErrorAction SilentlyContinue } catch { } if ($vmmsService.Count -eq 1) { $vmmsRunning = $vmmsService[0].Status -eq "Running" } if ($vmmsRunning) { break } else { Restart-Service -Name vmms -ErrorAction SilentlyContinue Start-Sleep -Seconds 5 } } $mgmtVlanId = 0 $existingPhysicalNICVlanId = Get-NetAdapterAdvancedProperty -RegistryKeyword VlanID -Name $SwitchAdapterNames[0] -ErrorAction SilentlyContinue if ($existingPhysicalNICVlanId -and $existingPhysicalNICVlanId.RegistryValue) { $mgmtVlanId = $existingPhysicalNICVlanId.RegistryValue[0] } $tmpVMSwitch = New-VMSwitch -Name $ExpectedVMSwitchName -NetAdapterName $SwitchAdapterNames -EnableEmbeddedTeaming $true -AllowManagementOS $true if ($tmpVMSwitch) { $retVal.VMSwitchInfo = $tmpVMSwitch $retVal.MgmtVlanId = 0 $retVal.NeedCleanUp = $true Get-VMNetworkAdapter -ManagementOS -Name $ExpectedVMSwitchName | Rename-VMNetworkAdapter -NewName $ExpectedMgmtVNicName Get-NetAdapter -Name "vEthernet ($($ExpectedMgmtVNicName))" -ErrorAction SilentlyContinue | Rename-NetAdapter -NewName $ExpectedMgmtVNicName if ($mgmtVlanId -ne 0) { Set-VMNetworkAdapterIsolation -ManagementOS ` -VMNetworkAdapterName $ExpectedMgmtVNicName ` -IsolationMode Vlan ` -AllowUntaggedTraffic $true ` -DefaultIsolationID $mgmtVlanId # Save the VLAN ID info to return value $retVal.MgmtVlanId = $mgmtVlanId } # In case of DHCP scenario, the new adapter might not get the IP address immediately # Wait for some time (60 seconds) to make sure the new IP is settled correctly. [System.Boolean] $currentIPReady = $false $ipStopWatch = [System.diagnostics.stopwatch]::StartNew() while (-not $currentIPReady -and ($ipStopWatch.Elapsed.TotalSeconds -lt 60)) { # If the vNIC has Manual or Dhcp IPv4 address with "Preferred" state, we consider it as "ready" $ipConfig = Get-NetIPAddress -InterfaceAlias $ExpectedMgmtVNicName -ErrorAction SilentlyContinue | Where-Object { ($_.PrefixOrigin -eq "Manual" -or $_.PrefixOrigin -eq "Dhcp") -and $_.AddressFamily -eq "IPv4" -and $_.AddressState -eq "Preferred" } if ($ipConfig) { $currentIPReady = $true $retVal.IPReady = $true break } else { Start-Sleep -Seconds 3 } } if (-not $currentIPReady) { # should not get into here, but keep it here for safety Write-Host "Cannot get the IP address bind to the vNIC after VMSwitch created. Please check the system manually." } else { Write-Host "[$($env:COMPUTERNAME)] VMSwitch created successfully. VMSwitch: $($ExpectedVMSwitchName), MgmtVNic: $($ExpectedMgmtVNicName)" } # We need to manually set the VLAN ID of the pNIC used in VMSwitch to 0 now, otherwise if the vNIC is using a different VLANID, the traffic will not go through # Ideally, pNIC should have correct VLAN ID configured and if that is the case, we don't need to set it to 0. # But we see some customers has the VLANID set incorrectly, causing the vNIC traffic not going through after we set the vNIC isolation ID. # So we want to set the VLAN ID of the pNIC to 0 after VMSwitch SET created. foreach ($tempPhysicalNIC in $SwitchAdapterNames) { Set-NetAdapterAdvancedProperty -Name $tempPhysicalNIC -RegistryKeyword "VLANID" -RegistryValue 0 -ErrorAction SilentlyContinue } } return $retVal } function EnvValidatorNwkLibConvertIPAddressToInt { param ( [Parameter(Mandatory=$true)] [System.Net.IPAddress] $IPAddress ) $bytes = $IPAddress.GetAddressBytes() [Array]::Reverse($bytes) return [BitConverter]::ToUInt32($bytes, 0) } function EnvValidatorNwkLibConvertToPrefixLength { param( [System.Net.IPAddress] $SubnetMask ) $Bits = "$($SubnetMask.GetAddressBytes() | ForEach-Object {[Convert]::ToString($_, 2)})" -Replace '[\s0]' return $Bits.Length } function EnvValidatorNwkLibConvertPrefixLengthToSubnetMask { <# .SYNOPSIS Parses a CIDR string and returns the normalized network address and subnet mask. .DESCRIPTION Given a CIDR notation string (e.g. "10.10.30.5/24"), this function normalizes the network address (using EnvValidatorNwkLibNormalizeIPv4Subnet) and converts the prefix length to a subnet mask string. Returns a hashtable with NetworkAddress and SubnetMask. .PARAMETER CidrNotation IPv4 CIDR notation string, e.g. "10.10.30.0/24". .EXAMPLE $result = EnvValidatorNwkLibConvertPrefixLengthToSubnetMask -CidrNotation "10.10.30.5/24" # $result.NetworkAddress = "10.10.30.0" # $result.SubnetMask = "255.255.255.0" # $result.PrefixLength = 24 #> param( [Parameter(Mandatory=$true)] [System.String] $CidrNotation ) # Normalize the CIDR to get the correct network address $normalizedCidr = EnvValidatorNwkLibNormalizeIPv4Subnet -cidrSubnet $CidrNotation $parts = $normalizedCidr -split '/' $networkAddress = $parts[0] $prefixLen = [int]$parts[1] # Convert prefix length to subnet mask $maskBits = [UInt32]::MaxValue -shl (32 - $prefixLen) $bytes = [System.BitConverter]::GetBytes($maskBits) [Array]::Reverse($bytes) $subnetMask = ([System.Net.IPAddress]$bytes).IPAddressToString return @{ NetworkAddress = $networkAddress SubnetMask = $subnetMask PrefixLength = $prefixLen } } function EnvValidatorNwkLibGenerateRandomIPInSubnet { <# .SYNOPSIS Generates a random host IP address within the given CIDR subnet. .DESCRIPTION Accepts a CIDR notation string (which may not be normalized), validates and normalizes it, then returns a random usable host IP within that subnet. Network address and broadcast address are excluded. .PARAMETER CidrNotation IPv4 CIDR notation string, e.g. "10.10.30.5/24" or "192.168.1.0/28". .EXAMPLE $ip = EnvValidatorNwkLibGenerateRandomIPInSubnet -CidrNotation "10.10.30.5/24" # Returns a random IP like "10.10.30.142" #> param( [Parameter(Mandatory=$true)] [System.String] $CidrNotation ) # Normalize and parse the CIDR $normalizedCidr = EnvValidatorNwkLibNormalizeIPv4Subnet -cidrSubnet $CidrNotation $parts = $normalizedCidr -split '/' $networkAddress = $parts[0] $prefixLen = [int]$parts[1] if ($prefixLen -ge 31) { throw "Subnet /$prefixLen is too small to generate a random host IP. Minimum prefix length is /30." } $networkInt = EnvValidatorNwkLibConvertIPAddressToInt ([System.Net.IPAddress]::Parse($networkAddress)) # Host range: network+1 to broadcast-1 $hostCount = [Math]::Pow(2, (32 - $prefixLen)) - 2 # exclude network and broadcast $randomOffset = [System.Random]::new().Next(1, [int]$hostCount + 1) # 1..hostCount inclusive $randomInt = $networkInt + [UInt32]$randomOffset return (EnvValidatorNwkLibConvertIntToIPAddressString $randomInt) } function EnvValidatorNwkLibCreateAtcHostIntentsInfoFromSystem { Import-Module -Name NetworkATC -Force -Verbose:$false -Scope Global -ErrorAction SilentlyContinue | Out-Null [PSObject[]] $atcHostIntents = Get-NetIntent [PSObject[]] $allIntentInfo = @() foreach ($intent in $atcHostIntents) { [PSObject] $currentIntentInfo = New-Object PSObject $currentIntentInfo | Add-Member -MemberType NoteProperty -Name "Name" -Value $intent.IntentName [String[]] $currentIntentType = @() if ($intent.IsManagementIntentSet) { $currentIntentType += "Management" } if ($intent.IsStorageIntentSet) { $currentIntentType += "Storage" } if ($intent.IsComputeIntentSet) { $currentIntentType += "Compute" } $currentIntentInfo | Add-Member -MemberType NoteProperty -Name "TrafficType" -Value $currentIntentType $currentIntentInfo | Add-Member -MemberType NoteProperty -Name "Adapter" -Value $intent.NetAdapterNamesAsList # Check if the intent has any overrides, note that we only convert the overrides that we supported right now if (($null -ne $intent.AdapterAdvancedParametersOverride.NetworkDirect) -or ($null -ne $intent.AdapterAdvancedParametersOverride.JumboPacket)) { [PSObject] $tempAdapterOverride = New-Object PSObject if ($null -ne $intent.AdapterAdvancedParametersOverride.JumboPacket) { $tempAdapterOverride | Add-Member -MemberType NoteProperty -Name "JumboPacket" -Value $intent.AdapterAdvancedParametersOverride.JumboPacket } else { $tempAdapterOverride | Add-Member -MemberType NoteProperty -Name "JumboPacket" -Value $null } if ($null -ne $intent.AdapterAdvancedParametersOverride.NetworkDirect) { $tempAdapterOverride | Add-Member -MemberType NoteProperty -Name "NetworkDirect" -Value $intent.AdapterAdvancedParametersOverride.NetworkDirect } else { $tempAdapterOverride | Add-Member -MemberType NoteProperty -Name "NetworkDirect" -Value $null } if ($null -ne $intent.AdapterAdvancedParametersOverride.NetworkDirectTechnology) { $tempAdapterOverride | Add-Member -MemberType NoteProperty -Name "NetworkDirectTechnology" -Value $intent.AdapterAdvancedParametersOverride.NetworkDirectTechnology } else { $tempAdapterOverride | Add-Member -MemberType NoteProperty -Name "NetworkDirectTechnology" -Value $null } $currentIntentInfo | Add-Member -MemberType NoteProperty -Name "AdapterPropertyOverrides" -Value $tempAdapterOverride $currentIntentInfo | Add-Member -MemberType NoteProperty -Name "OverrideAdapterProperty" -Value $true } else { $currentIntentInfo | Add-Member -MemberType NoteProperty -Name "AdapterPropertyOverrides" -Value $null $currentIntentInfo | Add-Member -MemberType NoteProperty -Name "OverrideAdapterProperty" -Value $false } if (($null -ne $intent.QosPolicyOverride.PriorityValue8021Action_SMB) -or ($null -ne $intent.QosPolicyOverride.PriorityValue8021Action_Cluster) -or ($null -ne $intent.QosPolicyOverride.BandwidthPercentage_SMB)) { [PSObject] $tempQosOverride = New-Object PSObject if ($null -ne $intent.QosPolicyOverride.PriorityValue8021Action_SMB) { $tempQosOverride | Add-Member -MemberType NoteProperty -Name "PriorityValue8021Action_SMB" -Value $intent.QosPolicyOverride.PriorityValue8021Action_SMB } else { $tempQosOverride | Add-Member -MemberType NoteProperty -Name "PriorityValue8021Action_SMB" -Value $null } if ($null -ne $intent.QosPolicyOverride.PriorityValue8021Action_Cluster) { $tempQosOverride | Add-Member -MemberType NoteProperty -Name "PriorityValue8021Action_Cluster" -Value $intent.QosPolicyOverride.PriorityValue8021Action_Cluster } else { $tempQosOverride | Add-Member -MemberType NoteProperty -Name "PriorityValue8021Action_Cluster" -Value $null } if ($null -ne $intent.QosPolicyOverride.BandwidthPercentage_SMB) { $tempQosOverride | Add-Member -MemberType NoteProperty -Name "BandwidthPercentage_SMB" -Value $intent.QosPolicyOverride.BandwidthPercentage_SMB } else { $tempQosOverride | Add-Member -MemberType NoteProperty -Name "BandwidthPercentage_SMB" -Value $null } $currentIntentInfo | Add-Member -MemberType NoteProperty -Name "QoSPolicyOverrides" -Value $tempQosOverride $currentIntentInfo | Add-Member -MemberType NoteProperty -Name "OverrideQoSPolicy" -Value $true } else { $currentIntentInfo | Add-Member -MemberType NoteProperty -Name "QoSPolicyOverrides" -Value $null $currentIntentInfo | Add-Member -MemberType NoteProperty -Name "OverrideQoSPolicy" -Value $false } if (($null -ne $intent.SwitchConfigOverride.EnableIov) -or ($null -ne $intent.SwitchConfigOverride.LoadBalancingAlgorithm)) { [PSObject] $tempVMSwitchOverride = New-Object PSObject if ($null -ne $intent.SwitchConfigOverride.EnableIov) { $tempVMSwitchOverride | Add-Member -MemberType NoteProperty -Name "EnableIov" -Value $intent.SwitchConfigOverride.EnableIov } else { $tempVMSwitchOverride | Add-Member -MemberType NoteProperty -Name "EnableIov" -Value $null } if ($null -ne $intent.SwitchConfigOverride.LoadBalancingAlgorithm) { $tempVMSwitchOverride | Add-Member -MemberType NoteProperty -Name "LoadBalancingAlgorithm" -Value $intent.SwitchConfigOverride.LoadBalancingAlgorithm } else { $tempVMSwitchOverride | Add-Member -MemberType NoteProperty -Name "LoadBalancingAlgorithm" -Value $null } $currentIntentInfo | Add-Member -MemberType NoteProperty -Name "VirtualSwitchConfigurationOverrides" -Value $tempVMSwitchOverride $currentIntentInfo | Add-Member -MemberType NoteProperty -Name "OverrideVirtualSwitchConfiguration" -Value $true } else { $currentIntentInfo | Add-Member -MemberType NoteProperty -Name "VirtualSwitchConfigurationOverrides" -Value $null $currentIntentInfo | Add-Member -MemberType NoteProperty -Name "OverrideVirtualSwitchConfiguration" -Value $false } $allIntentInfo += $currentIntentInfo } return $allIntentInfo } function EnvValidatorNwkLibEnsureTestSessionOpen { <# .SYNOPSIS Make sure the test session is opened for the given PSSessions .DESCRIPTION Make sure the test session is opened for the given PSSessions. If the session is not opened, open a new session for it. .PARAMETER PSSessions The PSSessions to be checked .EXAMPLE EnvValidatorNwkLibEnsureTestSessionOpen -PSSessions $PSSessions #> [CmdletBinding()] param ( [System.Management.Automation.Runspaces.PSSession[]] $PSSessions ) [System.Management.Automation.Runspaces.PSSession[]] $newTestSessionsAfterChecking = @() foreach ($testSession in $PSSessions) { [System.Management.Automation.Runspaces.PSSession] $sessionToReturn = $null Log-Info "[EnvValidatorNwkLibEnsureTestSessionOpen] Clean up PSSession on $($testSession.ComputerName) and create a new session" Remove-PSSession -Session $testSession -ErrorAction SilentlyContinue $sessionCredential = $testSession.Runspace.ConnectionInfo.Credential if ($sessionCredential) { $sessionToReturn = EnvValidatorNwkLibNewPsSessionWithRetries -Node $testSession.ComputerName -Credential $sessionCredential } else { $sessionToReturn = EnvValidatorNwkLibNewPsSessionWithRetries -Node $testSession.ComputerName } $newTestSessionsAfterChecking += $sessionToReturn } return $newTestSessionsAfterChecking } function EnvValidatorNwkLibGetIpRange { param ( [Parameter(Mandatory = $false, HelpMessage = "Specify starting Management IP Range")] [System.Net.IPAddress] $StartingAddress, [Parameter(Mandatory = $false, HelpMessage = "Specify end Management IP Range")] [System.Net.IPAddress] $EndingAddress ) try { # Convert to unsigned 32-bit integer $startInt = EnvValidatorNwkLibConvertIPAddressToInt -IPAddress $StartingAddress $endInt = EnvValidatorNwkLibConvertIPAddressToInt -IPAddress $EndingAddress # Build list of IPs [System.String[]] $range = @() for ($i = $startInt; $i -le $endInt; $i++) { [System.String] $currentIpString = EnvValidatorNwkLibConvertIntToIPAddressString -Value $i $range += $currentIpString } return $range } catch { throw "[EnvValidatorNwkLibGetIpRange] Failed to get management IP range for start IP $($StartingAddress) and end IP $($EndingAddress). Error: $_" } } function EnvValidatorNwkLibGetMgmtIpRangeFromPools { param ( [Parameter(Mandatory = $false, HelpMessage = "Specify starting Management IP Range")] [System.Collections.ArrayList] $IpPools ) $result = @() foreach ($ipPool in $IpPools) { $result += EnvValidatorNwkLibGetIpRange -StartingAddress $ipPool.StartingAddress -EndingAddress $ipPool.EndingAddress } return $result } function EnvValidatorNwkLibGetNetworkAddress { param ( [Parameter(Mandatory=$true)] [System.Net.IPAddress] $IPAddress, [Parameter(Mandatory=$true)] [UInt32] $PrefixLength ) $value = EnvValidatorNwkLibConvertIPAddressToInt $IPAddress $networkMask = [Convert]::ToUInt32(("1" * $PrefixLength).PadRight(32, "0"), 2) $transformedValue = $value -band $networkMask return (EnvValidatorNwkLibConvertIntToIPAddressString $transformedValue) } function EnvValidatorNwkLibGetSortedMgmtIntentAdapter { param ( [System.String[]] $MgmtAdapterNames ) # Re-arrange the order in $MgmtAdapterNames to make sure the nic having a valid IPv4 address appears before the other NIC in the array $mgmtNicNamesTemp = [System.Collections.ArrayList] $MgmtAdapterNames foreach($name in $MgmtAdapterNames) { $a = Get-NetIPAddress -InterfaceAlias $name -AddressFamily ipv4 -Type Unicast -AddressState Preferred -PrefixOrigin Dhcp -ErrorAction SilentlyContinue $b = Get-NetIPAddress -InterfaceAlias $name -AddressFamily ipv4 -Type Unicast -AddressState Preferred -PrefixOrigin Manual -ErrorAction SilentlyContinue if (($null -ne $a) -or ($null -ne $b)) { # move the NIC name to the top $mgmtNicNamesTemp.Remove($name) $mgmtNicNamesTemp.Insert(0, $name) break } } [System.String[]] $retVal = [System.String[]] $mgmtNicNamesTemp return $retVal } function EnvValidatorNwkLibInvokePing { <# Runs ping command with the given parameters and returns the result. This wrapper allows ping to be mocked. #> param( [string]$Destination, [string]$Source, [int]$Count = 2, [int]$TimeoutMs = 2000 ) return ping $Destination -S $Source -n $Count -w $TimeoutMs } function EnvValidatorNwkLibInvokePingWithRetries { <# Runs ping command with retries. This wrapper allows mocking without waiting for the timeout. #> param( [string]$Destination, [string]$Source, [int]$Count = 1, [int]$TimeoutMs = 2000, [int]$RetryCount = 10, [int]$SleepSeconds = 1 ) # By default ping timeout is 2000, with a sleep of 1 second, so each ping iteration will take 3 seconds # With a default of 10 retries, the total time for ping will be 30 seconds [System.Boolean] $pingSuccess = $false $retry = 0 while (-not $pingSuccess -and ($retry -lt $RetryCount)) { $output = EnvValidatorNwkLibInvokePing -Destination $Destination -Source $Source -Count $Count -TimeoutMs $TimeoutMs try { $pingSuccess = $output | Select-String "Reply from $($Destination): bytes=" -Quiet } catch { $pingSuccess = $false } if ($pingSuccess) { break } else { $retry++ Start-Sleep -Seconds $SleepSeconds } } return $pingSuccess } function EnvValidatorNwkLibNormalizeIPv4Subnet { param ( [Parameter(Mandatory=$true)][string]$cidrSubnet ) # $cidrSubnet is IPv4 subnet in CIDR format, such as 192.168.10.0/24 $subnet, $prefixLength = $cidrSubnet.Split('/') $addr = $null if (([System.Net.IPAddress]::TryParse($subnet, [ref]$addr) -ne $true) -or ($addr.AddressFamily -ne [System.Net.Sockets.AddressFamily]::InterNetwork)) { throw "$subnet is not a valid IPv4 address." } if ([System.Int16] $prefixLength -lt 0 -or [System.Int16] $prefixLength -gt 32) { throw "$prefixLength is not a valid IPv4 subnet prefix-length." } $networkAddress = EnvValidatorNwkLibGetNetworkAddress $subnet $prefixLength return $networkAddress.ToString() + '/' + $prefixLength } function EnvValidatorNwkLibTryCreateNewPsSessionOnNode { param ( [System.String] $NodeName, [System.String] $NodeIP, [PSCredential] $ConnectionDomainAdminCredential = $null, [PSCredential] $ConnectionLocalAdminCredential = $null, [System.String] $DeployADLess = "false" ) Import-Module -Name Microsoft.WSMan.Management -Force -Verbose:$false -ErrorAction SilentlyContinue | Out-Null # LocalAdmin ECE store container is wiped post deployment and scaleout. This is AD'less Update path. Use implicit credential. if ($DeployADLess -eq "true" -and $null -eq $ConnectionLocalAdminCredential) { return EnvValidatorNwkLibNewPsSessionWithRetries -Node $NodeIP } # Test-WSMan with domain credentials and capture any errors e.g. access denied, connectivity etc.. # Testing WSMan to $NodeIP with domain credentials $($domainCredential.UserName)" try { Test-WSMan -ComputerName $NodeIP -Credential $ConnectionDomainAdminCredential -Authentication Default -ErrorAction Stop | Out-Null $credential = $ConnectionDomainAdminCredential } catch { # if local admin is null, the above should not error. # troubleshooting should be performed on secrets and PsSession creation. if ($null -eq $ConnectionLocalAdminCredential) { throw "Unable to create a valid session to $NodeIP with domain credentials. Ensure domain credential in ECE store is correct and can establish a PsSession to $NodeIP. Error: $($_.Exception.Message)" } else { $credential = $ConnectionLocalAdminCredential } } # Try to connect to IP first try { $PsSession = EnvValidatorNwkLibNewPsSessionWithRetries -Node $NodeIP -Credential $credential return $PsSession } catch { # As a last resort, try to connect with node name $PsSession = EnvValidatorNwkLibNewPsSessionWithRetries -Node $NodeName -Credential $credential return $PsSession } } function EnvValidatorNwkLibNewPsSessionWithRetries { [CmdletBinding()] Param ( [System.String] $Node, [PSCredential] $Credential = $null, [System.Int16] $Retries = 60, [System.Int16] $WaitSeconds = 10 ) for ($i=1; $i -le $Retries; $i++) { try { if ($Credential) { #Log-Info "Creating PsSession ($i/$retries) to $Node as $($Credential.UserName)..." $psSessionCreated = Microsoft.PowerShell.Core\New-PSSession -ComputerName $Node -Credential $Credential -ErrorAction Stop } else { #Log-Info "Creating PsSession ($i/$retries) to $Node with implicit credential..." $psSessionCreated = Microsoft.PowerShell.Core\New-PSSession -ComputerName $Node -ErrorAction Stop } $computerNameFromSession = Microsoft.PowerShell.Core\Invoke-Command -Session $psSessionCreated -ScriptBlock { $ENV:COMPUTERNAME } -ErrorAction Stop $isAdminSession = Microsoft.PowerShell.Core\Invoke-Command -Session $psSessionCreated -ScriptBlock { ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator') } -ErrorAction Stop if (-not $isAdminSession) { throw ("PsSession was successful but user: {0} is not an administrator on computer {1} " -f $psSessionCreated.Runspace.ConnectionInfo.Credential.Username, $computerName) } else { #Log-Info "PsSession to $Node is an administrator session." } break } catch { #Log-Info "Creating PsSession ($i/$Retries) to $Node failed: $($_.exception.message)" $errMsg = $_.ToString() Start-Sleep -Seconds $WaitSeconds } } if ($psSessionCreated -and $computerNameFromSession -and $isAdminSession) { #Log-Info ("PsSession to {0} created after {1} retries. (Remote machine name: {2})" -f $Node, ("$i/$retries"), $computerNameFromSession) return $psSessionCreated } else { throw "Unable to create a valid session to $Node`: $errMsg" } } #################################################### ####### Helper functions below, not exported ####### #################################################### function EnvValidatorNwkLibConvertIntToIPAddressString { param ( [Parameter(Mandatory=$true)] [UInt32] $Value ) $bytes = [BitConverter]::GetBytes($Value) [Array]::Reverse($bytes) # Construct new IPAddress object from byte array. # ', ' construct is used to wrap $bytes array into another array to prevent treating each byte as a separate argument. $ipAddress = New-Object System.Net.IPAddress -ArgumentList (, $bytes) return $ipAddress.IPAddressToString } function EnvValidatorNwkLibGetWinProxyConfiguration { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [ValidateSet("WinHttp", "WinInet")] [System.String] $ProxyType ) [System.Collections.Hashtable] $getParameter = @{} [System.String] $matchString = "" switch ($ProxyType) { "WinHttp" { $getParameter = @{ Default = $true } $matchString = "Proxy Server\(s\)\s*:\s*(.+)" } "WinInet" { $getParameter = @{ Advanced = $true } $matchString = "^Proxy\s*:\s*(.+)$" } Default { throw "Unsupported ProxyType: $ProxyType" } } Import-Module WinHttpProxy -Verbose:$false *>$null $proxySetting = Get-WinHttpProxy @getParameter $proxyHttp = "" $proxyHttps = "" foreach ($setting in $proxySetting) { if ($setting -match $matchString) { $rawProxies = $Matches[1].Trim() $proxyServers = $rawProxies -split ';' foreach ($server in $proxyServers){ if($server -like "*http=*") { $proxyHttp = $server -split "=", 2 | Select-Object -Last 1 $proxyHttp = $proxyHttp.Trim() } elseif($server -like "*https=*") { $proxyHttps = $server -split "=", 2 | Select-Object -Last 1 $proxyHttps = $proxyHttps.Trim() } else { # Single proxy applies to both $proxyHttp = $server.Trim() $proxyHttps = $proxyHttp } } } } # Construct the proxy settings object to return $proxySettings = @{ HttpProxy = $proxyHttp HttpsProxy = $proxyHttps ProxyIsEnabled = -not [string]::IsNullOrEmpty($proxyHttp) } return $proxySettings } function EnvValidatorNwkLibGetMaxParallelJobs { <# .SYNOPSIS Reads a MaxParallelJobs override from a well-known file, falling back to a caller-supplied default. .DESCRIPTION Looks for 'networkmaxparallel.txt' in the AzStackHci.EnvironmentChecker root folder ($PSScriptRoot\..). If the file exists and contains a valid positive integer, that value is returned. Otherwise the DefaultMaxParallelJobs value is returned. .PARAMETER DefaultMaxParallelJobs The fallback value to use when the override file is missing or contains invalid content. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [int] $DefaultMaxParallelJobs ) $overrideFileName = 'networkmaxparallel.txt' $overrideFilePath = Join-Path -Path (Join-Path -Path $PSScriptRoot -ChildPath '..') -ChildPath $overrideFileName if (Test-Path -Path $overrideFilePath -PathType Leaf) { try { $content = (Get-Content -Path $overrideFilePath -Raw).Trim() $parsedValue = 0 if ([int]::TryParse($content, [ref]$parsedValue) -and $parsedValue -gt 0) { Log-Info "Using MaxParallelJobs override from '$overrideFilePath': $parsedValue" return $parsedValue } else { Log-Info "Invalid content in '$overrideFilePath': '$content'. Using default: $DefaultMaxParallelJobs" -Type 'WARNING' } } catch { Log-Info "Failed to read '$overrideFilePath': $_. Using default: $DefaultMaxParallelJobs" -Type 'WARNING' } } return $DefaultMaxParallelJobs } Export-ModuleMember -Function EnvValidatorNwkLibCheckTcpConnectionWithRetries Export-ModuleMember -Function EnvValidatorNwkLibConfigureVMSwitchForTesting Export-ModuleMember -Function EnvValidatorNwkLibConvertIntToIPAddressString Export-ModuleMember -Function EnvValidatorNwkLibConvertIPAddressToInt Export-ModuleMember -Function EnvValidatorNwkLibConvertToPrefixLength Export-ModuleMember -Function EnvValidatorNwkLibConvertPrefixLengthToSubnetMask Export-ModuleMember -Function EnvValidatorNwkLibCreateAtcHostIntentsInfoFromSystem Export-ModuleMember -Function EnvValidatorNwkLibEnsureTestSessionOpen Export-ModuleMember -Function EnvValidatorNwkLibGenerateRandomIPInSubnet Export-ModuleMember -Function EnvValidatorNwkLibGetIpRange Export-ModuleMember -Function EnvValidatorNwkLibGetMaxParallelJobs Export-ModuleMember -Function EnvValidatorNwkLibGetMgmtIpRangeFromPools Export-ModuleMember -Function EnvValidatorNwkLibGetNetworkAddress Export-ModuleMember -Function EnvValidatorNwkLibGetSortedMgmtIntentAdapter Export-ModuleMember -Function EnvValidatorNwkLibGetWinProxyConfiguration Export-ModuleMember -Function EnvValidatorNwkLibInvokePing Export-ModuleMember -Function EnvValidatorNwkLibInvokePingWithRetries Export-ModuleMember -Function EnvValidatorNwkLibNewPsSessionWithRetries Export-ModuleMember -Function EnvValidatorNwkLibNormalizeIPv4Subnet Export-ModuleMember -Function EnvValidatorNwkLibTryCreateNewPsSessionOnNode # SIG # Begin signature block # MIInRgYJKoZIhvcNAQcCoIInNzCCJzMCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBuYYSKK7Os0lV8 # MZS5yCo6sWYr+cLVLxtgje+e0WdboKCCDLowggX1MIID3aADAgECAhMzAAACHU0Z # yE7XD1dIAAAAAAIdMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNVBAYTAlVTMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBD # b2RlIFNpZ25pbmcgUENBIDIwMjQwHhcNMjYwNDE2MTg1OTQzWhcNMjcwNDE1MTg1 # OTQzWjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYD # VQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IB # DwAwggEKAoIBAQDQvewXxx9gZZFC6Ys1WBay8BJ8kGA4JQnH5CMafqOASlTpK9H8 # o5ZXTXt0caVQTNMUPt445wXYD+dFtaKWTwDn1I52oUSrC9vJin1Gsqt+zyKJL5Dg # 3eQXbQNR61DmMy20GLTIO3SFed9Rfi/ophgCLGFLDR3r0KvHjwMb/jYWS0celV/4 # Lz27LfAekm8v9E5IXaeiXbAUYZKK090n4CVl3JBtbN+9DtI9SNu/yjvozW52/u7R # X/Ttpa/KDlpuokZ+Zcbvmtd9ur9gFLvZzh41o9MsE/clQtdaFWGvuo6Jua/ntpgk # ey3E5/vBFe+MJPG6phdnuo6r57ZudCudiI1bAgMBAAGjggGbMIIBlzAOBgNVHQ8B # Af8EBAMCB4AwHwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYBBQUHAwMwHQYDVR0O # BBYEFH6QuMwqcPG0hQlQ6c5jCtTTLrVeMEUGA1UdEQQ+MDykOjA4MR4wHAYDVQQL # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xFjAUBgNVBAUTDTIzMDAxMis1MDc1NTkw # HwYDVR0jBBgwFoAUf1k/VCHarU/vBeXmo9ctBpQSCDEwYAYDVR0fBFkwVzBVoFOg # UYZPaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0 # JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNybDBtBggrBgEFBQcBAQRh # MF8wXQYIKwYBBQUHMAKGUWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv # Y2VydHMvTWljcm9zb2Z0JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNy # dDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQBKTbYOjzwTG/DXGaz9 # s6+fQeaTtDcFmMY+5UyVFCyj7Pv+5i37qfX8lSL/tBIfYQfWsMuBQlfZurJD6r4H # VJ2CeH+1fgiq8dcHdVKoZ3Sa2qXoX3cq9iS8cVb06B7+5/XJ7I0OxHH9fDsvJ3T3 # w5V/ZtAIFmLrl+P0CtG+92uzRsn0nTbdFjOkLMLWPLAU3THohKRlSEMgFJpPkm5n # 5UAZ35xX6FWCrDLsSKb555bTifwa8mJBwdlof0bmfYidH+dxZ1FdDxvLnNl9zeKs # A4kejaaIqqIPguhwAti5Ql7BlTNoJNwxCvBmqW2MQLnCkYN/VVUsR3V2x/rcTNzo # Bf/Z/SpROvdaA2ZOOd1uioXJt3tdLQ7vHpqpib0KfWr/FWXW10q38VxfCnRQBqzb # SuztR7nEMuzX7Ck+B/XaPDXd1qh72+QYyB0Z2VzWmO9zsnb9Uq/dwu8LGeQqnyu6 # 7SDGACvnXii2fb9+US492VTnXSnFKyqwgzUyFMtZK1/sHYTv6bG4TtQUygQxTN+Z # V+aJIlKO2MqZ7bKrAnOzS9m6NgoTdWOq11bTOZwKlIEV/EhV9SWkDmdpR/hPPT2v # 6TEj4F8PT/zHjRezIU5c/DGlt/VhY/pK0XkJtEyMmmS1BMtjU/rqBZVMIm3dnxQs # /TBByr+Cf8Z1r7aifQVQ+WSqzjCCBr0wggSloAMCAQICEzMAAAA5O7Y3Gb8GHWcA # AAAAADkwDQYJKoZIhvcNAQEMBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX # YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg # Q29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRl # IEF1dGhvcml0eSAyMDExMB4XDTI0MDgwODIwNTQxOFoXDTM2MDMyMjIyMTMwNFow # VzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEo # MCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAyNDCCAiIwDQYJ # KoZIhvcNAQEBBQADggIPADCCAgoCggIBANgBnB7jOMeqlRYHNa265v4IY9fH8TKh # emHfPINe1gpLaV3dhg324WwH06LcHbpnsBukCDNitryo0dtS/EW6I/yEL/bLSY8h # KpbfQuWusBPr9qazYcDxCW/qnjb5JsI1s8bNOg3bVATvQVL4tcf03aTycsz8QeCd # M0l/yHRObJ9QqazM1r6VPEOJ7LL+uEEb73w6QCuhs89a1uv1zerOYMnsneRRwCbp # yW11IcggU0cRKDDq1pjVJzIbIF6+oiXXbReOsgeI8zu1FyQfK0fVkaya8SmVHQ/t # Of23mZ4W9k0Ri22QW9p3UgSC5OUDktKxxcCmGL6tXLfOGSWHIIV4YrTJTT6PNty5 # REojHJuZHArkF9VnHTERWoTjAzfI3kP+5b4alUdhgAZ7ttOu1bVnXfHaqPYl2rPs # 20ji03LOVWsh/radgE17es5hL+t6lV0eVHrVhsssROWJuz2MXMCt7iw7lFPG9LXK # Gjsmonn2gotGdHIuEg5JnJMJVmixd5LRlkmgYRZKzhxSCwyoGIq0PhaA7Y+VPct5 # pCHkijcIIDm0nlkK+0KyepolcqGm0T/GYQRMhHJlGOOmVQop36wUVUYklUy++vDW # eEgEo4s7hxN6mIbf2MSIQ/iIfMZgJxC69oukMUXCrOC3SkE/xIkgpfl22MM1itkZ # 35nNXkMolU1lAgMBAAGjggFOMIIBSjAOBgNVHQ8BAf8EBAMCAYYwEAYJKwYBBAGC # NxUBBAMCAQAwHQYDVR0OBBYEFH9ZP1Qh2q1P7wXl5qPXLQaUEggxMBkGCSsGAQQB # gjcUAgQMHgoAUwB1AGIAQwBBMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU # ci06AjGQQ7kUBU7h6qfHMdEjiTQwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDovL2Ny # bC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0MjAx # MV8yMDExXzAzXzIyLmNybDBeBggrBgEFBQcBAQRSMFAwTgYIKwYBBQUHMAKGQmh0 # dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0MjAx # MV8yMDExXzAzXzIyLmNydDANBgkqhkiG9w0BAQwFAAOCAgEAFJQfOChP7onn6fLI # MKrSlN1WYKwDFgAddymOUO3FrM8d7B/W/iQ6DxXsDn7D5W4wMwYeLystcEqfkjz4 # NURRgazyMu5yRzQh4LqjA4tStTcJh1opExo7nn5PuPBYnbu0+THSuVHTe0VTTPVh # ily/piFrDo3axQ9P4C+Ol5yet+2gTfekICS5xS+cYfSIvgn0JksVBVMYVI5QFu/q # hnLhsEFEUzG8fvv0hjgkO+lkpV9ty6GkN4vdnd7ya6Q6aR9y34aiM1qmxaxBi6OU # nyNl6fkuun/diTFnYDLTppOkr/mg5WSfCiDVMNCxtj4wPKC5OmHm1DQIt/MNokbb # H3UGsFP1QbzsLocuSqLCvH09Io3fDPTmscR9Y75G4qX7RTX8AdBPo0I6OEojf39z # uFZt0qOHm65YWQE69cZM2ueE1MB05dNNgHK9gTE7zKvK/fg8B2qjW88MT/WF5V5u # vZGtqa9FSL2RazArA+rDPuf6JGYz4HpgMZHB4S6szWSKYBv0VisCzfxgeU+dquXW # 9bd0auYlOB58DPcOYKdc3Se94g+xL4pcEhbB54JOgAkwYTu/9dLeH2pDqeJZAABV # DWRQCaXfO5LgyKwKCLYXpigrZYCjUSBcr+Ve8PFWMhVTQl0v4q8J/AUmQN5W4n10 # 1cY2L4A7GTQG1h32HHAvfQESWP0xghniMIIZ3gIBATBuMFcxCzAJBgNVBAYTAlVT # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jv # c29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMjQCEzMAAAIdTRnITtcPV0gAAAAAAh0w # DQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYK # KwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIF1dLNG7 # tI+rxya4HDqgx07G5CRR0mfJlWXUK5i11GScMEIGCisGAQQBgjcCAQwxNDAyoBSA # EgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20w # DQYJKoZIhvcNAQEBBQAEggEAmgSfFLT+eVWUK+lbHQYvwpdQsn91SnqMc52CicpT # 2aZPgyroZhzkO76cdFEgQ52sRQeWpdljyetaAmc2wm+LgthCFIAZCnwRDP3TMpFK # uf7E6BvmhTFJaYdMcm5cF5Wkb+i6+I/79l7Q3DNm4VtCV0SX8WLKO9ivgvCis1HY # qtMga8XITYNTEroG0Kpf1Q+FnptOndIWcMdvJ8bk+UwDEWKVfhCBn6/aAy5aBpFk # Rd+1fkHioesbP/3UbFJW4GRiQ56JGGUV6sDTwR8qXJhWFncUUhD//JWimX9haicG # UvZtBglR+uQDyIRaa3zBe396EEppii662t/wVptlXJcNmqGCF5QwgheQBgorBgEE # AYI3AwMBMYIXgDCCF3wGCSqGSIb3DQEHAqCCF20wghdpAgEDMQ8wDQYJYIZIAWUD # BAIBBQAwggFSBgsqhkiG9w0BCRABBKCCAUEEggE9MIIBOQIBAQYKKwYBBAGEWQoD # ATAxMA0GCWCGSAFlAwQCAQUABCDD5xtMo81Ude2SIaEq/+L/Df91ckhlUU3OMgho # rYBy8AIGaeeNG/HsGBMyMDI2MDUwMzE0MzEzMC4yOTlaMASAAgH0oIHRpIHOMIHL # MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk # bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxN # aWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRT # UyBFU046REMwMC0wNUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0 # YW1wIFNlcnZpY2WgghHqMIIHIDCCBQigAwIBAgITMwAAAiQ7hCGwLKxkIgABAAAC # JDANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAe # Fw0yNjAyMTkxOTM5NTlaFw0yNzA1MTcxOTM5NTlaMIHLMQswCQYDVQQGEwJVUzET # MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV # TWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmlj # YSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046REMwMC0wNUUw # LUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIi # MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCj6W3UaQ2Zr4hNvSy7j7UMPFVy # s7aExGB+JFwykzzXg3jayYm9gOLXJ7tNhU2emhrLQCOZcgLvz6FkqmghzQxzmkgK # tLYiKaEzhogO/ce0lThdLNdVtMwQOYgo+XtXAZcViBX4LcHk38RusZiF7wxSa5t/ # Lxic04+Z/hly1gJQpIeFDqp4a9PuLt8rsfH05vW9pU9uriGdDxfJXn/lc49CxbXq # A3EX17L24bc6t+mFuPDAJKKpai3XXqF2nJlpTPfdrA29sWTSNKig9CtBC5tzQj0f # lbsa/4wqO9u+RkuwpZb3b7qnW5FdFrDR1vQmXfjlyUP9ZO38839NwSuiHtvsFCNk # TNIX8OL5XVq1nsKyu//GeIZ9YuxsfLBedqG024PDERyrAs0pvfUWOLapVQajHPoC # nuNSKvbEh7s5IQ0YgupGji+H7rIDx2/mIEI+6Q8WwBtk3Yxyhjj0GXw909i0EkTk # Vyy+1yADjwSC8bw2qM4+Mc4hyytlZzSc0IPUBq1YGnYwCjIwa5/lMW0pFn/HpJdB # 6XeMuTtYTOpaPoo64FjQryLXWjd4ovpw5lOw7X+v3E9kwN9VBC+wJESBECC1gZMC # S5TaVwfE1w4pnXXb1qT9bjgRsPg4dklruUTdon/3SNt0a0Q5Nc2Ul+rMlQxXoP9i # sXwMNnKO5JJkqRDRVQIDAQABo4IBSTCCAUUwHQYDVR0OBBYEFHMfkX1u/zJLCMe0 # gqYitx1tAHeoMB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1Ud # HwRYMFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3Js # L01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggr # BgEFBQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNv # bS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIw # MTAoMSkuY3J0MAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgw # DgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUAA4ICAQA+wHSbmhIpM8CRVZ4t # k624hQ+LdZXE4qoeQui77CeNa3jq1FOzi7MRKkko6diEDHXPNWvAagxastCewPzm # 5TCNh1s4qCHh4R2G/r48wU/Mpc68/WDmJy5CIQn/Fwps1sbNUEu7Bzg004qULIVJ # 963jo/am4xwKgwh+vSVL7/dhsfT7dvhpRddbYLQTHZgwuNB6QhcEEsgogLVwNRj3 # 7VEWZDiwoMdxyC7YYrQu6MCVtizHnOtkSX7FqIoi6jlcfqfo619uDH9r8k2qAOHC # eEAqKXKymIXDMcGGlEdDFbYiDZgPCBM0IHgAeilUSon07wjHu0e0ssBmtBafPb4G # d+5FuRnWG3XGe91NCpLKqmFa/4GkVz9OMzZUg8oczxC/4JT3Hf45JEtszToXwNsk # V3JNCcu2IItr6SJHmi3EDVADDRSNhdzFRpYmplGElPl5GRoPtJiDEvRIbv5MFKIw # 2x9gnehf5IvBjC4ZkBg+4GTpqGE3mmnzF3nIekOkX4ug0/0mN2CSarhuSi9NmHIO # pUN2eQHUtgTb/+Gmq7gktCMwIq/JOCYIiTYqpv1objAGKdWMPCrlSyNAs0jZYzkh # a535158NMx+wBGvsfFoVsCMG5Ocp6vW6CXyuWRbUVqMU1OrQbHfdyzJpbhJC1PbA # ZIyJCbN+VBgDTAzTKY8w4ISSwTCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkA # AAAAABUwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX # YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg # Q29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRl # IEF1dGhvcml0eSAyMDEwMB4XDTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVow # fDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl # ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMd # TWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUA # A4ICDwAwggIKAoICAQDk4aZM57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX # 9gF/bErg4r25PhdgM/9cT8dm95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1q # UoNEt6aORmsHFPPFdvWGUNzBRMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8d # q6z2Nr41JmTamDu6GnszrYBbfowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byN # pOORj7I5LFGc6XBpDco2LXCOMcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2k # rnopN6zL64NF50ZuyjLVwIYwXE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4d # Pf0gz3N9QZpGdc3EXzTdEonW/aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgS # Uei/BQOj0XOmTTd0lBw0gg/wEPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8 # QmguEOqEUUbi0b1qGFphAXPKZ6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6Cm # gyFdXzB0kZSU2LlQ+QuJYfM2BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzF # ER1y7435UsSFF5PAPBXbGjfHCBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQID # AQABo4IB3TCCAdkwEgYJKwYBBAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQU # KqdS/mTEmr6CkTxGNSnPEP8vBO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1 # GelyMFwGA1UdIARVMFMwUQYMKwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0 # dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0 # bTATBgNVHSUEDDAKBggrBgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMA # QTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbL # j+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1p # Y3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0w # Ni0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIz # LmNydDANBgkqhkiG9w0BAQsFAAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwU # tj5OR2R4sQaTlz0xM7U518JxNj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN # 3Zi6th542DYunKmCVgADsAW+iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU # 5HhTdSRXud2f8449xvNo32X2pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5 # KYnDvBewVIVCs/wMnosZiefwC2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGy # qVvfSaN0DLzskYDSPeZKPmY7T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB6 # 2FD+CljdQDzHVG2dY3RILLFORy3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltE # AY5aGZFrDZ+kKNxnGSgkujhLmm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFp # AUR+fKFhbHP+CrvsQWY9af3LwUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcd # FYmNcP7ntdAoGokLjzbaukz5m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRb # atGePu1+oDEzfbzL6Xu/OHBE0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQd # VTNYs6FwZvKhggNNMIICNQIBATCB+aGB0aSBzjCByzELMAkGA1UEBhMCVVMxEzAR # BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2Eg # T3BlcmF0aW9uczEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOkRDMDAtMDVFMC1E # OTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEw # BwYFKw4DAhoDFQCmCPHbmseASfe//bGtX9eQG+0+46CBgzCBgKR+MHwxCzAJBgNV # BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w # HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29m # dCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA7aEy/DAiGA8y # MDI2MDUwMzAyMzU0MFoYDzIwMjYwNTA0MDIzNTQwWjB0MDoGCisGAQQBhFkKBAEx # LDAqMAoCBQDtoTL8AgEAMAcCAQACAg/aMAcCAQACAhIOMAoCBQDtooR8AgEAMDYG # CisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEA # AgMBhqAwDQYJKoZIhvcNAQELBQADggEBAL9QPClikC5HBBjkEV8kQCdOYgladCEK # OVxs6xbIHbuWlp0Zx98nC6OKdOCDGy6v72cKF73oTc+MJIyg+V9MC5amxJhbOexC # 6UhDv2K0e2UO29hd6bLMwJNSOG2cikQtrCx2HwUFMfA0cL5/v5JkCrGVOMC9+dqO # l3UGJV2kDyUveA8XZTcOfE0uxhBi9WqI3+2o35c0pirxSDZ+e4a0Y1kfCvmV8SKX # 03/4OobFPRKBe3Zen5G3X4q6a0kwFdb20958JGDQIOztpc1e9ozHpvw6EaLGJQf1 # XBgPBcpQHUAPzG2cxa2bFdeczcOxTadb9fhz54UjiqP4PlAqI2nMi0gxggQNMIIE # CQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYw # JAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAiQ7hCGw # LKxkIgABAAACJDANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqG # SIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCDUMKbyEV7nzcnOt0Aex8VCkZnGs3Dt # z09ZPKf/lg5lqDCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIEghPTdqm/dR # yZ0BczXcdloVEqICdcmpVNbH9CEVzWSOMIGYMIGApH4wfDELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt # U3RhbXAgUENBIDIwMTACEzMAAAIkO4QhsCysZCIAAQAAAiQwIgQgA//oxm2fo5LO # mmtYNHLZwfdHBrA0Jfu1dxOXSFqdBYowDQYJKoZIhvcNAQELBQAEggIATxgqnAZ/ # vcDJtizaA9geg06xKIkdx3/1/Z7WXpUHAfIkMp+J+wIBru4fkLXlrx6R2UjaiDHt # XZaeeKdHWL8BrTZcf2V3yJyPHXGhxTl/Wk+2k9nm7/fzKIg+YMsQVaHnmRokLQ5z # AiCyHPj37qETJh/FaH7D/W40zIlDf+LqojlLb5r4TfXuxdRjdIe493TdkLTu2752 # rlYGYhRwOpdjTyfgz8uoA6PF6BAXDg8NvRi+/vQQAaU/jAVknP9kx+YcPTLItlft # npWxm3fe1FkZ1yt8wqeSzrpUDxV0Bf+6iOjk/AYsJ/bTQGX1x53xC4lPpTfQwA+H # mcbfvzq2Ko6sfPXn2nYvr6ATRx9I2p4mOppwUP5xX0xdc0m7kmkQfB6s3r306Gud # h9074eyLG+kWAUVlRBOyDrIXReVD0fw0L6aKOsZMPIUJ/YWqnijr/HZyzGS6DFQF # UsC+ejH32z4LbfMi0VyEyHDFd6uJmO27db7yz7sjMwVa5ug5BbPi6v3KRU8R0Ij1 # 4b+APlkiZoE52+8Gq3ngzvRV4oaKP/97Ew+Jk9GcNwVzPvbx7akpiKlR6iBGs1Pz # RYheL9BQ9d2QXdnP+pVtJY58Hb1QGQj/zcplw8dnTntlDJ81ntt4i65cj+kUzR2H # FdqyjG5r6sHB6AjLcGGzSqdQEbgI32AAtuY= # SIG # End signature block |