Init: project - initial commit.
This commit is contained in:
commit
593cb41fe4
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Castaway Labs LLC
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
48
README-CN.md
Normal file
48
README-CN.md
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# SSH基准测试工具
|
||||||
|
|
||||||
|
> 这是一款支持离线和批处理执行的服务器基准测试工具。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 说明
|
||||||
|
|
||||||
|
当前工具的实现流程描述:
|
||||||
|
|
||||||
|
1. 检查主机配置是否正确。
|
||||||
|
2. 检查主机是否可以连通(连通性测试)。
|
||||||
|
3. 启动Iperf3服务器端。
|
||||||
|
4. 将压力测试脚本上传到目标服务器并根据主机列表批量执行脚本(串行执行)。
|
||||||
|
5. 实际并显示结果。
|
||||||
|
6. 清理脚本数据(完成)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 工具打包和编译。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go mod tidy
|
||||||
|
go build -o ssh-benchmark main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
如果您已安装了 [task](https://taskfile.dev/),也可以使用它。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
task build:binary
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
1. 修改 `config.json` 中的配置
|
||||||
|
|
||||||
|
> 1. 如果您没有Linux版Geekbench 5的许可证,可以通过电子邮件联系我(<yangzun@treesir.pub>)。我愿意与您分享。
|
||||||
|
> 2. 如果您希望对多个节点同时进行测试,可以在 `Host` 字段中填入逗号分隔的列表。
|
||||||
|
|
||||||
|
2. 执行此项目预编译的 ssh-benchmark 文件
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./ssh-benchmark
|
||||||
|
```
|
||||||
|
|
||||||
|

|
51
README.md
Normal file
51
README.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
|
||||||
|
|
||||||
|
# SSH Benchmark
|
||||||
|
|
||||||
|
> A server benchmarking tool that supports offline and batch execution.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Explanation
|
||||||
|
|
||||||
|
Current tool implementation process description:
|
||||||
|
|
||||||
|
1. Check if the host configuration is correct.
|
||||||
|
2. Check if the host is connectable (connectivity test).
|
||||||
|
3. Launch Iperf3 server-side.
|
||||||
|
4. Stress test script uploaded to target server and execute the script in batches according to the host list (executed serially).
|
||||||
|
5. Actual and display of results.
|
||||||
|
6. Clean up script data (complete).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tool packaging and compilation.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go mod tidy
|
||||||
|
go build -o ssh-benchmark main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
If you have installed the [task](https://taskfile.dev/), you can also use it.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
task build:binary
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Instructions for use
|
||||||
|
|
||||||
|
1. Modify the configuration in `config.json`
|
||||||
|
|
||||||
|
> 1. If you don't have a license for Geekbench 5 for Linux, you can contact me by email (<yangzun@treesir.pub>). I'd be willing to share it with you.
|
||||||
|
> 2. If you want to perform simultaneous testing on multiple nodes, fill in the `Host` field with a comma-separated list.
|
||||||
|
|
||||||
|
|
||||||
|
2. Execute the pre-compiled ssh-benchmark file of this project
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./ssh-benchmark
|
||||||
|
```
|
||||||
|
|
||||||
|

|
48
Taskfile.yaml
Normal file
48
Taskfile.yaml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
vars:
|
||||||
|
BASE_DIR:
|
||||||
|
sh: dirname $(pwd)
|
||||||
|
PROJECT_NAME:
|
||||||
|
sh: pwd|sed "s#{{.BASE_DIR}}/##g"
|
||||||
|
GOOS: ''
|
||||||
|
GOARCH: ''
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
cmds:
|
||||||
|
- task: deps
|
||||||
|
- task: build:binary
|
||||||
|
silent: true
|
||||||
|
|
||||||
|
deps:
|
||||||
|
desc: Install all dependencies (except dredd requirements)
|
||||||
|
cmds:
|
||||||
|
- task: deps:be
|
||||||
|
- task: deps:tools
|
||||||
|
|
||||||
|
deps:be:
|
||||||
|
desc: application dependencies
|
||||||
|
cmds:
|
||||||
|
- go mod tidy
|
||||||
|
- zip -r benchmark.zip benchmark/
|
||||||
|
|
||||||
|
deps:tools:
|
||||||
|
desc: Installs tools needed for building
|
||||||
|
vars:
|
||||||
|
GORELEASER_VERSION: "1.19.2"
|
||||||
|
cmds:
|
||||||
|
- '{{ if ne OS "windows" }} sh -c "curl -L https://github.com/goreleaser/goreleaser/releases/download/v{{ .GORELEASER_VERSION }}/goreleaser_$(uname -s)_$(uname -m).tar.gz | tar -xz -C $(go env GOPATH)/bin goreleaser"{{ else }} {{ end }}'
|
||||||
|
- '{{ if ne OS "windows" }} chmod +x $(go env GOPATH)/bin/goreleaser{{ else }} {{ end }}'
|
||||||
|
- '{{ if eq OS "windows" }} echo "NOTICE: You must download goreleaser manually to build this application https://github.com/goreleaser/goreleaser/releases "{{ else }}:{{ end }}'
|
||||||
|
- '{{ if eq OS "windows" }} echo "NOTICE: You need to install golangci-lint manually to build this application https://github.com/golangci/golangci-lint#install"{{ else }}{{ end }}'
|
||||||
|
|
||||||
|
build:binary:
|
||||||
|
desc: Build a binary for the current architecture
|
||||||
|
platforms: [amd64]
|
||||||
|
cmds:
|
||||||
|
- env CGO_ENABLED=0 GOOS={{ .GOOS }} GOARCH={{ .GOARCH }} go build -o ./bin/{{.PROJECT_NAME}}{{ if eq OS "windows" }}.exe{{ end }}
|
||||||
|
|
||||||
|
release:prod:
|
||||||
|
cmds:
|
||||||
|
- goreleaser
|
901
benchmark/run.sh
Executable file
901
benchmark/run.sh
Executable file
@ -0,0 +1,901 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
export PATH=$PATH:$(pwd)/tools
|
||||||
|
MAIN_VERSION="V2022-11-24"
|
||||||
|
BENCHMARK_HOST="${1}"
|
||||||
|
GEEKBENCH_LICENSE="${2}"
|
||||||
|
|
||||||
|
echo -e '# ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## #'
|
||||||
|
echo -e '# RUN-BenchMark-Script #'
|
||||||
|
echo -e '# '$MAIN_VERSION' #'
|
||||||
|
echo -e '# https://gitlab-ee.treesir.pub/devops/benchmark #'
|
||||||
|
echo -e '# ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## #'
|
||||||
|
|
||||||
|
echo -e
|
||||||
|
date
|
||||||
|
TIME_START=$(date '+%Y%m%d-%H%M%S')
|
||||||
|
|
||||||
|
# override locale to eliminate parsing errors (i.e. using commas as delimiters rather than periods)
|
||||||
|
if locale -a | 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 YYDS."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# flags to skip certain performance tests
|
||||||
|
unset PREFER_BIN SKIP_FIO SKIP_IPERF SKIP_GEEKBENCH PRINT_HELP REDUCE_NET GEEKBENCH_4 GEEKBENCH_5 DD_FALLBACK IPERF_DL_FAIL JSON JSON_SEND JSON_RESULT JSON_FILE
|
||||||
|
GEEKBENCH_5="True" # gb5 test enabled by default
|
||||||
|
IPV4_CHECK="true" # iperf3 test enable by default
|
||||||
|
|
||||||
|
# get any arguments that were passed to the script and set the associated skip flags (if applicable)
|
||||||
|
while getopts 'bfdighr49jw: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" ;;
|
||||||
|
h) PRINT_HELP="True" ;;
|
||||||
|
r) REDUCE_NET="True" ;;
|
||||||
|
# 4) GEEKBENCH_4="True" && unset GEEKBENCH_5 ;;
|
||||||
|
# 9) GEEKBENCH_4="True" && GEEKBENCH_5="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 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: ./YYDS.sh [-flags]"
|
||||||
|
echo -e " curl -sL YYDS.sh | bash"
|
||||||
|
echo -e " curl -sL YYDS.sh | bash -s -- -flags"
|
||||||
|
echo -e " wget -qO- YYDS.sh | bash"
|
||||||
|
echo -e " wget -qO- YYDS.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 " -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 5"
|
||||||
|
echo -e " -9 : use both geekbench 4 AND geekbench 5"
|
||||||
|
echo -e " -j : print jsonified YYDS results at conclusion of test"
|
||||||
|
echo -e " -w <filename> : write jsonified YYDS results to disk using file name provided"
|
||||||
|
echo -e " -s <url> : send jsonified YYDS 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 $REDUCE_NET ]] && echo -e " -r, using reduced (3) iperf3 locations"
|
||||||
|
[[ ! -z $GEEKBENCH_5 ]] && echo -e " running geekbench 5"
|
||||||
|
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 YYDS 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"
|
||||||
|
if [[ $ARCH = *aarch64* || $ARCH = *arm* ]]; 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* ]]; 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"
|
||||||
|
|
||||||
|
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")
|
||||||
|
VIRT=$([[ "$CPU_VIRT" = *Enabled* ]] && echo "true" || echo "false")
|
||||||
|
JSON_RESULT='{"version":"'$MAIN_VERSION'","time":"'$TIME_START'","os":{"arch":"'$ARCH'","distro":"'$DISTRO'","kernel":"'$KERNEL'",'
|
||||||
|
JSON_RESULT+='"uptime":'$UPTIME_S'},"net":{"ipv4":'$IPV4',"ipv6":'$IPV6'},"cpu":{"model":"'$CPU_PROC'","cores":'$CPU_CORES','
|
||||||
|
JSON_RESULT+='"freq":"'$CPU_FREQ'","aes":'$AES',"virt":'$VIRT'},"mem":{"ram":'$TOTAL_RAM_RAW',"swap":'$TOTAL_SWAP_RAW',"disk":'$TOTAL_DISK_RAW'}'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# create a directory in the same location that the script is being run to temporarily store RUN-related files
|
||||||
|
DATE=`date -Iseconds | sed -e "s/:/_/g"`
|
||||||
|
YYDS_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 $YYDS_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
|
||||||
|
# RUN-related files.
|
||||||
|
function catch_abort() {
|
||||||
|
echo -e "\n** Aborting YYDS. Cleaning up files...\n"
|
||||||
|
rm -rf $YYDS_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=$(bc <<< "$free_space*1024")
|
||||||
|
size_b='G'
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $(df -Th | grep -w $long) == *"zfs"* ]];then
|
||||||
|
|
||||||
|
if [[ $size_b == 'G' ]]; then
|
||||||
|
if [[ $(echo "$free_space < $mul_spa" | bc) -ne 0 ]];then
|
||||||
|
warning=1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warning=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $warning -eq 1 ]];then
|
||||||
|
echo -en "\nWarning! You are running YYDS 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=$YYDS_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
|
||||||
|
[[ ! -z $JSON ]] && JSON_RESULT+=',"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):"
|
||||||
|
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]}'},'
|
||||||
|
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]}'},'
|
||||||
|
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
|
||||||
|
|
||||||
|
# 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)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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\n" "Provider" "Location (Link)" "Send Speed" "Recv Speed"
|
||||||
|
printf "%-15s | %-25s | %-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 }')
|
||||||
|
# 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\n" "${IPERF_LOCS[i*5+2]}" "${IPERF_LOCS[i*5+3]}" "$IPERF_SENDRESULT_VAL $IPERF_SENDRESULT_UNIT" "$IPERF_RECVRESULT_VAL $IPERF_RECVRESULT_UNIT"
|
||||||
|
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'"},'
|
||||||
|
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=$YYDS_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=( \
|
||||||
|
"${BENCHMARK_HOST}" "5201-5201" "Local" "Offline (Local)" "IPv4"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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" \
|
||||||
|
# "ping.online.net" "5200-5209" "Online.net" "Paris, FR (10G)" "IPv4" \
|
||||||
|
# "ping6.online.net" "5200-5209" "Online.net" "Paris, FR (10G)" "IPv6" \
|
||||||
|
# "nyc.speedtest.clouvider.net" "5200-5209" "Clouvider" "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=tools/geekbench_$VERSION
|
||||||
|
mkdir -p $GEEKBENCH_PATH
|
||||||
|
|
||||||
|
# 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."
|
||||||
|
elif [[ $VERSION == *4* && $ARCH != *aarch64* && $ARCH != *arm* ]]; then # Geekbench v4
|
||||||
|
echo -en "\nRunning GB4 benchmark test... *cue elevator music*"
|
||||||
|
# download the latest Geekbench 4 tarball and extract to geekbench temp directory
|
||||||
|
# $DL_CMD https://cdn.geekbench.com/Geekbench-4.4.4-Linux.tar.gz | tar xz --strip-components=1 -C $GEEKBENCH_PATH &>/dev/null
|
||||||
|
|
||||||
|
if [[ "$ARCH" == *"x86"* ]]; then
|
||||||
|
# check if geekbench file exists
|
||||||
|
# if test -f "geekbench.license"; then
|
||||||
|
# $GEEKBENCH_PATH/geekbench_x86_32 --unlock `cat geekbench.license` > /dev/null 2>&1
|
||||||
|
# fi
|
||||||
|
$GEEKBENCH_PATH/geekbench_x86_32 --unlock ${GEEKBENCH_LICENSE} > /dev/null 2>&1
|
||||||
|
|
||||||
|
# run the Geekbench 4 test and grep the test results URL given at the end of the test
|
||||||
|
GEEKBENCH_TEST=$($GEEKBENCH_PATH/geekbench_x86_32 --no-upload 2>/dev/null | grep "https://browser")
|
||||||
|
else
|
||||||
|
# check if geekbench file exists
|
||||||
|
# if test -f "geekbench.license"; then
|
||||||
|
# $GEEKBENCH_PATH/geekbench4 --unlock `cat geekbench.license` > /dev/null 2>&1
|
||||||
|
# fi
|
||||||
|
$GEEKBENCH_PATH/geekbench4 --unlock ${GEEKBENCH_LICENSE} > /dev/null 2>&1
|
||||||
|
|
||||||
|
# run the Geekbench 4 test and grep the test results URL given at the end of the test
|
||||||
|
GEEKBENCH_TEST=$($GEEKBENCH_PATH/geekbench4 --upload 2>/dev/null | grep "https://browser")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $VERSION == *5* ]]; then # Geekbench v5
|
||||||
|
if [[ $ARCH = *x86* && $GEEKBENCH_4 == *False* ]]; then # don't run Geekbench 5 if on 32-bit arch
|
||||||
|
echo -e "\nGeekbench 5 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 5."
|
||||||
|
elif [[ $ARCH = *x86* && $GEEKBENCH_4 == *True* ]]; then
|
||||||
|
echo -e "\nGeekbench 5 cannot run on 32-bit architectures. Skipping test."
|
||||||
|
else
|
||||||
|
echo -en "\nRunning GB5 benchmark test... *cue elevator music*"
|
||||||
|
# download the latest Geekbench 5 tarball and extract to geekbench temp directory
|
||||||
|
# if [[ $ARCH = *aarch64* || $ARCH = *arm* ]]; then
|
||||||
|
# $DL_CMD https://cdn.geekbench.com/Geekbench-5.4.4-LinuxARMPreview.tar.gz | tar xz --strip-components=1 -C $GEEKBENCH_PATH &>/dev/null
|
||||||
|
# else
|
||||||
|
# $DL_CMD https://cdn.geekbench.com/Geekbench-5.4.5-Linux.tar.gz | tar xz --strip-components=1 -C $GEEKBENCH_PATH &>/dev/null
|
||||||
|
# fi
|
||||||
|
|
||||||
|
# check if geekbench file exists
|
||||||
|
# if test -f "geekbench.license"; then
|
||||||
|
# $GEEKBENCH_PATH/geekbench5 --unlock `cat geekbench.license` > /dev/null 2>&1
|
||||||
|
# fi
|
||||||
|
$GEEKBENCH_PATH/geekbench5 --unlock ${GEEKBENCH_LICENSE} > /dev/null 2>&1
|
||||||
|
|
||||||
|
GEEKBENCH_TEST=$($GEEKBENCH_PATH/geekbench5 --no-upload 2>/dev/null)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ensure the test ran successfully
|
||||||
|
if [ -z "$GEEKBENCH_TEST" ]; then
|
||||||
|
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 [[ $ARCH != *x86* ]]; then
|
||||||
|
# if the Geekbench test failed for any 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 20
|
||||||
|
# parse the public results page for the single and multi core geekbench scores
|
||||||
|
# GEEKBENCH_SCORES_SINGLE=$(echo $GEEKBENCH_SCORES | awk -v FS="(>|<)" '{ print $3 }')
|
||||||
|
GEEKBENCH_SCORES_SINGLE=$(echo $GEEKBENCH_TEST|egrep -o "Single-Core Score [0-9]+"|awk '{print $3}')
|
||||||
|
GEEKBENCH_SCORES_MULTI=$(echo $GEEKBENCH_TEST|egrep -o "Multi-Core Score [0-9]+"|awk '{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
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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
|
||||||
|
[[ ! -z $JSON ]] && JSON_RESULT=${JSON_RESULT::${#JSON_RESULT}-1} && JSON_RESULT+=']'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# finished all tests, clean up all YYDS files and exit
|
||||||
|
echo -e
|
||||||
|
rm -rf $YYDS_PATH
|
||||||
|
|
||||||
|
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
|
BIN
benchmark/tools/fio
Executable file
BIN
benchmark/tools/fio
Executable file
Binary file not shown.
BIN
benchmark/tools/geekbench_5/geekbench.plar
Normal file
BIN
benchmark/tools/geekbench_5/geekbench.plar
Normal file
Binary file not shown.
BIN
benchmark/tools/geekbench_5/geekbench5
Executable file
BIN
benchmark/tools/geekbench_5/geekbench5
Executable file
Binary file not shown.
BIN
benchmark/tools/geekbench_5/geekbench_x86_64
Executable file
BIN
benchmark/tools/geekbench_5/geekbench_x86_64
Executable file
Binary file not shown.
BIN
benchmark/tools/iperf3
Executable file
BIN
benchmark/tools/iperf3
Executable file
Binary file not shown.
7
config.json.template
Normal file
7
config.json.template
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"Host": "xxxx",
|
||||||
|
"Port": 22,
|
||||||
|
"Username": "xxx",
|
||||||
|
"Password": "xxx",
|
||||||
|
"Geekbench_License": ""
|
||||||
|
}
|
18
go.mod
Normal file
18
go.mod
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
module gitlab-ee.treesir.pub/go-projects/ssh-benchmark
|
||||||
|
|
||||||
|
go 1.20
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/bitfield/script v0.22.0
|
||||||
|
github.com/bramvdbogaerde/go-scp v1.2.1
|
||||||
|
github.com/elulcao/progress-bar v0.1.1
|
||||||
|
github.com/sirupsen/logrus v1.9.3
|
||||||
|
golang.org/x/crypto v0.9.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/itchyny/gojq v0.12.12 // indirect
|
||||||
|
github.com/itchyny/timefmt-go v0.1.5 // indirect
|
||||||
|
golang.org/x/sys v0.8.0 // indirect
|
||||||
|
mvdan.cc/sh/v3 v3.6.0 // indirect
|
||||||
|
)
|
66
go.sum
Normal file
66
go.sum
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
github.com/bitfield/script v0.22.0 h1:LA7QHuEsXMPD52YLtxWrlqCCy+9FOpzNYfsRHC5Gsrc=
|
||||||
|
github.com/bitfield/script v0.22.0/go.mod h1:ms4w+9B8f2/W0mbsgWDVTtl7K94bYuZc3AunnJC4Ebs=
|
||||||
|
github.com/bramvdbogaerde/go-scp v1.2.1 h1:BKTqrqXiQYovrDlfuVFaEGz0r4Ou6EED8L7jCXw6Buw=
|
||||||
|
github.com/bramvdbogaerde/go-scp v1.2.1/go.mod h1:s4ZldBoRAOgUg8IrRP2Urmq5qqd2yPXQTPshACY8vQ0=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/elulcao/progress-bar v0.1.1 h1:yLVafDn4pDitkpqomzF5zixDRl0TySLX6unEUuYJ5wE=
|
||||||
|
github.com/elulcao/progress-bar v0.1.1/go.mod h1:ejyDVliG6R+ekjJY8BHPHFNJiIQV00zyWT7920D/Nls=
|
||||||
|
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
||||||
|
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
|
||||||
|
github.com/itchyny/gojq v0.12.12 h1:x+xGI9BXqKoJQZkr95ibpe3cdrTbY8D9lonrK433rcA=
|
||||||
|
github.com/itchyny/gojq v0.12.12/go.mod h1:j+3sVkjxwd7A7Z5jrbKibgOLn0ZfLWkV+Awxr/pyzJE=
|
||||||
|
github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE=
|
||||||
|
github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
|
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||||
|
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||||
|
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
||||||
|
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||||
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||||
|
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
mvdan.cc/editorconfig v0.2.0/go.mod h1:lvnnD3BNdBYkhq+B4uBuFFKatfp02eB6HixDvEz91C0=
|
||||||
|
mvdan.cc/sh/v3 v3.6.0 h1:gtva4EXJ0dFNvl5bHjcUEvws+KRcDslT8VKheTYkbGU=
|
||||||
|
mvdan.cc/sh/v3 v3.6.0/go.mod h1:U4mhtBLZ32iWhif5/lD+ygy1zrgaQhUu+XFy7C8+TTA=
|
BIN
images/README/2023-07-31 18.15.23 2.gif
Normal file
BIN
images/README/2023-07-31 18.15.23 2.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 818 KiB |
276
main.go
Normal file
276
main.go
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"context"
|
||||||
|
"embed"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bitfield/script"
|
||||||
|
"github.com/bramvdbogaerde/go-scp"
|
||||||
|
pcmd "github.com/elulcao/progress-bar/cmd"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
type envConfig struct {
|
||||||
|
Host string `json:"Host"`
|
||||||
|
Port int `json:"Port"`
|
||||||
|
Username string `json:"Username"`
|
||||||
|
Password string `json:"Password"`
|
||||||
|
Geekbench_License string `json:"Geekbench_License"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start an iperf3 server locally.
|
||||||
|
func iperfServer() {
|
||||||
|
cmd := exec.Command("benchmark/tools/iperf3", "-s")
|
||||||
|
cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func pBar(hsotNum int) {
|
||||||
|
pb := pcmd.NewPBar()
|
||||||
|
pb.SignalHandler()
|
||||||
|
pb.Total = uint16(150 * hsotNum)
|
||||||
|
|
||||||
|
// pb.RenderPBar(i)
|
||||||
|
for i := 1; uint16(i) <= pb.Total; i++ {
|
||||||
|
pb.RenderPBar(i)
|
||||||
|
if uint16(i) == pb.Total-1 {
|
||||||
|
i -= 2
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Unzip(src string, dest string) error {
|
||||||
|
r, err := zip.OpenReader(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
for _, f := range r.File {
|
||||||
|
rc, err := f.Open()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer rc.Close()
|
||||||
|
|
||||||
|
fpath := filepath.Join(dest, f.Name)
|
||||||
|
if f.FileInfo().IsDir() {
|
||||||
|
os.MkdirAll(fpath, os.ModePerm)
|
||||||
|
} else {
|
||||||
|
var dir string
|
||||||
|
if lastIndex := strings.LastIndex(fpath, string(os.PathSeparator)); lastIndex > -1 {
|
||||||
|
dir = fpath[:lastIndex]
|
||||||
|
}
|
||||||
|
err = os.MkdirAll(dir, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f, err := os.OpenFile(
|
||||||
|
fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(f, rc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func randomString(length int) string {
|
||||||
|
const charset = "abcdefghijklmnopqrstuvwxyz" +
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||||
|
|
||||||
|
var seededRand *rand.Rand = rand.New(
|
||||||
|
rand.NewSource(time.Now().UnixNano()))
|
||||||
|
|
||||||
|
b := make([]byte, length)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = charset[seededRand.Intn(len(charset))]
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encapsulating Remote Execution of Shell Commands
|
||||||
|
func remoteShellExec(rClient ssh.Client, commands []string, logOut bool) error {
|
||||||
|
outputStr := ""
|
||||||
|
for _, command := range commands {
|
||||||
|
session, err := rClient.NewSession()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Failed to create session: %s", err.Error())
|
||||||
|
}
|
||||||
|
defer session.Close()
|
||||||
|
output, err := session.CombinedOutput(command)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Failed to run: %s", err.Error())
|
||||||
|
}
|
||||||
|
outputStr = string(output)
|
||||||
|
if logOut {
|
||||||
|
fmt.Println(" ")
|
||||||
|
fmt.Println(outputStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test whether the SSH target host is reachable.
|
||||||
|
func testSSH(rClient ssh.Client) bool {
|
||||||
|
|
||||||
|
session, err := rClient.NewSession()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("Failed to dial: ", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer session.Close()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:embed benchmark.zip
|
||||||
|
var f embed.FS
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
srcDir := "benchmark"
|
||||||
|
dstDir := "/tmp/" + randomString(10) + "/"
|
||||||
|
|
||||||
|
data, _ := f.ReadFile("benchmark.zip")
|
||||||
|
|
||||||
|
// Write byte slices to temporary zip files.
|
||||||
|
tmpfile, err := os.CreateTemp("", "example")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
tmpfile.Write(data)
|
||||||
|
tmpfile.Close()
|
||||||
|
|
||||||
|
// Extract files from embed.
|
||||||
|
Unzip(tmpfile.Name(), ".")
|
||||||
|
|
||||||
|
file, err := os.Open("config.json")
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("Error opening file:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
envConfig := &envConfig{}
|
||||||
|
decoder := json.NewDecoder(file)
|
||||||
|
err = decoder.Decode(&envConfig)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error decoding file:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Read the local config.json file and inject it as variables.
|
||||||
|
username := envConfig.Username
|
||||||
|
password := envConfig.Password
|
||||||
|
port := envConfig.Port
|
||||||
|
geekbenchLicense := envConfig.Geekbench_License
|
||||||
|
HostExecOut, _ := script.Exec("hostname -I").String()
|
||||||
|
hostIPS := strings.Split(HostExecOut, " ")
|
||||||
|
|
||||||
|
config := &ssh.ClientConfig{
|
||||||
|
User: username,
|
||||||
|
Auth: []ssh.AuthMethod{
|
||||||
|
ssh.Password(password),
|
||||||
|
},
|
||||||
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print and start the motto.
|
||||||
|
logrus.Info("Benchmarking...")
|
||||||
|
|
||||||
|
// Get current time.
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
// Initialize bar cmd.
|
||||||
|
go pBar(len(strings.Split(envConfig.Host, ",")))
|
||||||
|
|
||||||
|
// Create iperfServer coroutine.
|
||||||
|
go iperfServer()
|
||||||
|
|
||||||
|
// Automatically close the iperfServer coroutine.
|
||||||
|
defer func() {
|
||||||
|
iperf3KillCmd := exec.Command("/bin/bash", "-c", "ps aux | awk '/iperf3/ {print $2}' | xargs kill -9")
|
||||||
|
iperf3KillCmd.Run()
|
||||||
|
cleanWorkspaceCmd := exec.Command("rm", "-rf", srcDir)
|
||||||
|
cleanWorkspaceCmd.Run()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Use the testSSH function to test whether the target host is reachable.
|
||||||
|
for _, host := range strings.Split(envConfig.Host, ",") {
|
||||||
|
serverURL := fmt.Sprintf("%s:%d", host, port)
|
||||||
|
client, err := ssh.Dial("tcp", serverURL, config)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("Failed to dial: ", err)
|
||||||
|
}
|
||||||
|
if testSSH(*client) {
|
||||||
|
logrus.Infof("Host: %s, SSH connection successful.", host)
|
||||||
|
} else {
|
||||||
|
logrus.Errorf("Host: %s, SSH connection failed.", host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split each host in config.Host by comma.
|
||||||
|
for _, host := range strings.Split(envConfig.Host, ",") {
|
||||||
|
serverURL := fmt.Sprintf("%s:%d", host, port)
|
||||||
|
|
||||||
|
client, err := ssh.Dial("tcp", serverURL, config)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("Failed to dial: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = remoteShellExec(*client, []string{"mkdir -p " + dstDir}, false)
|
||||||
|
|
||||||
|
filepath.Walk(srcDir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if info.IsDir() {
|
||||||
|
remoteShellExec(*client, []string{"mkdir -p " + dstDir + path}, false)
|
||||||
|
} else {
|
||||||
|
scpClient, err := scp.NewClientBySSH(client)
|
||||||
|
scpClient.Connect()
|
||||||
|
file, err := os.Open(path)
|
||||||
|
defer file.Close()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("Failed to open file: ", err)
|
||||||
|
}
|
||||||
|
err = scpClient.CopyFile(context.Background(), file, dstDir+path, "0655")
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("Failed to copy file: ", err)
|
||||||
|
}
|
||||||
|
scpClient.Close()
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// Execute remote script.
|
||||||
|
logrus.Infof("Host: %s, Executing the remote script...", host)
|
||||||
|
|
||||||
|
_ = remoteShellExec(*client, []string{
|
||||||
|
"cd " + dstDir + srcDir + " && " + "./run.sh " + hostIPS[0] + " " + "'" + geekbenchLicense + "'",
|
||||||
|
}, true)
|
||||||
|
|
||||||
|
// Delete remote file.
|
||||||
|
remoteShellExec(*client, []string{"rm -rf " + dstDir}, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print execution time.
|
||||||
|
logrus.Infof("Total execution time for this run: %s", time.Since(start))
|
||||||
|
}
|
276
test/test.go
Normal file
276
test/test.go
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"context"
|
||||||
|
"embed"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bitfield/script"
|
||||||
|
"github.com/bramvdbogaerde/go-scp"
|
||||||
|
pcmd "github.com/elulcao/progress-bar/cmd"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
type envConfig struct {
|
||||||
|
Host string `json:"Host"`
|
||||||
|
Port int `json:"Port"`
|
||||||
|
Username string `json:"Username"`
|
||||||
|
Password string `json:"Password"`
|
||||||
|
Geekbench_License string `json:"Geekbench_License"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start an iperf3 server locally.
|
||||||
|
func iperfServer() {
|
||||||
|
cmd := exec.Command("benchmark/tools/iperf3", "-s")
|
||||||
|
cmd.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func pBar(hsotNum int) {
|
||||||
|
pb := pcmd.NewPBar()
|
||||||
|
pb.SignalHandler()
|
||||||
|
pb.Total = uint16(150 * hsotNum)
|
||||||
|
|
||||||
|
// pb.RenderPBar(i)
|
||||||
|
for i := 1; uint16(i) <= pb.Total; i++ {
|
||||||
|
pb.RenderPBar(i)
|
||||||
|
if uint16(i) == pb.Total-1 {
|
||||||
|
i -= 2
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Unzip(src string, dest string) error {
|
||||||
|
r, err := zip.OpenReader(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
for _, f := range r.File {
|
||||||
|
rc, err := f.Open()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer rc.Close()
|
||||||
|
|
||||||
|
fpath := filepath.Join(dest, f.Name)
|
||||||
|
if f.FileInfo().IsDir() {
|
||||||
|
os.MkdirAll(fpath, os.ModePerm)
|
||||||
|
} else {
|
||||||
|
var dir string
|
||||||
|
if lastIndex := strings.LastIndex(fpath, string(os.PathSeparator)); lastIndex > -1 {
|
||||||
|
dir = fpath[:lastIndex]
|
||||||
|
}
|
||||||
|
err = os.MkdirAll(dir, os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f, err := os.OpenFile(
|
||||||
|
fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(f, rc)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func randomString(length int) string {
|
||||||
|
const charset = "abcdefghijklmnopqrstuvwxyz" +
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||||
|
|
||||||
|
var seededRand *rand.Rand = rand.New(
|
||||||
|
rand.NewSource(time.Now().UnixNano()))
|
||||||
|
|
||||||
|
b := make([]byte, length)
|
||||||
|
for i := range b {
|
||||||
|
b[i] = charset[seededRand.Intn(len(charset))]
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encapsulating Remote Execution of Shell Commands
|
||||||
|
func remoteShellExec(rClient ssh.Client, commands []string, logOut bool) error {
|
||||||
|
outputStr := ""
|
||||||
|
for _, command := range commands {
|
||||||
|
session, err := rClient.NewSession()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Failed to create session: %s", err.Error())
|
||||||
|
}
|
||||||
|
defer session.Close()
|
||||||
|
output, err := session.CombinedOutput(command)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("Failed to run: %s", err.Error())
|
||||||
|
}
|
||||||
|
outputStr = string(output)
|
||||||
|
if logOut {
|
||||||
|
fmt.Println(" ")
|
||||||
|
fmt.Println(outputStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test whether the SSH target host is reachable.
|
||||||
|
func testSSH(rClient ssh.Client) bool {
|
||||||
|
|
||||||
|
session, err := rClient.NewSession()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("Failed to dial: ", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer session.Close()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:embed benchmark.zip
|
||||||
|
var f embed.FS
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
srcDir := "benchmark"
|
||||||
|
dstDir := "/tmp/" + randomString(10) + "/"
|
||||||
|
|
||||||
|
data, _ := f.ReadFile("benchmark.zip")
|
||||||
|
|
||||||
|
// Write byte slices to temporary zip files.
|
||||||
|
tmpfile, err := os.CreateTemp("", "example")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
tmpfile.Write(data)
|
||||||
|
tmpfile.Close()
|
||||||
|
|
||||||
|
// Extract files from embed.
|
||||||
|
Unzip(tmpfile.Name(), ".")
|
||||||
|
|
||||||
|
file, err := os.Open("config.json")
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("Error opening file:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
envConfig := &envConfig{}
|
||||||
|
decoder := json.NewDecoder(file)
|
||||||
|
err = decoder.Decode(&envConfig)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error decoding file:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Read the local config.json file and inject it as variables.
|
||||||
|
username := envConfig.Username
|
||||||
|
password := envConfig.Password
|
||||||
|
port := envConfig.Port
|
||||||
|
geekbenchLicense := envConfig.Geekbench_License
|
||||||
|
HostExecOut, _ := script.Exec("hostname -I").String()
|
||||||
|
hostIPS := strings.Split(HostExecOut, " ")
|
||||||
|
|
||||||
|
config := &ssh.ClientConfig{
|
||||||
|
User: username,
|
||||||
|
Auth: []ssh.AuthMethod{
|
||||||
|
ssh.Password(password),
|
||||||
|
},
|
||||||
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print and start the motto.
|
||||||
|
logrus.Info("Benchmarking...")
|
||||||
|
|
||||||
|
// Get current time.
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
// 初始化 bar cmd
|
||||||
|
go pBar(len(strings.Split(envConfig.Host, ",")))
|
||||||
|
|
||||||
|
// 创建 iperfServer 协程
|
||||||
|
go iperfServer()
|
||||||
|
|
||||||
|
// Automatically close the iperfServer coroutine.
|
||||||
|
defer func() {
|
||||||
|
iperf3KillCmd := exec.Command("/bin/bash", "-c", "ps aux | awk '/iperf3/ {print $2}' | xargs kill -9")
|
||||||
|
iperf3KillCmd.Run()
|
||||||
|
cleanWorkspaceCmd := exec.Command("rm", "-rf", srcDir)
|
||||||
|
cleanWorkspaceCmd.Run()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Use the testSSH function to test whether the target host is reachable.
|
||||||
|
for _, host := range strings.Split(envConfig.Host, ",") {
|
||||||
|
serverURL := fmt.Sprintf("%s:%d", host, port)
|
||||||
|
client, err := ssh.Dial("tcp", serverURL, config)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("Failed to dial: ", err)
|
||||||
|
}
|
||||||
|
if testSSH(*client) {
|
||||||
|
logrus.Infof("Host: %s, SSH connection successful.", host)
|
||||||
|
} else {
|
||||||
|
logrus.Errorf("Host: %s, SSH connection failed.", host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split each host in config.Host by comma.
|
||||||
|
for _, host := range strings.Split(envConfig.Host, ",") {
|
||||||
|
serverURL := fmt.Sprintf("%s:%d", host, port)
|
||||||
|
|
||||||
|
client, err := ssh.Dial("tcp", serverURL, config)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("Failed to dial: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = remoteShellExec(*client, []string{"mkdir -p " + dstDir}, false)
|
||||||
|
|
||||||
|
filepath.Walk(srcDir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if info.IsDir() {
|
||||||
|
remoteShellExec(*client, []string{"mkdir -p " + dstDir + path}, false)
|
||||||
|
} else {
|
||||||
|
scpClient, err := scp.NewClientBySSH(client)
|
||||||
|
scpClient.Connect()
|
||||||
|
file, err := os.Open(path)
|
||||||
|
defer file.Close()
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("Failed to open file: ", err)
|
||||||
|
}
|
||||||
|
err = scpClient.CopyFile(context.Background(), file, dstDir+path, "0655")
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("Failed to copy file: ", err)
|
||||||
|
}
|
||||||
|
scpClient.Close()
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// Execute remote script.
|
||||||
|
logrus.Infof("Host: %s, Executing the remote script...", host)
|
||||||
|
|
||||||
|
_ = remoteShellExec(*client, []string{
|
||||||
|
"cd " + dstDir + srcDir + " && " + "./run.sh " + hostIPS[0] + " " + "'" + geekbenchLicense + "'",
|
||||||
|
}, true)
|
||||||
|
|
||||||
|
// Delete remote file.
|
||||||
|
remoteShellExec(*client, []string{"rm -rf " + dstDir}, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print execution time.
|
||||||
|
logrus.Infof("Total execution time for this run: %s", time.Since(start))
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user