AvZone-Latency-Test/AvZone-Latency-Test.ps1 (381 lines of code) (raw):
<#
.SYNOPSIS
Creates VMs and tests latency between VMs
.DESCRIPTION
The script creates VMs in Zone 1, 2 and 3, installing qperf on it and testing latency between VMs
.PARAMETER Region
The Azure region name
.EXAMPLE
./AvZone-Latency-Test.ps1 -Region westeurope
Example output:
Region: westeurope
VM Type: Standard_E8s_v3
Latency:
----------------------------------------------
| zone 1 | zone 2 | zone 3 |
-------------------------------------------------------
| zone 1 | | xx us | xx us |
| zone 2 | xx us | | xx us |
| zone 3 | xx us | xx us | |
-------------------------------------------------------
Bandwidth:
----------------------------------------------
| zone 1 | zone 2 | zone 3 |
-------------------------------------------------------
| zone 1 | | xxx MB/sec | xxx MB/sec |
| zone 2 | xxx MB/sec | | xxx MB/sec |
| zone 3 | xxx MB/sec | xxx MB/sec | |
-------------------------------------------------------
.LINK
https://github.com/Azure/SAP-on-Azure-Scripts-and-Utilities
.NOTES
v0.1 - Initial version
v0.2 - adding usage of existing VNET
v0.3 - switching from variables to parameters
- adding documentation
- adding logon check
2022041101 - using IP address instead of hostname to avoid issues in existing VNETs
- creating storage account for diagnostic information
2023030601 - changing how existing sessions are deleted
- switching to CentOS 8.5
- adding TCP port check
- fixing issue with "breaking change" warnings
- changing how the physical hostname is retrieved from .kvp_pool_3 file
#>
<#
Copyright (c) Microsoft Corporation.
Licensed under the MIT license.
#>
#Requires -Version 7.1
#Requires -Modules Az.Compute
#Requires -Modules @{ ModuleName="Posh-SSH"; ModuleVersion="3.0.0" }
param(
#Azure Subscription Name
[Parameter(Mandatory=$true)][string]$SubscriptionName,
#Azure Region, use Get-AzLocation to get region names
[string]$region = "westeurope",
#Resource Group Name that will be created
[string]$ResourceGroupName = "AvZoneLatencyTest",
#Delete the test environment after test
[boolean]$DestroyAfterTest = $true,
#Use an existing VNET, direct SSH connection to VMs required
[boolean]$UseExistingVnet = $false,
#use existing VMs of a previous test
[boolean]$UseExistingVMs = $false,
#use public IP addresses to connect
[boolean]$UsePublicIPAddresses = $true,
# VM type, recommended Standard_D8s_v3
[string]$VMSize = "Standard_D8s_v3",
#OS provider, for CentOS it is OpenLogic
[string]$OSPublisher = "OpenLogic",
#OS Type
[string]$OSOffer = "CentOS",
#OS Verion
[string]$OSSku = "8_5",
#Latest OS image
[string]$OSVersion = "latest",
#OS username
[string]$VMLocalAdminUser = "azping",
#OS password
[string]$VMLocalAdminPassword = "P@ssw0rd!",
#VM name prefix, 1,2,3 will be added based on zone
[string]$VMPrefix = "azping-vm0",
#VM nic name
[string]$NICPostfix = "-nic1",
#Public IP address postfix
[string]$pippostfix = "-pip",
#Azure Network Security Group (NSG) name
[string]$NSGName = "azping-nsg",
#Azure VNET name, if using existing VNET
[string]$NetworkName = "azping-mgmt-vnet",
#Azure Subnet name, if using exising
[string]$SubnetName = "default",
#Resource Group Name of existing VNET
[string]$ResourceGroupNameNetwork = "azping-mgmt",
#Azure IP Subnet prefix if using public IP to VNET creation
[string]$SubnetAddressPrefix = "10.1.1.0/24",
#Azure IP VNET prefix if using public IP to VNET creation
[string]$VnetAddressPrefix = "10.1.1.0/24",
#decide to use qperf or niping
[ValidateSet("qperf","niping")][string]$testtool = "qperf",
#path to niping
[string]$nipingpath
)
Function Get-RandomAlphanumericString {
[CmdletBinding()]
Param (
[int] $length = 8
)
Begin{
}
Process{
Write-Output ( -join (( 0x61..0x7A) | Get-Random -Count $length | % {[char]$_}) )
}
}
$breakingchangewarning = Get-AzConfig -DisplayBreakingChangeWarning
if ($breakingchangewarning.Value -eq $true) {
Update-AzConfig -DisplayBreakingChangeWarning $false
}
if ($testtool -eq "niping") {
if (!$nipingpath) {
$nipingpath = Read-Host -Prompt "Please enter download path for niping executable: "
}
}
# select subscription
$Subscription = Get-AzSubscription -SubscriptionName $SubscriptionName
if (-Not $Subscription) {
Write-Host -ForegroundColor Red -BackgroundColor White "Sorry, it seems you are not connected to Azure or don't have access to the subscription. Please use Connect-AzAccount to connect."
exit
}
Select-AzSubscription -Subscription $SubscriptionName -Force
$VMLocalAdminSecurePassword = ConvertTo-SecureString $VMLocalAdminPassword -AsPlainText -Force
$zones = 3
#create the secure credential object
$Credential = New-Object System.Management.Automation.PSCredential ($VMLocalAdminUser, $VMLocalAdminSecurePassword);
# initialize the arrays for outputs
$latency = @(("","",""),("","",""),("","",""))
$bandwidth = @(("","",""),("","",""),("","",""))
for ($x=1; $x -le $zones; $x++) {
for ($y=1; $y -le 3; $y++) {
$latency[$x-1][$y-1] = "0"
$bandwidth[$x-1][$y-1] = "0"
}
}
if ($UseExistingVMs) {
Write-Host "Using existing VMs" -ForegroundColor Green
}
else {
# create resource group
Write-Host -ForegroundColor Green "Creating resource group"
$ResourceGroup = New-AzResourceGroup -Location $region -Name $ResourceGroupName
# creating storage account for diagnostic information
$StorageAccountName = Get-RandomAlphanumericString
$StorageAccount = New-AzStorageAccount -ResourceGroupName $ResourceGroupName -Name $StorageAccountName -SkuName Standard_LRS -Location $region
# create vNET and Subnet or getting existing
if ($UseExistingVnet) {
Write-Host -ForegroundColor Green "Getting existing vNET and Subnet Config"
$Vnet = Get-AzVirtualNetwork -Name $NetworkName -ResourceGroupName $ResourceGroupNameNetwork
$SingleSubnet = Get-AzVirtualNetworkSubnetConfig -VirtualNetwork $Vnet -Name $SubnetName
}
else {
Write-Host -ForegroundColor Green "Creating vNET and Subnet"
$SingleSubnet = New-AzVirtualNetworkSubnetConfig -Name $SubnetName -AddressPrefix $SubnetAddressPrefix
$Vnet = New-AzVirtualNetwork -Name $NetworkName -ResourceGroupName $ResourceGroupName -Location $region -AddressPrefix $VnetAddressPrefix -Subnet $SingleSubnet
}
# create NSG
Write-Host -ForegroundColor Green "Creating NSG"
$rule1 = New-AzNetworkSecurityRuleConfig -Name ssh-rule -Description "Allow SSH" -Access Allow -Direction Inbound -Protocol Tcp -Priority 100 -SourcePortRange * -SourceAddressPrefix * -DestinationAddressPrefix * -DestinationPortRange 22
$nsg = New-AzNetworkSecurityGroup -ResourceGroupName $ResourceGroupName -Location $region -Name $NSGName -SecurityRules $rule1
# create VMs
Write-Host -ForegroundColor Green "Creating VMs"
For ($zone=1; $zone -le $zones; $zone++) {
$ComputerName = $VMPrefix + $zone
$NICName = $ComputerName + $NICPostfix
$PIPName = $NICName + $pippostfix
$Subnet = Get-AzVirtualNetworkSubnetConfig -Name $SubnetName -VirtualNetwork $Vnet
if ($UsePublicIPAddresses) {
$PIP = New-AzPublicIpAddress -Name $PIPName -ResourceGroupName $ResourceGroupName -Location $region -Sku Standard -AllocationMethod Static -IpAddressVersion IPv4 -Zone $zone
$IPConfig1 = New-AzNetworkInterfaceIpConfig -Name "IPConfig-1" -Subnet $Subnet -PublicIpAddress $PIP -Primary
}
else {
$IPConfig1 = New-AzNetworkInterfaceIpConfig -Name "IPConfig-1" -Subnet $Subnet -Primary
}
$NIC = New-AzNetworkInterface -Name $NicName -ResourceGroupName $ResourceGroupName -Location $region -IpConfiguration $IpConfig1 -EnableAcceleratedNetworking -NetworkSecurityGroup $nsg
$VirtualMachine = New-AzVMConfig -VMName $ComputerName -VMSize $VMSize
$VirtualMachine = Set-AzVMOperatingSystem -VM $VirtualMachine -Linux -ComputerName $ComputerName -Credential $Credential
$VirtualMachine = Add-AzVMNetworkInterface -VM $VirtualMachine -Id $NIC.Id
$VirtualMachine = Set-AzVMSourceImage -VM $VirtualMachine -PublisherName $OSPublisher -Offer $OSOffer -Skus $OSSku -Version $OSVersion
$VirtualMachine = Set-AzVMBootDiagnostic -VM $VirtualMachine -StorageAccountName $StorageAccountName -Enable -ResourceGroupName $ResourceGroupName
$vm = New-AzVM -ResourceGroupName $ResourceGroupName -Location $region -VM $VirtualMachine -zone $zone -Verbose -AsJob
}
# waiting for VM creation jobs to finish
"All jobs created, waiting ..."
Get-Job | Wait-Job
"All jobs completed"
Get-AzVM -ResourceGroupName $ResourceGroupName
# adding some time as it sometimes helps :-)
"Waiting for four minute for all systems to come up ..."
Start-Sleep -Seconds 240
}
# removing all open ssh sessions
Get-SSHTrustedHost | Remove-SSHTrustedHost
$sshsessions = Get-SSHSession
foreach ($sshsession in $sshsessions) {
Remove-SSHSession -SessionId $sshsession.SessionId
}
# creating SSH sessions to VMs
$ipaddresses = @{}
Write-Host -ForegroundColor Green "Creating SSH sessions"
For ($zone=1; $zone -le $zones; $zone++) {
$ComputerName = $VMPrefix + $zone
$pipname = $VMPrefix + $zone + $NICPostfix + $pippostfix
$NICName = $ComputerName + $NICPostfix
if ($UsePublicIPAddresses) {
$nic = Get-AzNetworkInterface -Name $NICName
$networkinterfaceconfig = Get-AzNetworkInterfaceIpConfig -NetworkInterface $nic
$ipaddresses[$ComputerName] += $networkinterfaceconfig.PrivateIpAddress
$pipname = $VMPrefix + $zone + $NICPostfix + $pippostfix
$PIP = Get-AzPublicIpAddress -Name $pipname
$ipaddress = $PIP.IpAddress
}
else {
$nic = Get-AzNetworkInterface -Name $NICName
$networkinterfaceconfig = Get-AzNetworkInterfaceIpConfig -NetworkInterface $nic
$ipaddress = $networkinterfaceconfig.PrivateIpAddress
$ipaddresses[$ComputerName] += $networkinterfaceconfig.PrivateIpAddress
}
try {
# checking TCP connectivity
$_testresult = New-Object System.Net.Sockets.TcpClient($ipaddress, 22)
if ($_testresult.Connected) {
# connected
Write-Host -ForegroundColor Green "TCP connection available to VM $ComputerName with IP address $ipaddress"
$sshsession = New-SSHSession -ComputerName $ipaddress -Credential $Credential -AcceptKey -Force
if ($sshsession.connected -ne $true)
{
Write-Host "Unable to connect to IP address $ipaddress"
exit
}
}
else {
Write-Host -ForegroundColor Red "unable to connect to SSH port for VM $ipaddress. Please check if you can connect to the VM from your host using e.g. putty"
}
}
catch {
exit
}
}
$sshsessions = Get-SSHSession
Write-Host -ForegroundColor Green "Getting Hosts for virtual machines"
For ($zone=1; $zone -le $zones; $zone++) {
#$output = Invoke-SSHCommand -Command "/bin/cat /var/lib/hyperv/.kvp_pool_3 | tr -d '\\000' | grep -o -P '(?<=HostName).*(?=CLOUD_INIT)'" -SessionId $sshsessions[$zone-1].SessionId
$output = Invoke-SSHCommand -Command "strings /var/lib/hyperv/.kvp_pool_3 | sed -n '2 p'" -SessionId $sshsessions[$zone-1].SessionId
Write-Host ("VM$zone : " + $output.Output)
}
# run qperf test
if ($testtool -eq "qperf") {
# install qperf on all VMs
Write-Host -ForegroundColor Green "Installing qperf on all VMs"
For ($zone=1; $zone -le $zones; $zone++) {
$output = Invoke-SSHCommand -Command "echo $VMLocalAdminPassword | sudo -S yum -y install qperf" -SessionId $sshsessions[$zone-1].SessionId
$output = Invoke-SSHCommand -Command "nohup qperf &" -SessionId $sshsessions[$zone-1].SessionId -TimeOut 3 -ErrorAction silentlycontinue
}
# run performance tests
Write-Host -ForegroundColor Green "Running bandwidth and latency tests"
For ($zone=1; $zone -le $zones; $zone++) {
$vmtopingno1 = (( $zone %3)+1)
$vmtoping1 = $VMPrefix + (( $zone %3)+1)
$ipaddresstoping1 = $ipaddresses[$vmtoping1]
$vmtopingno2 = ((($zone+1)%3)+1)
$vmtoping2 = $VMPrefix + ((($zone+1)%3)+1)
$ipaddresstoping2 = $ipaddresses[$vmtoping2]
$output = Invoke-SSHCommand -Command "qperf $ipaddresstoping1 tcp_lat" -SessionId $sshsessions[$zone-1].SessionId
$latencytemp = [string]$output.Output[1]
$latencytemp = $latencytemp.substring($latencytemp.IndexOf("=")+3)
$latencytemp = $latencytemp.PadLeft(12)
$latency[$zone -1][$vmtopingno1 -1] = $latencytemp
$output = Invoke-SSHCommand -Command "qperf $ipaddresstoping1 tcp_bw" -SessionId $sshsessions[$zone-1].SessionId
$bandwidthtemp = [string]$output.Output[1]
$bandwidthtemp = $bandwidthtemp.substring($bandwidthtemp.IndexOf("=")+3)
$bandwidthtemp = $bandwidthtemp.PadLeft(12)
$bandwidth[$zone -1][$vmtopingno1 -1] = $bandwidthtemp
$output = Invoke-SSHCommand -Command "qperf $ipaddresstoping2 tcp_lat" -SessionId $sshsessions[$zone-1].SessionId
$latencytemp = [string]$output.Output[1]
$latencytemp = $latencytemp.substring($latencytemp.IndexOf("=")+3)
$latencytemp = $latencytemp.PadLeft(12)
$latency[$zone -1][$vmtopingno2 -1] = $latencytemp
$output = Invoke-SSHCommand -Command "qperf $ipaddresstoping2 tcp_bw" -SessionId $sshsessions[$zone-1].SessionId
$bandwidthtemp = [string]$output.Output[1]
$bandwidthtemp = $bandwidthtemp.substring($bandwidthtemp.IndexOf("=")+3)
$bandwidthtemp = $bandwidthtemp.PadLeft(12)
$bandwidth[$zone -1][$vmtopingno2 -1] = $bandwidthtemp
}
}
if ($testtool -eq "niping") {
# download niping on all hosts and run niping server
Write-Host -ForegroundColor Green "Installing niping on all VMs"
For ($zone=1; $zone -le $zones; $zone++) {
$output = Invoke-SSHCommand -Command "echo $VMLocalAdminPassword | wget $nipingpath -O /tmp/niping" -SessionId $sshsessions[$zone-1].SessionId
$output = Invoke-SSHCommand -Command "echo $VMLocalAdminPassword | chmod +x /tmp/niping" -SessionId $sshsessions[$zone-1].SessionId
$output = Invoke-SSHCommand -Command "echo $VMLocalAdminPassword | nohup /tmp/niping -s -I 0 &" -SessionId $sshsessions[$zone-1].SessionId -TimeOut 3 -ErrorAction silentlycontinue
}
# run performance tests
Write-Host -ForegroundColor Green "Running bandwidth and latency tests"
For ($zone=1; $zone -le $zones; $zone++) {
$vmtopingno1 = (( $zone %3)+1)
$vmtoping1 = $VMPrefix + (( $zone %3)+1)
$ipaddresstoping1 = $ipaddresses[$vmtoping1]
$vmtopingno2 = ((($zone+1)%3)+1)
$vmtoping2 = $VMPrefix + ((($zone+1)%3)+1)
$ipaddresstoping2 = $ipaddresses[$vmtoping2]
$output = Invoke-SSHCommand -Command "/tmp/niping -c -B 10 -L 100 -H $ipaddresstoping1 | grep av2" -SessionId $sshsessions[$zone-1].SessionId
$latencytemp = [string]$output.Output
$latencytemp = $latencytemp -replace '\s+', ' '
$latencytemp = $latencytemp -Split " "
$latencytemp = [string]$latencytemp[1] + " " + $latencytemp[2]
$latencytemp = $latencytemp.PadLeft(12)
$latency[$zone -1][$vmtopingno1 -1] = $latencytemp
$output = Invoke-SSHCommand -Command "/tmp/niping -c -B 100000 -L 100 -H $ipaddresstoping1 | grep tr2" -SessionId $sshsessions[$zone-1].SessionId
$bandwidthtemp = [string]$output.Output
$bandwidthtemp = $bandwidthtemp -replace '\s+', ' '
$bandwidthtemp = $bandwidthtemp -Split ". "
$bandwidthtemp = [int]$bandwidthtemp[1] / 1024
$bandwidthtemp = [string]([math]::ceiling($bandwidthtemp)) + " MB/s"
$bandwidthtemp = $bandwidthtemp.PadLeft(12)
$bandwidth[$zone -1][$vmtopingno1 -1] = $bandwidthtemp
$output = Invoke-SSHCommand -Command "/tmp/niping -c -B 10 -L 100 -H $ipaddresstoping2 | grep av2" -SessionId $sshsessions[$zone-1].SessionId
$latencytemp = [string]$output.Output
$latencytemp = $latencytemp -replace '\s+', ' '
$latencytemp = $latencytemp -Split " "
$latencytemp = [string]$latencytemp[1] + " " + $latencytemp[2]
$latencytemp = $latencytemp.PadLeft(12)
$latency[$zone -1][$vmtopingno2 -1] = $latencytemp
$output = Invoke-SSHCommand -Command "/tmp/niping -c -B 100000 -L 100 -H $ipaddresstoping2 | grep tr2" -SessionId $sshsessions[$zone-1].SessionId
$bandwidthtemp = [string]$output.Output
$bandwidthtemp = $bandwidthtemp -replace '\s+', ' '
$bandwidthtemp = $bandwidthtemp -Split ". "
$bandwidthtemp = [int]$bandwidthtemp[1] / 1024
$bandwidthtemp = [string]([math]::ceiling($bandwidthtemp)) + " MB/s"
$bandwidthtemp = $bandwidthtemp.PadLeft(12)
$bandwidth[$zone -1][$vmtopingno2 -1] = $bandwidthtemp
}
}
# Print output
Write-Host "Region: " $region
Write-Host "VM Type: " $VMSize
Write-Host "Latency:"
Write-Host " ----------------------------------------------"
Write-Host " | zone 1 | zone 2 | zone 3 |"
Write-Host "-------------------------------------------------------"
Write-Host "| zone 1 | |" $latency[0][1] "|" $latency[0][2] "|"
Write-Host "| zone 2 |" $latency[1][0] "| |" $latency[1][2] "|"
Write-Host "| zone 3 |" $latency[2][0] "|" $latency[2][1] "| |"
Write-Host "-------------------------------------------------------"
Write-Host ""
Write-Host "Bandwidth:"
Write-Host " ----------------------------------------------"
Write-Host " | zone 1 | zone 2 | zone 3 |"
Write-Host "-------------------------------------------------------"
Write-Host "| zone 1 | |" $bandwidth[0][1] "|" $bandwidth[0][2] "|"
Write-Host "| zone 2 |" $bandwidth[1][0] "| |" $bandwidth[1][2] "|"
Write-Host "| zone 3 |" $bandwidth[2][0] "|" $bandwidth[2][1] "| |"
Write-Host "-------------------------------------------------------"
# Removing SSH sessions
Write-Host -ForegroundColor Green "Removing SSH Sessions"
#Get-SSHSession | Remove-SSHSession -ErrorAction SilentlyContinue | Out-Null
$sshsessions = Get-SSHSession
foreach ($sshsession in $sshsessions) {
Remove-SSHSession -SessionId $sshsession.SessionId
}
#destroy resource group
if ($DestroyAfterTest) {
Write-Host -ForegroundColor Green "Deleting Resource Group"
Remove-AzResourceGroup -Name $ResourceGroupName -Force
}
else
{
Write-Host -ForegroundColor Green "Resource group will NOT be deleted"
}
if ($breakingchangewarning.Value -eq $true) {
Update-AzConfig -DisplayBreakingChangeWarning $true
}