mirror of
https://github.com/AuxXxilium/yet-another-bench-script.git
synced 2024-11-23 23:01:02 +07:00
d1dcd70713
HOSTKEY's iperf3 server seems to be limited to 1G. Another option in Helsinki would be Telia and they seem to be able to reach 10G without issues.
1034 lines
44 KiB
Bash
1034 lines
44 KiB
Bash
#!/bin/bash
|
|
|
|
# Yet Another Bench Script by Mason Rowe
|
|
# Initial Oct 2019; Last update Mar 2024
|
|
|
|
# Disclaimer: This project is a work in progress. Any errors or suggestions should be
|
|
# relayed to me via the GitHub project page linked below.
|
|
#
|
|
# Purpose: The purpose of this script is to quickly gauge the performance of a Linux-
|
|
# based server by benchmarking network performance via iperf3, CPU and
|
|
# overall system performance via Geekbench 4/5, and random disk
|
|
# performance via fio. The script is designed to not require any dependencies
|
|
# - either compiled or installed - nor admin privileges to run.
|
|
|
|
YABS_VERSION="v2024-03-05"
|
|
|
|
echo -e '# ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## #'
|
|
echo -e '# Yet-Another-Bench-Script #'
|
|
echo -e '# '$YABS_VERSION' #'
|
|
echo -e '# https://github.com/masonr/yet-another-bench-script #'
|
|
echo -e '# ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## #'
|
|
|
|
echo -e
|
|
date
|
|
TIME_START=$(date '+%Y%m%d-%H%M%S')
|
|
YABS_START_TIME=$(date +%s)
|
|
|
|
# override locale to eliminate parsing errors (i.e. using commas as delimiters rather than periods)
|
|
if locale -a 2>/dev/null | grep ^C$ > /dev/null; then
|
|
# locale "C" installed
|
|
export LC_ALL=C
|
|
else
|
|
# locale "C" not installed, display warning
|
|
echo -e "\nWarning: locale 'C' not detected. Test outputs may not be parsed correctly."
|
|
fi
|
|
|
|
# determine architecture of host
|
|
ARCH=$(uname -m)
|
|
if [[ $ARCH = *x86_64* ]]; then
|
|
# host is running a 64-bit kernel
|
|
ARCH="x64"
|
|
elif [[ $ARCH = *i?86* ]]; then
|
|
# host is running a 32-bit kernel
|
|
ARCH="x86"
|
|
elif [[ $ARCH = *aarch* || $ARCH = *arm* ]]; then
|
|
KERNEL_BIT=$(getconf LONG_BIT)
|
|
if [[ $KERNEL_BIT = *64* ]]; then
|
|
# host is running an ARM 64-bit kernel
|
|
ARCH="aarch64"
|
|
else
|
|
# host is running an ARM 32-bit kernel
|
|
ARCH="arm"
|
|
fi
|
|
echo -e "\nARM compatibility is considered *experimental*"
|
|
else
|
|
# host is running a non-supported kernel
|
|
echo -e "Architecture not supported by YABS."
|
|
exit 1
|
|
fi
|
|
|
|
# flags to skip certain performance tests
|
|
unset PREFER_BIN SKIP_FIO SKIP_IPERF SKIP_GEEKBENCH SKIP_NET PRINT_HELP REDUCE_NET GEEKBENCH_4 GEEKBENCH_5 GEEKBENCH_6 DD_FALLBACK IPERF_DL_FAIL JSON JSON_SEND JSON_RESULT JSON_FILE
|
|
GEEKBENCH_6="True" # gb6 test enabled by default
|
|
|
|
# get any arguments that were passed to the script and set the associated skip flags (if applicable)
|
|
while getopts 'bfdignhr4596jw:s:' flag; do
|
|
case "${flag}" in
|
|
b) PREFER_BIN="True" ;;
|
|
f) SKIP_FIO="True" ;;
|
|
d) SKIP_FIO="True" ;;
|
|
i) SKIP_IPERF="True" ;;
|
|
g) SKIP_GEEKBENCH="True" ;;
|
|
n) SKIP_NET="True" ;;
|
|
h) PRINT_HELP="True" ;;
|
|
r) REDUCE_NET="True" ;;
|
|
4) GEEKBENCH_4="True" && unset GEEKBENCH_6 ;;
|
|
5) GEEKBENCH_5="True" && unset GEEKBENCH_6 ;;
|
|
9) GEEKBENCH_4="True" && GEEKBENCH_5="True" && unset GEEKBENCH_6 ;;
|
|
6) GEEKBENCH_6="True" ;;
|
|
j) JSON+="j" ;;
|
|
w) JSON+="w" && JSON_FILE=${OPTARG} ;;
|
|
s) JSON+="s" && JSON_SEND=${OPTARG} ;;
|
|
*) exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
# check for local fio/iperf installs
|
|
command -v fio >/dev/null 2>&1 && LOCAL_FIO=true || unset LOCAL_FIO
|
|
command -v iperf3 >/dev/null 2>&1 && LOCAL_IPERF=true || unset LOCAL_IPERF
|
|
|
|
# check for ping
|
|
command -v ping >/dev/null 2>&1 && LOCAL_PING=true || unset LOCAL_PING
|
|
|
|
# check for curl/wget
|
|
command -v curl >/dev/null 2>&1 && LOCAL_CURL=true || unset LOCAL_CURL
|
|
|
|
# test if the host has IPv4/IPv6 connectivity
|
|
[[ ! -z $LOCAL_CURL ]] && IP_CHECK_CMD="curl -s -m 4" || IP_CHECK_CMD="wget -qO- -T 4"
|
|
IPV4_CHECK=$( (ping -4 -c 1 -W 4 ipv4.google.com >/dev/null 2>&1 && echo true) || $IP_CHECK_CMD -4 icanhazip.com 2> /dev/null)
|
|
IPV6_CHECK=$( (ping -6 -c 1 -W 4 ipv6.google.com >/dev/null 2>&1 && echo true) || $IP_CHECK_CMD -6 icanhazip.com 2> /dev/null)
|
|
if [[ -z "$IPV4_CHECK" && -z "$IPV6_CHECK" ]]; then
|
|
echo -e
|
|
echo -e "Warning: Both IPv4 AND IPv6 connectivity were not detected. Check for DNS issues..."
|
|
fi
|
|
|
|
# print help and exit script, if help flag was passed
|
|
if [ ! -z "$PRINT_HELP" ]; then
|
|
echo -e
|
|
echo -e "Usage: ./yabs.sh [-flags]"
|
|
echo -e " curl -sL yabs.sh | bash"
|
|
echo -e " curl -sL yabs.sh | bash -s -- -flags"
|
|
echo -e " wget -qO- yabs.sh | bash"
|
|
echo -e " wget -qO- yabs.sh | bash -s -- -flags"
|
|
echo -e
|
|
echo -e "Flags:"
|
|
echo -e " -b : prefer pre-compiled binaries from repo over local packages"
|
|
echo -e " -f/d : skips the fio disk benchmark test"
|
|
echo -e " -i : skips the iperf network test"
|
|
echo -e " -g : skips the geekbench performance test"
|
|
echo -e " -n : skips the network information lookup and print out"
|
|
echo -e " -h : prints this lovely message, shows any flags you passed,"
|
|
echo -e " shows if fio/iperf3 local packages have been detected,"
|
|
echo -e " then exits"
|
|
echo -e " -r : reduce number of iperf3 network locations (to only three)"
|
|
echo -e " to lessen bandwidth usage"
|
|
echo -e " -4 : use geekbench 4 instead of geekbench 6"
|
|
echo -e " -5 : use geekbench 5 instead of geekbench 6"
|
|
echo -e " -9 : use both geekbench 4 AND geekbench 5 instead of geekbench 6"
|
|
echo -e " -6 : user geekbench 6 in addition to 4 and/or 5 (only needed if -4, -5, or -9 are set; -6 must come last)"
|
|
echo -e " -j : print jsonified YABS results at conclusion of test"
|
|
echo -e " -w <filename> : write jsonified YABS results to disk using file name provided"
|
|
echo -e " -s <url> : send jsonified YABS results to URL"
|
|
echo -e
|
|
echo -e "Detected Arch: $ARCH"
|
|
echo -e
|
|
echo -e "Detected Flags:"
|
|
[[ ! -z $PREFER_BIN ]] && echo -e " -b, force using precompiled binaries from repo"
|
|
[[ ! -z $SKIP_FIO ]] && echo -e " -f/d, skipping fio disk benchmark test"
|
|
[[ ! -z $SKIP_IPERF ]] && echo -e " -i, skipping iperf network test"
|
|
[[ ! -z $SKIP_GEEKBENCH ]] && echo -e " -g, skipping geekbench test"
|
|
[[ ! -z $SKIP_NET ]] && echo -e " -n, skipping network info lookup and print out"
|
|
[[ ! -z $REDUCE_NET ]] && echo -e " -r, using reduced (3) iperf3 locations"
|
|
[[ ! -z $GEEKBENCH_4 ]] && echo -e " running geekbench 4"
|
|
[[ ! -z $GEEKBENCH_5 ]] && echo -e " running geekbench 5"
|
|
[[ ! -z $GEEKBENCH_6 ]] && echo -e " running geekbench 6"
|
|
echo -e
|
|
echo -e "Local Binary Check:"
|
|
[[ -z $LOCAL_FIO ]] && echo -e " fio not detected, will download precompiled binary" ||
|
|
[[ -z $PREFER_BIN ]] && echo -e " fio detected, using local package" ||
|
|
echo -e " fio detected, but using precompiled binary instead"
|
|
[[ -z $LOCAL_IPERF ]] && echo -e " iperf3 not detected, will download precompiled binary" ||
|
|
[[ -z $PREFER_BIN ]] && echo -e " iperf3 detected, using local package" ||
|
|
echo -e " iperf3 detected, but using precompiled binary instead"
|
|
echo -e
|
|
echo -e "Detected Connectivity:"
|
|
[[ ! -z $IPV4_CHECK ]] && echo -e " IPv4 connected" ||
|
|
echo -e " IPv4 not connected"
|
|
[[ ! -z $IPV6_CHECK ]] && echo -e " IPv6 connected" ||
|
|
echo -e " IPv6 not connected"
|
|
echo -e
|
|
echo -e "JSON Options:"
|
|
[[ -z $JSON ]] && echo -e " none"
|
|
[[ $JSON = *j* ]] && echo -e " printing json to screen after test"
|
|
[[ $JSON = *w* ]] && echo -e " writing json to file ($JSON_FILE) after test"
|
|
[[ $JSON = *s* ]] && echo -e " sharing json YABS results to $JSON_SEND"
|
|
echo -e
|
|
echo -e "Exiting..."
|
|
|
|
exit 0
|
|
fi
|
|
|
|
# format_size
|
|
# Purpose: Formats raw disk and memory sizes from kibibytes (KiB) to largest unit
|
|
# Parameters:
|
|
# 1. RAW - the raw memory size (RAM/Swap) in kibibytes
|
|
# Returns:
|
|
# Formatted memory size in KiB, MiB, GiB, or TiB
|
|
function format_size {
|
|
RAW=$1 # mem size in KiB
|
|
RESULT=$RAW
|
|
local DENOM=1
|
|
local UNIT="KiB"
|
|
|
|
# ensure the raw value is a number, otherwise return blank
|
|
re='^[0-9]+$'
|
|
if ! [[ $RAW =~ $re ]] ; then
|
|
echo ""
|
|
return 0
|
|
fi
|
|
|
|
if [ "$RAW" -ge 1073741824 ]; then
|
|
DENOM=1073741824
|
|
UNIT="TiB"
|
|
elif [ "$RAW" -ge 1048576 ]; then
|
|
DENOM=1048576
|
|
UNIT="GiB"
|
|
elif [ "$RAW" -ge 1024 ]; then
|
|
DENOM=1024
|
|
UNIT="MiB"
|
|
fi
|
|
|
|
# divide the raw result to get the corresponding formatted result (based on determined unit)
|
|
RESULT=$(awk -v a="$RESULT" -v b="$DENOM" 'BEGIN { print a / b }')
|
|
# shorten the formatted result to two decimal places (i.e. x.x)
|
|
RESULT=$(echo $RESULT | awk -F. '{ printf "%0.1f",$1"."substr($2,1,2) }')
|
|
# concat formatted result value with units and return result
|
|
RESULT="$RESULT $UNIT"
|
|
echo $RESULT
|
|
}
|
|
|
|
# gather basic system information (inc. CPU, AES-NI/virt status, RAM + swap + disk size)
|
|
echo -e
|
|
echo -e "Basic System Information:"
|
|
echo -e "---------------------------------"
|
|
UPTIME=$(uptime | awk -F'( |,|:)+' '{d=h=m=0; if ($7=="min") m=$6; else {if ($7~/^day/) {d=$6;h=$8;m=$9} else {h=$6;m=$7}}} {print d+0,"days,",h+0,"hours,",m+0,"minutes"}')
|
|
echo -e "Uptime : $UPTIME"
|
|
# check for local lscpu installs
|
|
command -v lscpu >/dev/null 2>&1 && LOCAL_LSCPU=true || unset LOCAL_LSCPU
|
|
if [[ $ARCH = *aarch64* || $ARCH = *arm* ]] && [[ ! -z $LOCAL_LSCPU ]]; then
|
|
CPU_PROC=$(lscpu | grep "Model name" | sed 's/Model name: *//g')
|
|
else
|
|
CPU_PROC=$(awk -F: '/model name/ {name=$2} END {print name}' /proc/cpuinfo | sed 's/^[ \t]*//;s/[ \t]*$//')
|
|
fi
|
|
echo -e "Processor : $CPU_PROC"
|
|
if [[ $ARCH = *aarch64* || $ARCH = *arm* ]] && [[ ! -z $LOCAL_LSCPU ]]; then
|
|
CPU_CORES=$(lscpu | grep "^[[:blank:]]*CPU(s):" | sed 's/CPU(s): *//g')
|
|
CPU_FREQ=$(lscpu | grep "CPU max MHz" | sed 's/CPU max MHz: *//g')
|
|
[[ -z "$CPU_FREQ" ]] && CPU_FREQ="???"
|
|
CPU_FREQ="${CPU_FREQ} MHz"
|
|
else
|
|
CPU_CORES=$(awk -F: '/model name/ {core++} END {print core}' /proc/cpuinfo)
|
|
CPU_FREQ=$(awk -F: ' /cpu MHz/ {freq=$2} END {print freq " MHz"}' /proc/cpuinfo | sed 's/^[ \t]*//;s/[ \t]*$//')
|
|
fi
|
|
echo -e "CPU cores : $CPU_CORES @ $CPU_FREQ"
|
|
CPU_AES=$(cat /proc/cpuinfo | grep aes)
|
|
[[ -z "$CPU_AES" ]] && CPU_AES="\xE2\x9D\x8C Disabled" || CPU_AES="\xE2\x9C\x94 Enabled"
|
|
echo -e "AES-NI : $CPU_AES"
|
|
CPU_VIRT=$(cat /proc/cpuinfo | grep 'vmx\|svm')
|
|
[[ -z "$CPU_VIRT" ]] && CPU_VIRT="\xE2\x9D\x8C Disabled" || CPU_VIRT="\xE2\x9C\x94 Enabled"
|
|
echo -e "VM-x/AMD-V : $CPU_VIRT"
|
|
TOTAL_RAM_RAW=$(free | awk 'NR==2 {print $2}')
|
|
TOTAL_RAM=$(format_size $TOTAL_RAM_RAW)
|
|
echo -e "RAM : $TOTAL_RAM"
|
|
TOTAL_SWAP_RAW=$(free | grep Swap | awk '{ print $2 }')
|
|
TOTAL_SWAP=$(format_size $TOTAL_SWAP_RAW)
|
|
echo -e "Swap : $TOTAL_SWAP"
|
|
# total disk size is calculated by adding all partitions of the types listed below (after the -t flags)
|
|
TOTAL_DISK_RAW=$(df -t simfs -t ext2 -t ext3 -t ext4 -t btrfs -t xfs -t vfat -t ntfs -t swap --total 2>/dev/null | grep total | awk '{ print $2 }')
|
|
TOTAL_DISK=$(format_size $TOTAL_DISK_RAW)
|
|
echo -e "Disk : $TOTAL_DISK"
|
|
DISTRO=$(grep 'PRETTY_NAME' /etc/os-release | cut -d '"' -f 2 )
|
|
echo -e "Distro : $DISTRO"
|
|
KERNEL=$(uname -r)
|
|
echo -e "Kernel : $KERNEL"
|
|
VIRT=$(systemd-detect-virt 2>/dev/null)
|
|
VIRT=${VIRT^^} || VIRT="UNKNOWN"
|
|
echo -e "VM Type : $VIRT"
|
|
[[ -z "$IPV4_CHECK" ]] && ONLINE="\xE2\x9D\x8C Offline / " || ONLINE="\xE2\x9C\x94 Online / "
|
|
[[ -z "$IPV6_CHECK" ]] && ONLINE+="\xE2\x9D\x8C Offline" || ONLINE+="\xE2\x9C\x94 Online"
|
|
echo -e "IPv4/IPv6 : $ONLINE"
|
|
|
|
# Function to get information from IP Address using ip-api.com free API
|
|
function ip_info() {
|
|
# check for curl vs wget
|
|
[[ ! -z $LOCAL_CURL ]] && DL_CMD="curl -s" || DL_CMD="wget -qO-"
|
|
|
|
local ip6me_resp="$($DL_CMD http://ip6.me/api/)"
|
|
local net_type="$(echo $ip6me_resp | cut -d, -f1)"
|
|
local net_ip="$(echo $ip6me_resp | cut -d, -f2)"
|
|
|
|
local response=$($DL_CMD http://ip-api.com/json/$net_ip)
|
|
|
|
# if no response, skip output
|
|
if [[ -z $response ]]; then
|
|
return
|
|
fi
|
|
|
|
local country=$(echo "$response" | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^country/ {print $2}' | head -1 | sed 's/^"\(.*\)"$/\1/')
|
|
local region=$(echo "$response" | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^regionName/ {print $2}' | sed 's/^"\(.*\)"$/\1/')
|
|
local region_code=$(echo "$response" | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^region/ {print $2}' | head -1 | sed 's/^"\(.*\)"$/\1/')
|
|
local city=$(echo "$response" | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^city/ {print $2}' | sed 's/^"\(.*\)"$/\1/')
|
|
local isp=$(echo "$response" | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^isp/ {print $2}' | sed 's/^"\(.*\)"$/\1/')
|
|
local org=$(echo "$response" | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^org/ {print $2}' | sed 's/^"\(.*\)"$/\1/')
|
|
local as=$(echo "$response" | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^as/ {print $2}' | sed 's/^"\(.*\)"$/\1/')
|
|
|
|
echo
|
|
echo "$net_type Network Information:"
|
|
echo "---------------------------------"
|
|
|
|
if [[ -n "$isp" ]]; then
|
|
echo "ISP : $isp"
|
|
else
|
|
echo "ISP : Unknown"
|
|
fi
|
|
if [[ -n "$as" ]]; then
|
|
echo "ASN : $as"
|
|
else
|
|
echo "ASN : Unknown"
|
|
fi
|
|
if [[ -n "$org" ]]; then
|
|
echo "Host : $org"
|
|
fi
|
|
if [[ -n "$city" && -n "$region" ]]; then
|
|
echo "Location : $city, $region ($region_code)"
|
|
fi
|
|
if [[ -n "$country" ]]; then
|
|
echo "Country : $country"
|
|
fi
|
|
|
|
[[ ! -z $JSON ]] && JSON_RESULT+=',"ip_info":{"protocol":"'$net_type'","isp":"'$isp'","asn":"'$as'","org":"'$org'","city":"'$city'","region":"'$region'","region_code":"'$region_code'","country":"'$country'"}'
|
|
}
|
|
|
|
if [ ! -z $JSON ]; then
|
|
UPTIME_S=$(awk '{print $1}' /proc/uptime)
|
|
IPV4=$([ ! -z $IPV4_CHECK ] && echo "true" || echo "false")
|
|
IPV6=$([ ! -z $IPV6_CHECK ] && echo "true" || echo "false")
|
|
AES=$([[ "$CPU_AES" = *Enabled* ]] && echo "true" || echo "false")
|
|
CPU_VIRT_BOOL=$([[ "$CPU_VIRT" = *Enabled* ]] && echo "true" || echo "false")
|
|
JSON_RESULT='{"version":"'$YABS_VERSION'","time":"'$TIME_START'","os":{"arch":"'$ARCH'","distro":"'$DISTRO'","kernel":"'$KERNEL'",'
|
|
JSON_RESULT+='"uptime":'$UPTIME_S',"vm":"'$VIRT'"},"net":{"ipv4":'$IPV4',"ipv6":'$IPV6'},"cpu":{"model":"'$CPU_PROC'","cores":'$CPU_CORES','
|
|
JSON_RESULT+='"freq":"'$CPU_FREQ'","aes":'$AES',"virt":'$CPU_VIRT_BOOL'},"mem":{"ram":'$TOTAL_RAM_RAW',"ram_units":"KiB","swap":'$TOTAL_SWAP_RAW',"swap_units":"KiB","disk":'$TOTAL_DISK_RAW',"disk_units":"KB"}'
|
|
fi
|
|
|
|
if [ -z $SKIP_NET ]; then
|
|
ip_info
|
|
fi
|
|
|
|
# create a directory in the same location that the script is being run to temporarily store YABS-related files
|
|
DATE=$(date -Iseconds | sed -e "s/:/_/g")
|
|
YABS_PATH=./$DATE
|
|
touch "$DATE.test" 2> /dev/null
|
|
# test if the user has write permissions in the current directory and exit if not
|
|
if [ ! -f "$DATE.test" ]; then
|
|
echo -e
|
|
echo -e "You do not have write permission in this directory. Switch to an owned directory and re-run the script.\nExiting..."
|
|
exit 1
|
|
fi
|
|
rm "$DATE.test"
|
|
mkdir -p "$YABS_PATH"
|
|
|
|
# trap CTRL+C signals to exit script cleanly
|
|
trap catch_abort INT
|
|
|
|
# catch_abort
|
|
# Purpose: This method will catch CTRL+C signals in order to exit the script cleanly and remove
|
|
# yabs-related files.
|
|
function catch_abort() {
|
|
echo -e "\n** Aborting YABS. Cleaning up files...\n"
|
|
rm -rf "$YABS_PATH"
|
|
unset LC_ALL
|
|
exit 0
|
|
}
|
|
|
|
# format_speed
|
|
# Purpose: This method is a convenience function to format the output of the fio disk tests which
|
|
# always returns a result in KB/s. If result is >= 1 GB/s, use GB/s. If result is < 1 GB/s
|
|
# and >= 1 MB/s, then use MB/s. Otherwise, use KB/s.
|
|
# Parameters:
|
|
# 1. RAW - the raw disk speed result (in KB/s)
|
|
# Returns:
|
|
# Formatted disk speed in GB/s, MB/s, or KB/s
|
|
function format_speed {
|
|
RAW=$1 # disk speed in KB/s
|
|
RESULT=$RAW
|
|
local DENOM=1
|
|
local UNIT="KB/s"
|
|
|
|
# ensure raw value is not null, if it is, return blank
|
|
if [ -z "$RAW" ]; then
|
|
echo ""
|
|
return 0
|
|
fi
|
|
|
|
# check if disk speed >= 1 GB/s
|
|
if [ "$RAW" -ge 1000000 ]; then
|
|
DENOM=1000000
|
|
UNIT="GB/s"
|
|
# check if disk speed < 1 GB/s && >= 1 MB/s
|
|
elif [ "$RAW" -ge 1000 ]; then
|
|
DENOM=1000
|
|
UNIT="MB/s"
|
|
fi
|
|
|
|
# divide the raw result to get the corresponding formatted result (based on determined unit)
|
|
RESULT=$(awk -v a="$RESULT" -v b="$DENOM" 'BEGIN { print a / b }')
|
|
# shorten the formatted result to two decimal places (i.e. x.xx)
|
|
RESULT=$(echo $RESULT | awk -F. '{ printf "%0.2f",$1"."substr($2,1,2) }')
|
|
# concat formatted result value with units and return result
|
|
RESULT="$RESULT $UNIT"
|
|
echo $RESULT
|
|
}
|
|
|
|
# format_iops
|
|
# Purpose: This method is a convenience function to format the output of the raw IOPS result
|
|
# Parameters:
|
|
# 1. RAW - the raw IOPS result
|
|
# Returns:
|
|
# Formatted IOPS (i.e. 8, 123, 1.7k, 275.9k, etc.)
|
|
function format_iops {
|
|
RAW=$1 # iops
|
|
RESULT=$RAW
|
|
|
|
# ensure raw value is not null, if it is, return blank
|
|
if [ -z "$RAW" ]; then
|
|
echo ""
|
|
return 0
|
|
fi
|
|
|
|
# check if IOPS speed > 1k
|
|
if [ "$RAW" -ge 1000 ]; then
|
|
# divide the raw result by 1k
|
|
RESULT=$(awk -v a="$RESULT" 'BEGIN { print a / 1000 }')
|
|
# shorten the formatted result to one decimal place (i.e. x.x)
|
|
RESULT=$(echo $RESULT | awk -F. '{ printf "%0.1f",$1"."substr($2,1,1) }')
|
|
RESULT="$RESULT"k
|
|
fi
|
|
|
|
echo $RESULT
|
|
}
|
|
|
|
# disk_test
|
|
# Purpose: This method is designed to test the disk performance of the host using the partition that the
|
|
# script is being run from using fio random read/write speed tests.
|
|
# Parameters:
|
|
# - (none)
|
|
function disk_test {
|
|
if [[ "$ARCH" = "aarch64" || "$ARCH" = "arm" ]]; then
|
|
FIO_SIZE=512M
|
|
else
|
|
FIO_SIZE=2G
|
|
fi
|
|
|
|
# run a quick test to generate the fio test file to be used by the actual tests
|
|
echo -en "Generating fio test file..."
|
|
$FIO_CMD --name=setup --ioengine=libaio --rw=read --bs=64k --iodepth=64 --numjobs=2 --size=$FIO_SIZE --runtime=1 --gtod_reduce=1 --filename="$DISK_PATH/test.fio" --direct=1 --minimal &> /dev/null
|
|
echo -en "\r\033[0K"
|
|
|
|
# get array of block sizes to evaluate
|
|
BLOCK_SIZES=("$@")
|
|
|
|
for BS in "${BLOCK_SIZES[@]}"; do
|
|
# run rand read/write mixed fio test with block size = $BS
|
|
echo -en "Running fio random mixed R+W disk test with $BS block size..."
|
|
DISK_TEST=$(timeout 35 $FIO_CMD --name=rand_rw_$BS --ioengine=libaio --rw=randrw --rwmixread=50 --bs=$BS --iodepth=64 --numjobs=2 --size=$FIO_SIZE --runtime=30 --gtod_reduce=1 --direct=1 --filename="$DISK_PATH/test.fio" --group_reporting --minimal 2> /dev/null | grep rand_rw_$BS)
|
|
DISK_IOPS_R=$(echo $DISK_TEST | awk -F';' '{print $8}')
|
|
DISK_IOPS_W=$(echo $DISK_TEST | awk -F';' '{print $49}')
|
|
DISK_IOPS=$(awk -v a="$DISK_IOPS_R" -v b="$DISK_IOPS_W" 'BEGIN { print a + b }')
|
|
DISK_TEST_R=$(echo $DISK_TEST | awk -F';' '{print $7}')
|
|
DISK_TEST_W=$(echo $DISK_TEST | awk -F';' '{print $48}')
|
|
DISK_TEST=$(awk -v a="$DISK_TEST_R" -v b="$DISK_TEST_W" 'BEGIN { print a + b }')
|
|
DISK_RESULTS_RAW+=( "$DISK_TEST" "$DISK_TEST_R" "$DISK_TEST_W" "$DISK_IOPS" "$DISK_IOPS_R" "$DISK_IOPS_W" )
|
|
|
|
DISK_IOPS=$(format_iops $DISK_IOPS)
|
|
DISK_IOPS_R=$(format_iops $DISK_IOPS_R)
|
|
DISK_IOPS_W=$(format_iops $DISK_IOPS_W)
|
|
DISK_TEST=$(format_speed $DISK_TEST)
|
|
DISK_TEST_R=$(format_speed $DISK_TEST_R)
|
|
DISK_TEST_W=$(format_speed $DISK_TEST_W)
|
|
|
|
DISK_RESULTS+=( "$DISK_TEST" "$DISK_TEST_R" "$DISK_TEST_W" "$DISK_IOPS" "$DISK_IOPS_R" "$DISK_IOPS_W" )
|
|
echo -en "\r\033[0K"
|
|
done
|
|
}
|
|
|
|
# dd_test
|
|
# Purpose: This method is invoked if the fio disk test failed. dd sequential speed tests are
|
|
# not indiciative or real-world results, however, some form of disk speed measure
|
|
# is better than nothing.
|
|
# Parameters:
|
|
# - (none)
|
|
function dd_test {
|
|
I=0
|
|
DISK_WRITE_TEST_RES=()
|
|
DISK_READ_TEST_RES=()
|
|
DISK_WRITE_TEST_AVG=0
|
|
DISK_READ_TEST_AVG=0
|
|
|
|
# run the disk speed tests (write and read) thrice over
|
|
while [ $I -lt 3 ]
|
|
do
|
|
# write test using dd, "direct" flag is used to test direct I/O for data being stored to disk
|
|
DISK_WRITE_TEST=$(dd if=/dev/zero of="$DISK_PATH/$DATE.test" bs=64k count=16k oflag=direct |& grep copied | awk '{ print $(NF-1) " " $(NF)}')
|
|
VAL=$(echo $DISK_WRITE_TEST | cut -d " " -f 1)
|
|
[[ "$DISK_WRITE_TEST" == *"GB"* ]] && VAL=$(awk -v a="$VAL" 'BEGIN { print a * 1000 }')
|
|
DISK_WRITE_TEST_RES+=( "$DISK_WRITE_TEST" )
|
|
DISK_WRITE_TEST_AVG=$(awk -v a="$DISK_WRITE_TEST_AVG" -v b="$VAL" 'BEGIN { print a + b }')
|
|
|
|
# read test using dd using the 1G file written during the write test
|
|
DISK_READ_TEST=$(dd if="$DISK_PATH/$DATE.test" of=/dev/null bs=8k |& grep copied | awk '{ print $(NF-1) " " $(NF)}')
|
|
VAL=$(echo $DISK_READ_TEST | cut -d " " -f 1)
|
|
[[ "$DISK_READ_TEST" == *"GB"* ]] && VAL=$(awk -v a="$VAL" 'BEGIN { print a * 1000 }')
|
|
DISK_READ_TEST_RES+=( "$DISK_READ_TEST" )
|
|
DISK_READ_TEST_AVG=$(awk -v a="$DISK_READ_TEST_AVG" -v b="$VAL" 'BEGIN { print a + b }')
|
|
|
|
I=$(( $I + 1 ))
|
|
done
|
|
# calculate the write and read speed averages using the results from the three runs
|
|
DISK_WRITE_TEST_AVG=$(awk -v a="$DISK_WRITE_TEST_AVG" 'BEGIN { print a / 3 }')
|
|
DISK_READ_TEST_AVG=$(awk -v a="$DISK_READ_TEST_AVG" 'BEGIN { print a / 3 }')
|
|
}
|
|
|
|
# check if disk performance is being tested and the host has required space (2G)
|
|
AVAIL_SPACE=$(df -k . | awk 'NR==2{print $4}')
|
|
if [[ -z "$SKIP_FIO" && "$AVAIL_SPACE" -lt 2097152 && "$ARCH" != "aarch64" && "$ARCH" != "arm" ]]; then # 2GB = 2097152KB
|
|
echo -e "\nLess than 2GB of space available. Skipping disk test..."
|
|
elif [[ -z "$SKIP_FIO" && "$AVAIL_SPACE" -lt 524288 && ("$ARCH" = "aarch64" || "$ARCH" = "arm") ]]; then # 512MB = 524288KB
|
|
echo -e "\nLess than 512MB of space available. Skipping disk test..."
|
|
# if the skip disk flag was set, skip the disk performance test, otherwise test disk performance
|
|
elif [ -z "$SKIP_FIO" ]; then
|
|
# Perform ZFS filesystem detection and determine if we have enough free space according to spa_asize_inflation
|
|
ZFSCHECK="/sys/module/zfs/parameters/spa_asize_inflation"
|
|
if [[ -f "$ZFSCHECK" ]];then
|
|
mul_spa=$((($(cat /sys/module/zfs/parameters/spa_asize_inflation)*2)))
|
|
warning=0
|
|
poss=()
|
|
|
|
for pathls in $(df -Th | awk '{print $7}' | tail -n +2)
|
|
do
|
|
if [[ "${PWD##$pathls}" != "${PWD}" ]]; then
|
|
poss+=("$pathls")
|
|
fi
|
|
done
|
|
|
|
long=""
|
|
m=-1
|
|
for x in ${poss[@]}
|
|
do
|
|
if [ ${#x} -gt $m ];then
|
|
m=${#x}
|
|
long=$x
|
|
fi
|
|
done
|
|
|
|
size_b=$(df -Th | grep -w $long | grep -i zfs | awk '{print $5}' | tail -c -2 | head -c 1)
|
|
free_space=$(df -Th | grep -w $long | grep -i zfs | awk '{print $5}' | head -c -2)
|
|
|
|
if [[ $size_b == 'T' ]]; then
|
|
free_space=$(awk "BEGIN {print int($free_space * 1024)}")
|
|
size_b='G'
|
|
fi
|
|
|
|
if [[ $(df -Th | grep -w $long) == *"zfs"* ]];then
|
|
|
|
if [[ $size_b == 'G' ]]; then
|
|
if ((free_space < mul_spa)); then
|
|
warning=1
|
|
fi
|
|
else
|
|
warning=1
|
|
fi
|
|
|
|
fi
|
|
|
|
if [[ $warning -eq 1 ]];then
|
|
echo -en "\nWarning! You are running YABS on a ZFS Filesystem and your disk space is too low for the fio test. Your test results will be inaccurate. You need at least $mul_spa GB free in order to complete this test accurately. For more information, please see https://github.com/masonr/yet-another-bench-script/issues/13\n"
|
|
fi
|
|
fi
|
|
|
|
echo -en "\nPreparing system for disk tests..."
|
|
|
|
# create temp directory to store disk write/read test files
|
|
DISK_PATH=$YABS_PATH/disk
|
|
mkdir -p "$DISK_PATH"
|
|
|
|
if [[ -z "$PREFER_BIN" && ! -z "$LOCAL_FIO" ]]; then # local fio has been detected, use instead of pre-compiled binary
|
|
FIO_CMD=fio
|
|
else
|
|
# download fio binary
|
|
if [[ ! -z $LOCAL_CURL ]]; then
|
|
curl -s --connect-timeout 5 --retry 5 --retry-delay 0 https://raw.githubusercontent.com/masonr/yet-another-bench-script/master/bin/fio/fio_$ARCH -o "$DISK_PATH/fio"
|
|
else
|
|
wget -q -T 5 -t 5 -w 0 https://raw.githubusercontent.com/masonr/yet-another-bench-script/master/bin/fio/fio_$ARCH -O "$DISK_PATH/fio"
|
|
fi
|
|
|
|
if [ ! -f "$DISK_PATH/fio" ]; then # ensure fio binary download successfully
|
|
echo -en "\r\033[0K"
|
|
echo -e "Fio binary download failed. Running dd test as fallback...."
|
|
DD_FALLBACK=True
|
|
else
|
|
chmod +x "$DISK_PATH/fio"
|
|
FIO_CMD=$DISK_PATH/fio
|
|
fi
|
|
fi
|
|
|
|
if [ -z "$DD_FALLBACK" ]; then # if not falling back on dd tests, run fio test
|
|
echo -en "\r\033[0K"
|
|
|
|
# init global array to store disk performance values
|
|
declare -a DISK_RESULTS DISK_RESULTS_RAW
|
|
# disk block sizes to evaluate
|
|
BLOCK_SIZES=( "4k" "64k" "512k" "1m" )
|
|
|
|
# execute disk performance test
|
|
disk_test "${BLOCK_SIZES[@]}"
|
|
fi
|
|
|
|
if [[ ! -z "$DD_FALLBACK" || ${#DISK_RESULTS[@]} -eq 0 ]]; then # fio download failed or test was killed or returned an error, run dd test instead
|
|
if [ -z "$DD_FALLBACK" ]; then # print error notice if ended up here due to fio error
|
|
echo -e "fio disk speed tests failed. Run manually to determine cause.\nRunning dd test as fallback..."
|
|
fi
|
|
|
|
dd_test
|
|
|
|
# format the speed averages by converting to GB/s if > 1000 MB/s
|
|
if [ $(echo $DISK_WRITE_TEST_AVG | cut -d "." -f 1) -ge 1000 ]; then
|
|
DISK_WRITE_TEST_AVG=$(awk -v a="$DISK_WRITE_TEST_AVG" 'BEGIN { print a / 1000 }')
|
|
DISK_WRITE_TEST_UNIT="GB/s"
|
|
else
|
|
DISK_WRITE_TEST_UNIT="MB/s"
|
|
fi
|
|
if [ $(echo $DISK_READ_TEST_AVG | cut -d "." -f 1) -ge 1000 ]; then
|
|
DISK_READ_TEST_AVG=$(awk -v a="$DISK_READ_TEST_AVG" 'BEGIN { print a / 1000 }')
|
|
DISK_READ_TEST_UNIT="GB/s"
|
|
else
|
|
DISK_READ_TEST_UNIT="MB/s"
|
|
fi
|
|
|
|
# print dd sequential disk speed test results
|
|
echo -e
|
|
echo -e "dd Sequential Disk Speed Tests:"
|
|
echo -e "---------------------------------"
|
|
printf "%-6s | %-6s %-4s | %-6s %-4s | %-6s %-4s | %-6s %-4s\n" "" "Test 1" "" "Test 2" "" "Test 3" "" "Avg" ""
|
|
printf "%-6s | %-6s %-4s | %-6s %-4s | %-6s %-4s | %-6s %-4s\n"
|
|
printf "%-6s | %-11s | %-11s | %-11s | %-6.2f %-4s\n" "Write" "${DISK_WRITE_TEST_RES[0]}" "${DISK_WRITE_TEST_RES[1]}" "${DISK_WRITE_TEST_RES[2]}" "${DISK_WRITE_TEST_AVG}" "${DISK_WRITE_TEST_UNIT}"
|
|
printf "%-6s | %-11s | %-11s | %-11s | %-6.2f %-4s\n" "Read" "${DISK_READ_TEST_RES[0]}" "${DISK_READ_TEST_RES[1]}" "${DISK_READ_TEST_RES[2]}" "${DISK_READ_TEST_AVG}" "${DISK_READ_TEST_UNIT}"
|
|
else # fio tests completed successfully, print results
|
|
CURRENT_PARTITION=$(df -P . 2>/dev/null | tail -1 | cut -d' ' -f 1)
|
|
[[ ! -z $JSON ]] && JSON_RESULT+=',"partition":"'$CURRENT_PARTITION'","fio":['
|
|
DISK_RESULTS_NUM=$(expr ${#DISK_RESULTS[@]} / 6)
|
|
DISK_COUNT=0
|
|
|
|
# print disk speed test results
|
|
echo -e "fio Disk Speed Tests (Mixed R/W 50/50) (Partition $CURRENT_PARTITION):"
|
|
echo -e "---------------------------------"
|
|
|
|
while [ $DISK_COUNT -lt $DISK_RESULTS_NUM ] ; do
|
|
if [ $DISK_COUNT -gt 0 ]; then printf "%-10s | %-20s | %-20s\n"; fi
|
|
printf "%-10s | %-11s %8s | %-11s %8s\n" "Block Size" "${BLOCK_SIZES[DISK_COUNT]}" "(IOPS)" "${BLOCK_SIZES[DISK_COUNT+1]}" "(IOPS)"
|
|
printf "%-10s | %-11s %8s | %-11s %8s\n" " ------" "---" "---- " "----" "---- "
|
|
printf "%-10s | %-11s %8s | %-11s %8s\n" "Read" "${DISK_RESULTS[DISK_COUNT*6+1]}" "(${DISK_RESULTS[DISK_COUNT*6+4]})" "${DISK_RESULTS[(DISK_COUNT+1)*6+1]}" "(${DISK_RESULTS[(DISK_COUNT+1)*6+4]})"
|
|
printf "%-10s | %-11s %8s | %-11s %8s\n" "Write" "${DISK_RESULTS[DISK_COUNT*6+2]}" "(${DISK_RESULTS[DISK_COUNT*6+5]})" "${DISK_RESULTS[(DISK_COUNT+1)*6+2]}" "(${DISK_RESULTS[(DISK_COUNT+1)*6+5]})"
|
|
printf "%-10s | %-11s %8s | %-11s %8s\n" "Total" "${DISK_RESULTS[DISK_COUNT*6]}" "(${DISK_RESULTS[DISK_COUNT*6+3]})" "${DISK_RESULTS[(DISK_COUNT+1)*6]}" "(${DISK_RESULTS[(DISK_COUNT+1)*6+3]})"
|
|
if [ ! -z $JSON ]; then
|
|
JSON_RESULT+='{"bs":"'${BLOCK_SIZES[DISK_COUNT]}'","speed_r":'${DISK_RESULTS_RAW[DISK_COUNT*6+1]}',"iops_r":'${DISK_RESULTS_RAW[DISK_COUNT*6+4]}
|
|
JSON_RESULT+=',"speed_w":'${DISK_RESULTS_RAW[DISK_COUNT*6+2]}',"iops_w":'${DISK_RESULTS_RAW[DISK_COUNT*6+5]}',"speed_rw":'${DISK_RESULTS_RAW[DISK_COUNT*6]}
|
|
JSON_RESULT+=',"iops_rw":'${DISK_RESULTS_RAW[DISK_COUNT*6+3]}',"speed_units":"KBps"},'
|
|
JSON_RESULT+='{"bs":"'${BLOCK_SIZES[DISK_COUNT+1]}'","speed_r":'${DISK_RESULTS_RAW[(DISK_COUNT+1)*6+1]}',"iops_r":'${DISK_RESULTS_RAW[(DISK_COUNT+1)*6+4]}
|
|
JSON_RESULT+=',"speed_w":'${DISK_RESULTS_RAW[(DISK_COUNT+1)*6+2]}',"iops_w":'${DISK_RESULTS_RAW[(DISK_COUNT+1)*6+5]}',"speed_rw":'${DISK_RESULTS_RAW[(DISK_COUNT+1)*6]}
|
|
JSON_RESULT+=',"iops_rw":'${DISK_RESULTS_RAW[(DISK_COUNT+1)*6+3]}',"speed_units":"KBps"},'
|
|
fi
|
|
DISK_COUNT=$(expr $DISK_COUNT + 2)
|
|
done
|
|
[[ ! -z $JSON ]] && JSON_RESULT=${JSON_RESULT::${#JSON_RESULT}-1} && JSON_RESULT+=']'
|
|
fi
|
|
fi
|
|
|
|
# iperf_test
|
|
# Purpose: This method is designed to test the network performance of the host by executing an
|
|
# iperf3 test to/from the public iperf server passed to the function. Both directions
|
|
# (send and receive) are tested.
|
|
# Parameters:
|
|
# 1. URL - URL/domain name of the iperf server
|
|
# 2. PORTS - the range of ports on which the iperf server operates
|
|
# 3. HOST - the friendly name of the iperf server host/owner
|
|
# 4. FLAGS - any flags that should be passed to the iperf command
|
|
function iperf_test {
|
|
URL=$1
|
|
PORTS=$2
|
|
HOST=$3
|
|
FLAGS=$4
|
|
|
|
# attempt the iperf send test 3 times, allowing for a slot to become available on the
|
|
# server or to throw out any bad/error results
|
|
I=1
|
|
while [ $I -le 3 ]
|
|
do
|
|
echo -en "Performing $MODE iperf3 send test to $HOST (Attempt #$I of 3)..."
|
|
# select a random iperf port from the range provided
|
|
PORT=$(shuf -i $PORTS -n 1)
|
|
# run the iperf test sending data from the host to the iperf server; includes
|
|
# a timeout of 15s in case the iperf server is not responding; uses 8 parallel
|
|
# threads for the network test
|
|
IPERF_RUN_SEND="$(timeout 15 $IPERF_CMD $FLAGS -c "$URL" -p $PORT -P 8 2> /dev/null)"
|
|
# check if iperf exited cleanly and did not return an error
|
|
if [[ "$IPERF_RUN_SEND" == *"receiver"* && "$IPERF_RUN_SEND" != *"error"* ]]; then
|
|
# test did not result in an error, parse speed result
|
|
SPEED=$(echo "${IPERF_RUN_SEND}" | grep SUM | grep receiver | awk '{ print $6 }')
|
|
# if speed result is blank or bad (0.00), rerun, otherwise set counter to exit loop
|
|
[[ -z $SPEED || "$SPEED" == "0.00" ]] && I=$(( $I + 1 )) || I=11
|
|
else
|
|
# if iperf server is not responding, set counter to exit, otherwise increment, sleep, and rerun
|
|
[[ "$IPERF_RUN_SEND" == *"unable to connect"* ]] && I=11 || I=$(( $I + 1 )) && sleep 2
|
|
fi
|
|
echo -en "\r\033[0K"
|
|
done
|
|
|
|
# small sleep necessary to give iperf server a breather to get ready for a new test
|
|
sleep 1
|
|
|
|
# attempt the iperf receive test 3 times, allowing for a slot to become available on
|
|
# the server or to throw out any bad/error results
|
|
J=1
|
|
while [ $J -le 3 ]
|
|
do
|
|
echo -n "Performing $MODE iperf3 recv test from $HOST (Attempt #$J of 3)..."
|
|
# select a random iperf port from the range provided
|
|
PORT=$(shuf -i $PORTS -n 1)
|
|
# run the iperf test receiving data from the iperf server to the host; includes
|
|
# a timeout of 15s in case the iperf server is not responding; uses 8 parallel
|
|
# threads for the network test
|
|
IPERF_RUN_RECV="$(timeout 15 $IPERF_CMD $FLAGS -c "$URL" -p $PORT -P 8 -R 2> /dev/null)"
|
|
# check if iperf exited cleanly and did not return an error
|
|
if [[ "$IPERF_RUN_RECV" == *"receiver"* && "$IPERF_RUN_RECV" != *"error"* ]]; then
|
|
# test did not result in an error, parse speed result
|
|
SPEED=$(echo "${IPERF_RUN_RECV}" | grep SUM | grep receiver | awk '{ print $6 }')
|
|
# if speed result is blank or bad (0.00), rerun, otherwise set counter to exit loop
|
|
[[ -z $SPEED || "$SPEED" == "0.00" ]] && J=$(( $J + 1 )) || J=11
|
|
else
|
|
# if iperf server is not responding, set counter to exit, otherwise increment, sleep, and rerun
|
|
[[ "$IPERF_RUN_RECV" == *"unable to connect"* ]] && J=11 || J=$(( $J + 1 )) && sleep 2
|
|
fi
|
|
echo -en "\r\033[0K"
|
|
done
|
|
|
|
# Run a latency test via ping -c1 command -> will return "xx.x ms"
|
|
[[ ! -z $LOCAL_PING ]] && LATENCY_RUN="$(ping -c1 $URL 2>/dev/null | grep -o 'time=.*' | sed s/'time='//)"
|
|
[[ -z $LATENCY_RUN ]] && LATENCY_RUN="--"
|
|
|
|
# parse the resulting send and receive speed results
|
|
IPERF_SENDRESULT="$(echo "${IPERF_RUN_SEND}" | grep SUM | grep receiver)"
|
|
IPERF_RECVRESULT="$(echo "${IPERF_RUN_RECV}" | grep SUM | grep receiver)"
|
|
LATENCY_RESULT="$(echo "${LATENCY_RUN}")"
|
|
}
|
|
|
|
# launch_iperf
|
|
# Purpose: This method is designed to facilitate the execution of iperf network speed tests to
|
|
# each public iperf server in the iperf server locations array.
|
|
# Parameters:
|
|
# 1. MODE - indicates the type of iperf tests to run (IPv4 or IPv6)
|
|
function launch_iperf {
|
|
MODE=$1
|
|
[[ "$MODE" == *"IPv6"* ]] && IPERF_FLAGS="-6" || IPERF_FLAGS="-4"
|
|
|
|
# print iperf3 network speed results as they are completed
|
|
echo -e
|
|
echo -e "iperf3 Network Speed Tests ($MODE):"
|
|
echo -e "---------------------------------"
|
|
printf "%-15s | %-25s | %-15s | %-15s | %-15s\n" "Provider" "Location (Link)" "Send Speed" "Recv Speed" "Ping"
|
|
printf "%-15s | %-25s | %-15s | %-15s | %-15s\n" "-----" "-----" "----" "----" "----"
|
|
|
|
# loop through iperf locations array to run iperf test using each public iperf server
|
|
for (( i = 0; i < IPERF_LOCS_NUM; i++ )); do
|
|
# test if the current iperf location supports the network mode being tested (IPv4/IPv6)
|
|
if [[ "${IPERF_LOCS[i*5+4]}" == *"$MODE"* ]]; then
|
|
# call the iperf_test function passing the required parameters
|
|
iperf_test "${IPERF_LOCS[i*5]}" "${IPERF_LOCS[i*5+1]}" "${IPERF_LOCS[i*5+2]}" "$IPERF_FLAGS"
|
|
# parse the send and receive speed results
|
|
IPERF_SENDRESULT_VAL=$(echo $IPERF_SENDRESULT | awk '{ print $6 }')
|
|
IPERF_SENDRESULT_UNIT=$(echo $IPERF_SENDRESULT | awk '{ print $7 }')
|
|
IPERF_RECVRESULT_VAL=$(echo $IPERF_RECVRESULT | awk '{ print $6 }')
|
|
IPERF_RECVRESULT_UNIT=$(echo $IPERF_RECVRESULT | awk '{ print $7 }')
|
|
LATENCY_VAL=$(echo $LATENCY_RESULT)
|
|
# if the results are blank, then the server is "busy" and being overutilized
|
|
[[ -z $IPERF_SENDRESULT_VAL || "$IPERF_SENDRESULT_VAL" == *"0.00"* ]] && IPERF_SENDRESULT_VAL="busy" && IPERF_SENDRESULT_UNIT=""
|
|
[[ -z $IPERF_RECVRESULT_VAL || "$IPERF_RECVRESULT_VAL" == *"0.00"* ]] && IPERF_RECVRESULT_VAL="busy" && IPERF_RECVRESULT_UNIT=""
|
|
# print the speed results for the iperf location currently being evaluated
|
|
printf "%-15s | %-25s | %-15s | %-15s | %-15s\n" "${IPERF_LOCS[i*5+2]}" "${IPERF_LOCS[i*5+3]}" "$IPERF_SENDRESULT_VAL $IPERF_SENDRESULT_UNIT" "$IPERF_RECVRESULT_VAL $IPERF_RECVRESULT_UNIT" "$LATENCY_VAL"
|
|
if [ ! -z $JSON ]; then
|
|
JSON_RESULT+='{"mode":"'$MODE'","provider":"'${IPERF_LOCS[i*5+2]}'","loc":"'${IPERF_LOCS[i*5+3]}
|
|
JSON_RESULT+='","send":"'$IPERF_SENDRESULT_VAL' '$IPERF_SENDRESULT_UNIT'","recv":"'$IPERF_RECVRESULT_VAL' '$IPERF_RECVRESULT_UNIT'","latency":"'$LATENCY_VAL'"},'
|
|
fi
|
|
fi
|
|
done
|
|
}
|
|
|
|
# if the skip iperf flag was set, skip the network performance test, otherwise test network performance
|
|
if [ -z "$SKIP_IPERF" ]; then
|
|
|
|
if [[ -z "$PREFER_BIN" && ! -z "$LOCAL_IPERF" ]]; then # local iperf has been detected, use instead of pre-compiled binary
|
|
IPERF_CMD=iperf3
|
|
else
|
|
# create a temp directory to house the required iperf binary and library
|
|
IPERF_PATH=$YABS_PATH/iperf
|
|
mkdir -p "$IPERF_PATH"
|
|
|
|
# download iperf3 binary
|
|
if [[ ! -z $LOCAL_CURL ]]; then
|
|
curl -s --connect-timeout 5 --retry 5 --retry-delay 0 https://raw.githubusercontent.com/masonr/yet-another-bench-script/master/bin/iperf/iperf3_$ARCH -o "$IPERF_PATH/iperf3"
|
|
else
|
|
wget -q -T 5 -t 5 -w 0 https://raw.githubusercontent.com/masonr/yet-another-bench-script/master/bin/iperf/iperf3_$ARCH -O "$IPERF_PATH/iperf3"
|
|
fi
|
|
|
|
if [ ! -f "$IPERF_PATH/iperf3" ]; then # ensure iperf3 binary downloaded successfully
|
|
IPERF_DL_FAIL=True
|
|
else
|
|
chmod +x "$IPERF_PATH/iperf3"
|
|
IPERF_CMD=$IPERF_PATH/iperf3
|
|
fi
|
|
fi
|
|
|
|
# array containing all currently available iperf3 public servers to use for the network test
|
|
# format: "1" "2" "3" "4" "5" \
|
|
# 1. domain name of the iperf server
|
|
# 2. range of ports that the iperf server is running on (lowest-highest)
|
|
# 3. friendly name of the host/owner of the iperf server
|
|
# 4. location and advertised speed link of the iperf server
|
|
# 5. network modes supported by the iperf server (IPv4 = IPv4-only, IPv4|IPv6 = IPv4 + IPv6, etc.)
|
|
IPERF_LOCS=( \
|
|
"lon.speedtest.clouvider.net" "5200-5209" "Clouvider" "London, UK (10G)" "IPv4|IPv6" \
|
|
"iperf-ams-nl.eranium.net" "5201-5210" "Eranium" "Amsterdam, NL (10G)" "IPv4|IPv6" \
|
|
"speedtest.extra.telia.fi" "5201-5208" "Telia" "Helsinki, FI (10G)" "IPv4" \
|
|
# AFR placeholder
|
|
"speedtest.uztelecom.uz" "5200-5209" "Uztelecom" "Tashkent, UZ (10G)" "IPv4|IPv6" \
|
|
"speedtest.sin1.sg.leaseweb.net" "5201-5210" "Leaseweb" "Singapore, SG (10G)" "IPv4|IPv6" \
|
|
"la.speedtest.clouvider.net" "5200-5209" "Clouvider" "Los Angeles, CA, US (10G)" "IPv4|IPv6" \
|
|
"speedtest.nyc1.us.leaseweb.net" "5201-5210" "Leaseweb" "NYC, NY, US (10G)" "IPv4|IPv6" \
|
|
"speedtest.sao1.edgoo.net" "9204-9240" "Edgoo" "Sao Paulo, BR (1G)" "IPv4|IPv6"
|
|
)
|
|
|
|
# if the "REDUCE_NET" flag is activated, then do a shorter iperf test with only three locations
|
|
# (Clouvider London, Clouvider NYC, and Online.net France)
|
|
if [ ! -z "$REDUCE_NET" ]; then
|
|
IPERF_LOCS=( \
|
|
"lon.speedtest.clouvider.net" "5200-5209" "Clouvider" "London, UK (10G)" "IPv4|IPv6" \
|
|
"speedtest.sin1.sg.leaseweb.net" "5201-5210" "Leaseweb" "Singapore, SG (10G)" "IPv4|IPv6" \
|
|
"speedtest.nyc1.us.leaseweb.net" "5201-5210" "Leaseweb" "NYC, NY, US (10G)" "IPv4|IPv6" \
|
|
)
|
|
fi
|
|
|
|
# get the total number of iperf locations (total array size divided by 5 since each location has 5 elements)
|
|
IPERF_LOCS_NUM=${#IPERF_LOCS[@]}
|
|
IPERF_LOCS_NUM=$((IPERF_LOCS_NUM / 5))
|
|
|
|
if [ -z "$IPERF_DL_FAIL" ]; then
|
|
[[ ! -z $JSON ]] && JSON_RESULT+=',"iperf":['
|
|
# check if the host has IPv4 connectivity, if so, run iperf3 IPv4 tests
|
|
[ ! -z "$IPV4_CHECK" ] && launch_iperf "IPv4"
|
|
# check if the host has IPv6 connectivity, if so, run iperf3 IPv6 tests
|
|
[ ! -z "$IPV6_CHECK" ] && launch_iperf "IPv6"
|
|
[[ ! -z $JSON ]] && JSON_RESULT=${JSON_RESULT::${#JSON_RESULT}-1} && JSON_RESULT+=']'
|
|
else
|
|
echo -e "\niperf3 binary download failed. Skipping iperf network tests..."
|
|
fi
|
|
fi
|
|
|
|
# launch_geekbench
|
|
# Purpose: This method is designed to run the Primate Labs' Geekbench 4/5 Cross-Platform Benchmark utility
|
|
# Parameters:
|
|
# 1. VERSION - indicates which Geekbench version to run
|
|
function launch_geekbench {
|
|
VERSION=$1
|
|
|
|
# create a temp directory to house all geekbench files
|
|
GEEKBENCH_PATH=$YABS_PATH/geekbench_$VERSION
|
|
mkdir -p "$GEEKBENCH_PATH"
|
|
|
|
GB_URL=""
|
|
GB_CMD=""
|
|
GB_RUN=""
|
|
|
|
# check for curl vs wget
|
|
[[ ! -z $LOCAL_CURL ]] && DL_CMD="curl -s" || DL_CMD="wget -qO-"
|
|
|
|
if [[ $VERSION == *4* && ($ARCH = *aarch64* || $ARCH = *arm*) ]]; then
|
|
echo -e "\nARM architecture not supported by Geekbench 4, use Geekbench 5 or 6."
|
|
elif [[ $VERSION == *4* && $ARCH != *aarch64* && $ARCH != *arm* ]]; then # Geekbench v4
|
|
GB_URL="https://cdn.geekbench.com/Geekbench-4.4.4-Linux.tar.gz"
|
|
[[ "$ARCH" == *"x86"* ]] && GB_CMD="geekbench_x86_32" || GB_CMD="geekbench4"
|
|
GB_RUN="True"
|
|
elif [[ $VERSION == *5* || $VERSION == *6* ]]; then # Geekbench v5/6
|
|
if [[ $ARCH = *x86* && $GEEKBENCH_4 == *False* ]]; then # don't run Geekbench 5 if on 32-bit arch
|
|
echo -e "\nGeekbench $VERSION cannot run on 32-bit architectures. Re-run with -4 flag to use"
|
|
echo -e "Geekbench 4, which can support 32-bit architectures. Skipping Geekbench $VERSION."
|
|
elif [[ $ARCH = *x86* && $GEEKBENCH_4 == *True* ]]; then
|
|
echo -e "\nGeekbench $VERSION cannot run on 32-bit architectures. Skipping test."
|
|
else
|
|
if [[ $VERSION == *5* ]]; then # Geekbench v5
|
|
[[ $ARCH = *aarch64* || $ARCH = *arm* ]] && GB_URL="https://cdn.geekbench.com/Geekbench-5.5.1-LinuxARMPreview.tar.gz" \
|
|
|| GB_URL="https://cdn.geekbench.com/Geekbench-5.5.1-Linux.tar.gz"
|
|
GB_CMD="geekbench5"
|
|
else # Geekbench v6
|
|
[[ $ARCH = *aarch64* || $ARCH = *arm* ]] && GB_URL="https://cdn.geekbench.com/Geekbench-6.2.2-LinuxARMPreview.tar.gz" \
|
|
|| GB_URL="https://cdn.geekbench.com/Geekbench-6.2.2-Linux.tar.gz"
|
|
GB_CMD="geekbench6"
|
|
fi
|
|
GB_RUN="True"
|
|
fi
|
|
fi
|
|
|
|
if [[ $GB_RUN == *True* ]]; then # run GB test
|
|
echo -en "\nRunning GB$VERSION benchmark test... *cue elevator music*"
|
|
|
|
# check for local geekbench installed
|
|
if command -v "$GB_CMD" &>/dev/null; then
|
|
GEEKBENCH_PATH=$(dirname "$(command -v "$GB_CMD")")
|
|
else
|
|
# download the desired Geekbench tarball and extract to geekbench temp directory
|
|
$DL_CMD $GB_URL | tar xz --strip-components=1 -C "$GEEKBENCH_PATH" &>/dev/null
|
|
fi
|
|
|
|
# unlock if license file detected
|
|
test -f "geekbench.license" && "$GEEKBENCH_PATH/$GB_CMD" --unlock $(cat geekbench.license) > /dev/null 2>&1
|
|
|
|
# run the Geekbench test and grep the test results URL given at the end of the test
|
|
GEEKBENCH_TEST=$("$GEEKBENCH_PATH/$GB_CMD" --upload 2>/dev/null | grep "https://browser")
|
|
|
|
# ensure the test ran successfully
|
|
if [ -z "$GEEKBENCH_TEST" ]; then
|
|
# detect if CentOS 7 and print a more helpful error message
|
|
if grep -q "CentOS Linux 7" /etc/os-release; then
|
|
echo -e "\r\033[0K CentOS 7 and Geekbench have known issues relating to glibc (see issue #71 for details)"
|
|
fi
|
|
if [[ -z "$IPV4_CHECK" ]]; then
|
|
# Geekbench test failed to download because host lacks IPv4 (cdn.geekbench.com = IPv4 only)
|
|
echo -e "\r\033[0KGeekbench releases can only be downloaded over IPv4. FTP the Geekbench files and run manually."
|
|
elif [[ $VERSION != *4* && $TOTAL_RAM_RAW -le 1048576 ]]; then
|
|
# Geekbench 5/6 test failed with low memory (<=1GB)
|
|
echo -e "\r\033[0KGeekbench test failed and low memory was detected. Add at least 1GB of SWAP or use GB4 instead (higher compatibility with low memory systems)."
|
|
elif [[ $ARCH != *x86* ]]; then
|
|
# if the Geekbench test failed for any other reason, exit cleanly and print error message
|
|
echo -e "\r\033[0KGeekbench $VERSION test failed. Run manually to determine cause."
|
|
fi
|
|
else
|
|
# if the Geekbench test succeeded, parse the test results URL
|
|
GEEKBENCH_URL=$(echo -e $GEEKBENCH_TEST | head -1)
|
|
GEEKBENCH_URL_CLAIM=$(echo $GEEKBENCH_URL | awk '{ print $2 }')
|
|
GEEKBENCH_URL=$(echo $GEEKBENCH_URL | awk '{ print $1 }')
|
|
# sleep a bit to wait for results to be made available on the geekbench website
|
|
sleep 10
|
|
# parse the public results page for the single and multi core geekbench scores
|
|
[[ $VERSION == *4* ]] && GEEKBENCH_SCORES=$($DL_CMD $GEEKBENCH_URL | grep "span class='score'") || \
|
|
GEEKBENCH_SCORES=$($DL_CMD $GEEKBENCH_URL | grep "div class='score'")
|
|
|
|
GEEKBENCH_SCORES_SINGLE=$(echo $GEEKBENCH_SCORES | awk -v FS="(>|<)" '{ print $3 }')
|
|
GEEKBENCH_SCORES_MULTI=$(echo $GEEKBENCH_SCORES | awk -v FS="(>|<)" '{ print $7 }')
|
|
|
|
# print the Geekbench results
|
|
echo -en "\r\033[0K"
|
|
echo -e "Geekbench $VERSION Benchmark Test:"
|
|
echo -e "---------------------------------"
|
|
printf "%-15s | %-30s\n" "Test" "Value"
|
|
printf "%-15s | %-30s\n"
|
|
printf "%-15s | %-30s\n" "Single Core" "$GEEKBENCH_SCORES_SINGLE"
|
|
printf "%-15s | %-30s\n" "Multi Core" "$GEEKBENCH_SCORES_MULTI"
|
|
printf "%-15s | %-30s\n" "Full Test" "$GEEKBENCH_URL"
|
|
|
|
if [ ! -z $JSON ]; then
|
|
JSON_RESULT+='{"version":'$VERSION',"single":'$GEEKBENCH_SCORES_SINGLE',"multi":'$GEEKBENCH_SCORES_MULTI
|
|
JSON_RESULT+=',"url":"'$GEEKBENCH_URL'"},'
|
|
fi
|
|
|
|
# write the geekbench claim URL to a file so the user can add the results to their profile (if desired)
|
|
[ ! -z "$GEEKBENCH_URL_CLAIM" ] && echo -e "$GEEKBENCH_URL_CLAIM" >> geekbench_claim.url 2> /dev/null
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# if the skip geekbench flag was set, skip the system performance test, otherwise test system performance
|
|
if [ -z "$SKIP_GEEKBENCH" ]; then
|
|
[[ ! -z $JSON ]] && JSON_RESULT+=',"geekbench":['
|
|
if [[ $GEEKBENCH_4 == *True* ]]; then
|
|
launch_geekbench 4
|
|
fi
|
|
|
|
if [[ $GEEKBENCH_5 == *True* ]]; then
|
|
launch_geekbench 5
|
|
fi
|
|
|
|
if [[ $GEEKBENCH_6 == *True* ]]; then
|
|
launch_geekbench 6
|
|
fi
|
|
[[ ! -z $JSON ]] && [[ $(echo -n $JSON_RESULT | tail -c 1) == ',' ]] && JSON_RESULT=${JSON_RESULT::${#JSON_RESULT}-1}
|
|
[[ ! -z $JSON ]] && JSON_RESULT+=']'
|
|
fi
|
|
|
|
# finished all tests, clean up all YABS files and exit
|
|
echo -e
|
|
rm -rf "$YABS_PATH"
|
|
|
|
YABS_END_TIME=$(date +%s)
|
|
|
|
# calculate_time_taken
|
|
# Purpose: This method is designed to find the time taken for the completion of a YABS run.
|
|
# Parameters:
|
|
# 1. YABS_END_TIME - time when GB has completed and all files are removed
|
|
# 2. YABS_START_TIME - time when YABS is started
|
|
function calculate_time_taken() {
|
|
end_time=$1
|
|
start_time=$2
|
|
|
|
time_taken=$(( ${end_time} - ${start_time} ))
|
|
if [ ${time_taken} -gt 60 ]; then
|
|
min=$(expr $time_taken / 60)
|
|
sec=$(expr $time_taken % 60)
|
|
echo "YABS completed in ${min} min ${sec} sec"
|
|
else
|
|
echo "YABS completed in ${time_taken} sec"
|
|
fi
|
|
[[ ! -z $JSON ]] && JSON_RESULT+=',"runtime":{"start":'$start_time',"end":'$end_time',"elapsed":'$time_taken'}'
|
|
}
|
|
|
|
calculate_time_taken $YABS_END_TIME $YABS_START_TIME
|
|
|
|
if [[ ! -z $JSON ]]; then
|
|
JSON_RESULT+='}'
|
|
|
|
# write json results to file
|
|
if [[ $JSON = *w* ]]; then
|
|
echo $JSON_RESULT > "$JSON_FILE"
|
|
fi
|
|
|
|
# send json results
|
|
if [[ $JSON = *s* ]]; then
|
|
IFS=',' read -r -a JSON_SITES <<< "$JSON_SEND"
|
|
for JSON_SITE in "${JSON_SITES[@]}"
|
|
do
|
|
if [[ ! -z $LOCAL_CURL ]]; then
|
|
curl -s -H "Content-Type:application/json" -X POST --data ''"$JSON_RESULT"'' $JSON_SITE
|
|
else
|
|
wget -qO- --post-data=''"$JSON_RESULT"'' --header='Content-Type:application/json' $JSON_SITE
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# print json result to screen
|
|
if [[ $JSON = *j* ]]; then
|
|
echo -e
|
|
echo $JSON_RESULT
|
|
fi
|
|
fi
|
|
|
|
# reset locale settings
|
|
unset LC_ALL
|