├── .github └── workflows │ └── ci.yml ├── LICENSE ├── README.md ├── bin ├── README.md ├── compile.sh ├── cross-compile.sh ├── example.json ├── fio │ ├── fio_aarch64 │ ├── fio_arm │ ├── fio_x64 │ └── fio_x86 └── iperf │ ├── iperf3_aarch64 │ ├── iperf3_arm │ ├── iperf3_x64 │ └── iperf3_x86 └── yabs.sh /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 🤖 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | shellcheck: 10 | name: Shellcheck 🐚 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout repository 📥 14 | uses: actions/checkout@v4 15 | 16 | - name: Run ShellCheck ✅ 17 | uses: ludeeus/action-shellcheck@2.0.0 18 | env: 19 | SHELLCHECK_OPTS: -e SC1091 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2019 Mason Rowe 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Yet-Another-Bench-Script 2 | 3 | Presenting an attempt to create _yet another_ Linux server *bench*marking _script_... 4 | 5 | ![](https://user-images.githubusercontent.com/8313125/106475387-e1f6da00-6473-11eb-918c-c785ebeef8b9.jpg) 6 | Logo design by [Dian Pratama](https://github.com/dianp) 7 | 8 | This script automates the execution of the best benchmarking tools in the industry. Included are several tests to check the performance of critical areas of a server: disk performance with [fio](https://github.com/axboe/fio), network performance with [iperf3](https://github.com/esnet/iperf), and CPU/memory performance with [Geekbench](https://www.geekbench.com/). The script is designed to not require any external dependencies to be installed nor elevated privileges to run. If there are any features that you would like to see added, feel free to submit an issue describing your feature request or fork the project and submit a PR! 9 | 10 | ### **What's New With YABS?** 11 | * [27 Feb 2023](https://github.com/masonr/yet-another-bench-script/commit/06eaa2ab3b32355bec8278c51c4be93b3662a96d) - Newly released [Geekbench 6](https://www.geekbench.com/) is added as the default Geekbench test. 12 | * [26 Feb 2023](https://github.com/masonr/yet-another-bench-script/commit/f075baf59c3057983fff0a30ea0c746b5ea88d91) - Network information added to YABS output using [ip-api](https://ip-api.com/). 13 | * [15 Aug 2022](https://github.com/masonr/yet-another-bench-script/commit/ae24e70fbf7a4848e81a70cf829ec44e060e63d5) - Added JSON output/upload support to export or auto-upload of YABS results for sharing. 14 | 15 | ## How to Run 16 | 17 | ``` 18 | curl -sL https://yabs.sh | bash 19 | ``` 20 | 21 | or 22 | 23 | ``` 24 | wget -qO- yabs.sh | bash 25 | ``` 26 | 27 | **Local fio/iperf3 Packages**: If the tested system has fio and/or iperf3 already installed, the local package will take precedence over the precompiled binary. 28 | 29 | **Experimental ARM Compatibility**: Initial ARM compatibility has been introduced, however, is not considered entirely stable due to limited testing on distinct ARM devices. Report any errors or issues. 30 | 31 | **High Bandwidth Usage Notice**: By default, this script will perform many iperf network tests, which will try to max out the network port for ~20s per location (10s in each direction). Low-bandwidth servers (such as a NAT VPS) should consider running this script with the `-r` flag (for reduced iperf locations) or the `-i` flag (to disable network tests entirely). 32 | 33 | **Windows Users**: This script can be run on Windows systems by using [Windows Subsystem for Linux v2 (WSL 2)](https://learn.microsoft.com/en-us/windows/wsl/about). WSLv1 will not run the script and binaries correctly. 34 | 35 | ### Flags (Skipping Tests, Reducing iperf Locations, Geekbench 4/5/6, etc.) 36 | 37 | ``` 38 | curl -sL https://yabs.sh | bash -s -- -flags 39 | ``` 40 | 41 | | Flag | Description | 42 | | ---- | ----------- | 43 | | -b | Forces use of pre-compiled binaries from repo over local packages | 44 | | -f/-d | Disables the fio (disk performance) test | 45 | | -i | Disables the iperf (network performance) test | 46 | | -g | Disables the Geekbench (system performance) test | 47 | | -n | Skips the network information lookup and print out | 48 | | -h | Prints the help message with usage, flags detected, and local package (fio/iperf) status | 49 | | -r | Reduces the number of iperf locations (Scaleway/Clouvider LON+NYC) to lessen bandwidth usage | 50 | | -4 | Runs a Geekbench 4 test and disables the Geekbench 6 test | 51 | | -5 | Runs a Geekbench 5 test and disables the Geekbench 6 test | 52 | | -9 | Runs both the Geekbench 4 and 5 tests instead of the Geekbench 6 test | 53 | | -6 | Re-enables the Geekbench 6 test if any of the following were used: -4, -5, or -9 (-6 flag must be last to not be overridden) | 54 | | -j | Prints a JSON representation of the results to the screen | 55 | | -w \ | Writes the JSON results to a file using the file name provided | 56 | | -s \ | Sends a JSON representation of the results to the designated URL(s) (see section below) | 57 | 58 | Options can be grouped together to skip multiple tests, i.e. `-fg` to skip the disk and system performance tests (effectively only testing network performance). 59 | 60 | **Geekbench License Key**: A Geekbench license key can be utilized during the Geekbench test to unlock all features. Simply put the email and key for the license in a file called _geekbench.license_. `echo "email@domain.com ABCDE-12345-FGHIJ-57890" > geekbench.license` 61 | 62 | ### Submitting JSON Results 63 | 64 | Results from running this script can be sent to your benchmark results website of choice in JSON format. Invoke the `-s` flag and pass the URL to where the results should be submitted to: 65 | 66 | ``` 67 | curl -sL https://yabs.sh | bash -s -- -s "https://example.com/yabs/post" 68 | ``` 69 | 70 | JSON results can be sent to multiple endpoints by entering each site joined by a comma (e.g. "https://example.com/yabs/post,http://example.com/yabs2/post"). 71 | 72 | Sites supporting submission of YABS JSON results: 73 | 74 | | Website | Example Command | 75 | | --- | --- | 76 | | [YABSdb](https://yabsdb.com/) | `curl -sL https://yabs.sh \| bash -s -- -s "https://yabsdb.com/add"` | 77 | | [VPSBenchmarks](https://www.vpsbenchmarks.com/yabs/get_started) | `curl -sL https://yabs.sh \| bash -s -- -s https://www.vpsbenchmarks.com/yabs/upload` | 78 | 79 | Example JSON output: [example.json](bin/example.json). 80 | 81 | ## Tests Conducted 82 | 83 | * **[fio](https://github.com/axboe/fio)** - the most comprehensive I/O testing software available, fio grants the ability to evaluate disk performance in a variety of methods with a variety of options. Four random read and write fio disk tests are conducted as part of this script with 4k, 64k, 512k, and 1m block sizes. The tests are designed to evaluate disk throughput in near-real world (using random) scenarios with a 50/50 split (50% reads and 50% writes per test). 84 | * **[iperf3](https://github.com/esnet/iperf)** - the industry standard for testing download and upload speeds to various locations. This script utilizes iperf3 with 8 parallel threads and tests both download and upload speeds. If an iperf server is busy after 5 tries, the speed test for that location/direction is skipped. 85 | * **[Geekbench](https://www.geekbench.com/)** - Geekbench is a benchmarking program that measures system performance, which is widely used in the tech community. The web URL is displayed to be able to see complete test and individual benchmark results and allow comparison to other geekbench'd systems. The claim URL to add the Geekbench result to your Geekbench profile is written to a file in the directory that this script is executed from. By default, Geekbench 6 is the only Geekbench test performed, however, Geekbench 4 and/or 5 can also be toggled on by passing the appropriate flag. 86 | 87 | ### Security Notice 88 | 89 | This script relies on external binaries in order to complete the performance tests. The network (iperf3) and disk (fio) tests use binaries that are compiled by myself utilizing a [Holy Build Box](https://github.com/phusion/holy-build-box) compilation environment to ensure binary portability. The reasons for doing this include ensuring standardized (parsable) output, allowing support of both 32-bit and 64-bit architectures, bypassing the need for prerequisites to be compiled and/or installed, among other reasons. For the system test, a Geekbench tarball is downloaded, extracted, and the resulting binary is run. Use this script at your own risk as you would with any script publicly available on the net. Additional information regarding the binaries, including compilation notes and steps, can be found in the bin directory's [README page](bin/README.md). 90 | 91 | ## Example Output 92 | 93 | ``` 94 | # ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## # 95 | # Yet-Another-Bench-Script # 96 | # v2023-04-23 # 97 | # https://github.com/masonr/yet-another-bench-script # 98 | # ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## # 99 | 100 | Sun 23 Apr 2023 01:41:14 PM EDT 101 | 102 | Basic System Information: 103 | --------------------------------- 104 | Uptime : 342 days, 18 hours, 35 minutes 105 | Processor : Intel(R) Xeon(R) E-2276G CPU @ 3.80GHz 106 | CPU cores : 12 @ 4693.667 MHz 107 | AES-NI : ✔ Enabled 108 | VM-x/AMD-V : ✔ Enabled 109 | RAM : 15.5 GiB 110 | Swap : 14.9 GiB 111 | Disk : 864.5 GiB 112 | Distro : Ubuntu 20.04.6 LTS 113 | Kernel : 5.4.0-110-generic 114 | VM Type : NONE 115 | IPv4/IPv6 : ✔ Online / ✔ Online 116 | 117 | IPv6 Network Information: 118 | --------------------------------- 119 | ISP : Clouvider Limited 120 | ASN : AS62240 Clouvider 121 | Host : USA Network 122 | Location : New York, New York (NY) 123 | Country : United States 124 | 125 | fio Disk Speed Tests (Mixed R/W 50/50): 126 | --------------------------------- 127 | Block Size | 4k (IOPS) | 64k (IOPS) 128 | ------ | --- ---- | ---- ---- 129 | Read | 405.41 MB/s (101.3k) | 407.96 MB/s (6.3k) 130 | Write | 406.48 MB/s (101.6k) | 410.11 MB/s (6.4k) 131 | Total | 811.90 MB/s (202.9k) | 818.08 MB/s (12.7k) 132 | | | 133 | Block Size | 512k (IOPS) | 1m (IOPS) 134 | ------ | --- ---- | ---- ---- 135 | Read | 380.21 MB/s (742) | 394.55 MB/s (385) 136 | Write | 400.41 MB/s (782) | 420.82 MB/s (410) 137 | Total | 780.62 MB/s (1.5k) | 815.37 MB/s (795) 138 | 139 | iperf3 Network Speed Tests (IPv4): 140 | --------------------------------- 141 | Provider | Location (Link) | Send Speed | Recv Speed | Ping 142 | ----- | ----- | ---- | ---- | ---- 143 | Clouvider | London, UK (10G) | 1.61 Gbits/sec | 2.39 Gbits/sec | 77.5 ms 144 | Scaleway | Paris, FR (10G) | busy | 2.25 Gbits/sec | 83.3 ms 145 | Clouvider | NYC, NY, US (10G) | 9.10 Gbits/sec | 8.85 Gbits/sec | 1.21 ms 146 | 147 | iperf3 Network Speed Tests (IPv6): 148 | --------------------------------- 149 | Provider | Location (Link) | Send Speed | Recv Speed | Ping 150 | ----- | ----- | ---- | ---- | ---- 151 | Clouvider | London, UK (10G) | 2.00 Gbits/sec | 21.1 Mbits/sec | 76.7 ms 152 | Scaleway | Paris, FR (10G) | 2.66 Gbits/sec | 1.56 Gbits/sec | 75.9 ms 153 | Clouvider | NYC, NY, US (10G) | 3.42 Gbits/sec | 7.80 Gbits/sec | 1.15 ms 154 | 155 | Geekbench 4 Benchmark Test: 156 | --------------------------------- 157 | Test | Value 158 | | 159 | Single Core | 5949 160 | Multi Core | 23425 161 | Full Test | https://browser.geekbench.com/v4/cpu/16746501 162 | 163 | Geekbench 5 Benchmark Test: 164 | --------------------------------- 165 | Test | Value 166 | | 167 | Single Core | 1317 168 | Multi Core | 5529 169 | Full Test | https://browser.geekbench.com/v5/cpu/21102444 170 | 171 | Geekbench 6 Benchmark Test: 172 | --------------------------------- 173 | Test | Value 174 | | 175 | Single Core | 1549 176 | Multi Core | 5278 177 | Full Test | https://browser.geekbench.com/v6/cpu/1021916 178 | 179 | YABS completed in 12 min 49 sec 180 | 181 | ``` 182 | 183 | ## Acknowledgements 184 | 185 | This script was inspired by several great benchmarking scripts out there, including, but not limited to, [bench.sh](https://bench.sh/), [nench.sh](https://github.com/n-st/nench), [ServerBench](https://github.com/K4Y5/ServerBench), among others. Members of the [HostBalls](https://hostballs.com), [LowEndSpirit](https://lowendspirit.com), and [LowEndTalk](https://lowendtalk.com) hosting-related communities play a pivotal role in testing, evaluating, and shaping this script as it matures. 186 | 187 | ## License 188 | ``` 189 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 190 | Version 2, December 2004 191 | 192 | Copyright (C) 2019 Mason Rowe 193 | 194 | Everyone is permitted to copy and distribute verbatim or modified 195 | copies of this license document, and changing it is allowed as long 196 | as the name is changed. 197 | 198 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 199 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 200 | 201 | 0. You just DO WHAT THE FUCK YOU WANT TO. 202 | ``` 203 | -------------------------------------------------------------------------------- /bin/README.md: -------------------------------------------------------------------------------- 1 | ## YABS Pre-Compiled Binaries 2 | 3 | This directory contains all of the binaries required to run the benchmarking tests. Naturally, there is a security risk to your machine and its contents by running this script since, after all, this is just a script on the internet. You'll simply have to have confidence that I don't have malicious intent and am semi-competent at writing a bash script. The script is made public so you can look at the code yourself. The binaries were compiled using a [Holy Build Box](https://github.com/phusion/holy-build-box) compilation environment in order to ensure the most portability. The compiled binary version numbers and compilations steps are noted below. Please open an issue if the compiled version is out of date and lacking any security-related and/or performance updates. 4 | 5 | ### Binaries 6 | 7 | | Binary Name | Version | Compile Date | Architecture | OS | SHA-256 Hash
(VirusTotal Scan) | 8 | |:-:|:-:|:-:|:-:|:-:|:-:| 9 | | fio_x64 | 3.39 | 20-APR-2025 | x86_64 | 64-bit | [b511bda](https://www.virustotal.com/gui/file/b511bda3b26b6d840698f543d63e956d7466b8512c10ff0ada8292d556c33fb1) | 10 | | fio_x86 | 3.39 | 20-APR-2025 | i686 | 32-bit | [42e2e0b](https://www.virustotal.com/gui/file/42e2e0b0370faeb8e53dcf48dfff15daa9baaadfd196d9bdda57af196bedf0b3) | 11 | | fio_aarch64 | 3.39 | 20-APR-2025 | ARM (aarch64) | 64-bit | [e2942a2](https://www.virustotal.com/gui/file/e2942a26d4b249076486677c9c12cd7f1a572854a5e136597d1391e8ad75ffb0) | 12 | | fio_arm | 3.39 | 20-APR-2025 | ARM | 32-bit | [3a96b1c](https://www.virustotal.com/gui/file/3a96b1cadfb51501b7fd54dc47a4dad666dc4382d4fe152116c62ae6a07485ea) | 13 | | iperf3_x64 | 3.18 | 14-DEC-2024 | x86_64 | 64-bit | [ef787ab](https://www.virustotal.com/gui/file/ef787abbe4b09c7958ed592df52dfe3a2848cbdee5b76738c757d7c51c348053) | 14 | | iperf3_x86 | 3.18 | 14-DEC-2024 | i686 | 32-bit | [655eb51](https://www.virustotal.com/gui/file/655eb51abc36ddaa624c1d0e98c6930e8b1e9d91c85e5a3443624355656be9b9) | 15 | | iperf3_aarch64 | 3.18 | 14-DEC-2024 | ARM (aarch64) | 64-bit | [92e5821](https://www.virustotal.com/gui/file/92e5821cfbaa1f8faf123b4d6773dc0f6efef221b9308668a21ddabc04a1de20) | 16 | | iperf3_arm | 3.15* | 20-OCT-2023 | ARM | 32-bit | [310e80f](https://www.virustotal.com/gui/file/310e80f442dda47fa0fe41225af85e8b91e75116dce5187f123380fd3c3c85a8) | 17 | 18 | Note: ARM compatibility is considered experimental. Static binaries for 32-bit and ARM-based machines are cross-compiled within a Holy Build Box container using the [musl toolchain](https://musl.cc/). 19 | 20 | ### Compile Notes 21 | 22 | **Pre-reqs**: 23 | * Docker - https://www.docker.com/ 24 | 25 | **Compiling 64-bit binaries**: 26 | 27 | ```sh 28 | docker run -t -i --rm -v `pwd`:/io phusion/holy-build-box-64:latest bash /io/compile.sh 29 | ``` 30 | 31 | 64-bit binaries will be placed in the current directory. 32 | 33 | ### Cross-compiling Notes 34 | 35 | Compilation of ARM-compatible binaries requires additional environment variables to identify the proper musl toolchain and architecture to target for cross-compilation. 36 | 37 | **Compiling 32-bit binaries**: 38 | 39 | ```sh 40 | docker run -t -i --rm -v `pwd`:/io --env ARCH=x86 --env CROSS=i686-linux-musl --env HOST=i686-linux-musl phusion/holy-build-box-64:latest bash /io/cross-compile.sh 41 | ``` 42 | 43 | **Compiling ARM 64-bit binaries**: 44 | 45 | ```sh 46 | docker run -t -i --rm -v `pwd`:/io --env ARCH=aarch64 --env CROSS=aarch64-linux-musl --env HOST=aarch64-linux-gnu phusion/holy-build-box-64:latest bash /io/cross-compile.sh 47 | ``` 48 | 49 | **Compiling ARM 32-bit binaries\***: 50 | 51 | ```sh 52 | docker run -t -i --rm -v `pwd`:/io --env ARCH=arm --env CROSS=arm-linux-musleabihf --env HOST=arm-linux-gnueabihf phusion/holy-build-box-64:latest bash /io/cross-compile.sh 53 | ``` 54 | 55 | 64-bit (aarch64) and 32-bit (x86, arm) binaries will be placed in the current directory. 56 | 57 | \* ARM 32-bit: Last sucessful compiliation of ARM 32-bit binary for iperf3 is v3.15. All later versions fail to compile. 58 | -------------------------------------------------------------------------------- /bin/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Activate Holy Build Box lib compiliation environment 5 | source /hbb/activate 6 | 7 | set -x 8 | 9 | # remove obsolete CentOS repos 10 | cd /etc/yum.repos.d/ 11 | rm CentOS-Base.repo CentOS-SCLo-scl-rh.repo CentOS-SCLo-scl.repo CentOS-fasttrack.repo CentOS-x86_64-kernel.repo 12 | 13 | yum install -y yum-plugin-ovl # fix for docker overlay fs 14 | yum install -y xz 15 | 16 | # determine arch 17 | ARCH=$(uname -m) 18 | if [[ $ARCH = *x86_64* ]]; then 19 | # container is 64-bit 20 | ARCH="x64" 21 | elif [[ $ARCH = *i?86* ]]; then 22 | # container is 32-bit 23 | ARCH="x86" 24 | fi 25 | 26 | 27 | # download, compile, and install libaio as static library 28 | cd ~ 29 | curl -L http://ftp.de.debian.org/debian/pool/main/liba/libaio/libaio_0.3.113.orig.tar.gz -o "libaio.tar.gz" 30 | tar xf libaio.tar.gz 31 | cd libaio-*/src 32 | ENABLE_SHARED=0 make prefix=/hbb_exe install 33 | 34 | # Activate Holy Build Box exe compilation environment 35 | source /hbb_exe/activate 36 | 37 | # download and compile fio 38 | cd ~ 39 | curl -L https://github.com/axboe/fio/archive/fio-3.39.tar.gz -o "fio.tar.gz" 40 | tar xf fio.tar.gz 41 | cd fio-fio* 42 | ./configure --disable-native 43 | make 44 | 45 | # verify no external shared library links 46 | libcheck fio 47 | # copy fio binary to mounted dir 48 | cp fio "/io/fio_$ARCH" 49 | 50 | # download and compile iperf 51 | cd ~ 52 | curl -L https://github.com/esnet/iperf/archive/3.18.tar.gz -o "iperf.tar.gz" 53 | tar xf iperf.tar.gz 54 | cd iperf* 55 | ./configure --disable-shared --disable-profiling 56 | make 57 | 58 | # verify no external shared library links 59 | libcheck src/iperf3 60 | # copy iperf binary to mounted dir 61 | cp src/iperf3 "/io/iperf3_$ARCH" 62 | -------------------------------------------------------------------------------- /bin/cross-compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # Activate Holy Build Box lib compiliation environment 5 | source /hbb/activate 6 | 7 | set -x 8 | 9 | # remove obsolete CentOS repos 10 | cd /etc/yum.repos.d/ 11 | rm CentOS-Base.repo CentOS-SCLo-scl-rh.repo CentOS-SCLo-scl.repo CentOS-fasttrack.repo CentOS-x86_64-kernel.repo 12 | 13 | yum install -y yum-plugin-ovl # fix for docker overlay fs 14 | yum install -y xz 15 | 16 | # download musl cross compilation toolchain 17 | cd ~ 18 | curl -L "https://musl.cc/${CROSS}-cross.tgz" -o "${CROSS}-cross.tgz" 19 | tar xf "${CROSS}-cross.tgz" 20 | 21 | # download, compile, and install libaio as static library 22 | cd ~ 23 | curl -L http://ftp.de.debian.org/debian/pool/main/liba/libaio/libaio_0.3.113.orig.tar.gz -o "libaio.tar.gz" 24 | tar xf libaio.tar.gz 25 | cd libaio-*/src 26 | CC=/root/${CROSS}-cross/bin/${CROSS}-gcc ENABLE_SHARED=0 make prefix=/hbb_exe install 27 | 28 | # Activate Holy Build Box exe compilation environment 29 | source /hbb_exe/activate 30 | 31 | # download and compile fio 32 | cd ~ 33 | curl -L https://github.com/axboe/fio/archive/fio-3.39.tar.gz -o "fio.tar.gz" 34 | tar xf fio.tar.gz 35 | cd fio-fio* 36 | CC=/root/${CROSS}-cross/bin/${CROSS}-gcc ./configure --disable-native --build-static 37 | make 38 | 39 | # verify no external shared library links 40 | libcheck fio 41 | # copy fio binary to mounted dir 42 | cp fio "/io/fio_$ARCH" 43 | 44 | # download and compile iperf 45 | cd ~ 46 | curl -L https://github.com/esnet/iperf/archive/3.18.tar.gz -o "iperf.tar.gz" 47 | tar xf iperf.tar.gz 48 | cd iperf* 49 | CC=/root/${CROSS}-cross/bin/${CROSS}-gcc ./configure --disable-shared --disable-profiling --build x86_64-pc-linux-gnu --host "${HOST}" --with-openssl=no --enable-static-bin 50 | make 51 | 52 | # verify no external shared library links 53 | libcheck src/iperf3 54 | # copy iperf binary to mounted dir 55 | cp src/iperf3 "/io/iperf3_$ARCH" 56 | -------------------------------------------------------------------------------- /bin/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "v2023-04-23", 3 | "time": "20230423-134114", 4 | "os": { 5 | "arch": "x64", 6 | "distro": "Ubuntu 20.04.6 LTS", 7 | "kernel": "5.4.0-110-generic", 8 | "uptime": 29615719.72, 9 | "vm": "NONE" 10 | }, 11 | "net": { 12 | "ipv4": true, 13 | "ipv6": true 14 | }, 15 | "cpu": { 16 | "model": "Intel(R) Xeon(R) E-2276G CPU @ 3.80GHz", 17 | "cores": 12, 18 | "freq": "4693.667 MHz", 19 | "aes": true, 20 | "virt": true 21 | }, 22 | "mem": { 23 | "ram": 16246580, 24 | "ram_units": "KiB", 25 | "swap": 15625212, 26 | "swap_units": "KiB", 27 | "disk": 906519496, 28 | "disk_units": "KB" 29 | }, 30 | "ip_info": { 31 | "protocol": "IPv6", 32 | "isp": "Clouvider Limited", 33 | "asn": "AS62240 Clouvider", 34 | "org": "USA Network", 35 | "city": "New York", 36 | "region": "New York", 37 | "region_code": "NY", 38 | "country": "United States" 39 | }, 40 | "fio": [{ 41 | "bs": "4k", 42 | "speed_r": 405417, 43 | "iops_r": 101354, 44 | "speed_w": 406487, 45 | "iops_w": 101621, 46 | "speed_rw": 811904, 47 | "iops_rw": 202975, 48 | "speed_units": "KBps" 49 | }, { 50 | "bs": "64k", 51 | "speed_r": 407967, 52 | "iops_r": 6374, 53 | "speed_w": 410114, 54 | "iops_w": 6408, 55 | "speed_rw": 818081, 56 | "iops_rw": 12782, 57 | "speed_units": "KBps" 58 | }, { 59 | "bs": "512k", 60 | "speed_r": 380212, 61 | "iops_r": 742, 62 | "speed_w": 400413, 63 | "iops_w": 782, 64 | "speed_rw": 780625, 65 | "iops_rw": 1524, 66 | "speed_units": "KBps" 67 | }, { 68 | "bs": "1m", 69 | "speed_r": 394550, 70 | "iops_r": 385, 71 | "speed_w": 420827, 72 | "iops_w": 410, 73 | "speed_rw": 815377, 74 | "iops_rw": 795, 75 | "speed_units": "KBps" 76 | }], 77 | "iperf": [{ 78 | "mode": "IPv4", 79 | "provider": "Clouvider", 80 | "loc": "London, UK (10G)", 81 | "send": "1.61 Gbits/sec", 82 | "recv": "2.39 Gbits/sec", 83 | "latency": "77.5 ms" 84 | }, { 85 | "mode": "IPv4", 86 | "provider": "Scaleway", 87 | "loc": "Paris, FR (10G)", 88 | "send": "busy ", 89 | "recv": "2.25 Gbits/sec", 90 | "latency": "83.3 ms" 91 | }, { 92 | "mode": "IPv4", 93 | "provider": "Clouvider", 94 | "loc": "NYC, NY, US (10G)", 95 | "send": "9.10 Gbits/sec", 96 | "recv": "8.85 Gbits/sec", 97 | "latency": "1.21 ms" 98 | }, { 99 | "mode": "IPv6", 100 | "provider": "Clouvider", 101 | "loc": "London, UK (10G)", 102 | "send": "2.00 Gbits/sec", 103 | "recv": "21.1 Mbits/sec", 104 | "latency": "76.7 ms" 105 | }, { 106 | "mode": "IPv6", 107 | "provider": "Scaleway", 108 | "loc": "Paris, FR (10G)", 109 | "send": "2.66 Gbits/sec", 110 | "recv": "1.56 Gbits/sec", 111 | "latency": "75.9 ms" 112 | }, { 113 | "mode": "IPv6", 114 | "provider": "Clouvider", 115 | "loc": "NYC, NY, US (10G)", 116 | "send": "3.42 Gbits/sec", 117 | "recv": "7.80 Gbits/sec", 118 | "latency": "1.15 ms" 119 | }], 120 | "geekbench": [{ 121 | "version": 4, 122 | "single": 5949, 123 | "multi": 23425, 124 | "url": "https://browser.geekbench.com/v4/cpu/16746501" 125 | }, { 126 | "version": 5, 127 | "single": 1317, 128 | "multi": 5529, 129 | "url": "https://browser.geekbench.com/v5/cpu/21102444" 130 | }, { 131 | "version": 6, 132 | "single": 1549, 133 | "multi": 5278, 134 | "url": "https://browser.geekbench.com/v6/cpu/1021916" 135 | }], 136 | "runtime": { 137 | "start": 1682271674, 138 | "end": 1682272443, 139 | "elapsed": 769 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /bin/fio/fio_aarch64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masonr/yet-another-bench-script/0958b7fcf382a2e3ddfb23a26df3cb05067f1367/bin/fio/fio_aarch64 -------------------------------------------------------------------------------- /bin/fio/fio_arm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masonr/yet-another-bench-script/0958b7fcf382a2e3ddfb23a26df3cb05067f1367/bin/fio/fio_arm -------------------------------------------------------------------------------- /bin/fio/fio_x64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masonr/yet-another-bench-script/0958b7fcf382a2e3ddfb23a26df3cb05067f1367/bin/fio/fio_x64 -------------------------------------------------------------------------------- /bin/fio/fio_x86: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masonr/yet-another-bench-script/0958b7fcf382a2e3ddfb23a26df3cb05067f1367/bin/fio/fio_x86 -------------------------------------------------------------------------------- /bin/iperf/iperf3_aarch64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masonr/yet-another-bench-script/0958b7fcf382a2e3ddfb23a26df3cb05067f1367/bin/iperf/iperf3_aarch64 -------------------------------------------------------------------------------- /bin/iperf/iperf3_arm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masonr/yet-another-bench-script/0958b7fcf382a2e3ddfb23a26df3cb05067f1367/bin/iperf/iperf3_arm -------------------------------------------------------------------------------- /bin/iperf/iperf3_x64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masonr/yet-another-bench-script/0958b7fcf382a2e3ddfb23a26df3cb05067f1367/bin/iperf/iperf3_x64 -------------------------------------------------------------------------------- /bin/iperf/iperf3_x86: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/masonr/yet-another-bench-script/0958b7fcf382a2e3ddfb23a26df3cb05067f1367/bin/iperf/iperf3_x86 -------------------------------------------------------------------------------- /yabs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Yet Another Bench Script by Mason Rowe 4 | # Initial Oct 2019; Last update Apr 2025 5 | 6 | # Disclaimer: This project is a work in progress. Any errors or suggestions should be 7 | # relayed to me via the GitHub project page linked below. 8 | # 9 | # Purpose: The purpose of this script is to quickly gauge the performance of a Linux- 10 | # based server by benchmarking network performance via iperf3, CPU and 11 | # overall system performance via Geekbench 4/5, and random disk 12 | # performance via fio. The script is designed to not require any dependencies 13 | # - either compiled or installed - nor admin privileges to run. 14 | 15 | YABS_VERSION="v2025-04-20" 16 | 17 | echo -e '# ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## #' 18 | echo -e '# Yet-Another-Bench-Script #' 19 | echo -e '# '$YABS_VERSION' #' 20 | echo -e '# https://github.com/masonr/yet-another-bench-script #' 21 | echo -e '# ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## #' 22 | 23 | echo -e 24 | date 25 | TIME_START=$(date '+%Y%m%d-%H%M%S') 26 | YABS_START_TIME=$(date +%s) 27 | 28 | # override locale to eliminate parsing errors (i.e. using commas as delimiters rather than periods) 29 | if locale -a 2>/dev/null | grep ^C$ > /dev/null; then 30 | # locale "C" installed 31 | export LC_ALL=C 32 | else 33 | # locale "C" not installed, display warning 34 | echo -e "\nWarning: locale 'C' not detected. Test outputs may not be parsed correctly." 35 | fi 36 | 37 | # determine architecture of host 38 | ARCH=$(uname -m) 39 | if [[ $ARCH = *x86_64* ]]; then 40 | # host is running a 64-bit kernel 41 | ARCH="x64" 42 | elif [[ $ARCH = *i?86* ]]; then 43 | # host is running a 32-bit kernel 44 | ARCH="x86" 45 | elif [[ $ARCH = *aarch* || $ARCH = *arm* ]]; then 46 | KERNEL_BIT=$(getconf LONG_BIT) 47 | if [[ $KERNEL_BIT = *64* ]]; then 48 | # host is running an ARM 64-bit kernel 49 | ARCH="aarch64" 50 | else 51 | # host is running an ARM 32-bit kernel 52 | ARCH="arm" 53 | fi 54 | echo -e "\nARM compatibility is considered *experimental*" 55 | else 56 | # host is running a non-supported kernel 57 | echo -e "Architecture not supported by YABS." 58 | exit 1 59 | fi 60 | 61 | # flags to skip certain performance tests 62 | 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 63 | GEEKBENCH_6="True" # gb6 test enabled by default 64 | 65 | # get any arguments that were passed to the script and set the associated skip flags (if applicable) 66 | while getopts 'bfdignhr4596jw:s:' flag; do 67 | case "${flag}" in 68 | b) PREFER_BIN="True" ;; 69 | f) SKIP_FIO="True" ;; 70 | d) SKIP_FIO="True" ;; 71 | i) SKIP_IPERF="True" ;; 72 | g) SKIP_GEEKBENCH="True" && unset GEEKBENCH_6 ;; 73 | n) SKIP_NET="True" ;; 74 | h) PRINT_HELP="True" ;; 75 | r) REDUCE_NET="True" ;; 76 | 4) GEEKBENCH_4="True" && unset GEEKBENCH_6 ;; 77 | 5) GEEKBENCH_5="True" && unset GEEKBENCH_6 ;; 78 | 9) GEEKBENCH_4="True" && GEEKBENCH_5="True" && unset GEEKBENCH_6 ;; 79 | 6) GEEKBENCH_6="True" ;; 80 | j) JSON+="j" ;; 81 | w) JSON+="w" && JSON_FILE=${OPTARG} ;; 82 | s) JSON+="s" && JSON_SEND=${OPTARG} ;; 83 | *) exit 1 ;; 84 | esac 85 | done 86 | 87 | # check for local fio/iperf installs 88 | if command -v fio >/dev/null 2>&1; then 89 | LOCAL_FIO=true 90 | else 91 | unset LOCAL_FIO 92 | fi 93 | 94 | if command -v iperf3 >/dev/null 2>&1; then 95 | LOCAL_IPERF=true 96 | else 97 | unset LOCAL_IPERF 98 | fi 99 | 100 | # check for ping 101 | if command -v ping >/dev/null 2>&1; then 102 | LOCAL_PING=true 103 | else 104 | unset LOCAL_PING 105 | fi 106 | 107 | # check for curl/wget 108 | if command -v curl >/dev/null 2>&1; then 109 | LOCAL_CURL=true 110 | else 111 | unset LOCAL_CURL 112 | fi 113 | 114 | 115 | # test if the host has IPv4/IPv6 connectivity 116 | [[ -n $LOCAL_CURL ]] && IP_CHECK_CMD="curl -s -m 4" || IP_CHECK_CMD="wget -qO- -T 4" 117 | 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) 118 | 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) 119 | if [[ -z "$IPV4_CHECK" && -z "$IPV6_CHECK" ]]; then 120 | echo -e 121 | echo -e "Warning: Both IPv4 AND IPv6 connectivity were not detected. Check for DNS issues..." 122 | fi 123 | 124 | # print help and exit script, if help flag was passed 125 | if [ -n "$PRINT_HELP" ]; then 126 | echo -e 127 | echo -e "Usage: ./yabs.sh [-flags]" 128 | echo -e " curl -sL yabs.sh | bash" 129 | echo -e " curl -sL yabs.sh | bash -s -- -flags" 130 | echo -e " wget -qO- yabs.sh | bash" 131 | echo -e " wget -qO- yabs.sh | bash -s -- -flags" 132 | echo -e 133 | echo -e "Flags:" 134 | echo -e " -b : prefer pre-compiled binaries from repo over local packages" 135 | echo -e " -f/d : skips the fio disk benchmark test" 136 | echo -e " -i : skips the iperf network test" 137 | echo -e " -g : skips the geekbench performance test" 138 | echo -e " -n : skips the network information lookup and print out" 139 | echo -e " -h : prints this lovely message, shows any flags you passed," 140 | echo -e " shows if fio/iperf3 local packages have been detected," 141 | echo -e " then exits" 142 | echo -e " -r : reduce number of iperf3 network locations (to only three)" 143 | echo -e " to lessen bandwidth usage" 144 | echo -e " -4 : use geekbench 4 instead of geekbench 6" 145 | echo -e " -5 : use geekbench 5 instead of geekbench 6" 146 | echo -e " -9 : use both geekbench 4 AND geekbench 5 instead of geekbench 6" 147 | echo -e " -6 : use geekbench 6 in addition to 4 and/or 5 (only needed if -4, -5, or -9 are set; -6 must come last)" 148 | echo -e " -j : print jsonified YABS results at conclusion of test" 149 | echo -e " -w : write jsonified YABS results to disk using file name provided" 150 | echo -e " -s : send jsonified YABS results to URL" 151 | echo -e 152 | echo -e "Detected Arch: $ARCH" 153 | echo -e 154 | echo -e "Detected Flags:" 155 | [[ -n $PREFER_BIN ]] && echo -e " -b, force using precompiled binaries from repo" 156 | [[ -n $SKIP_FIO ]] && echo -e " -f/d, skipping fio disk benchmark test" 157 | [[ -n $SKIP_IPERF ]] && echo -e " -i, skipping iperf network test" 158 | [[ -n $SKIP_GEEKBENCH ]] && echo -e " -g, skipping geekbench test" 159 | [[ -n $SKIP_NET ]] && echo -e " -n, skipping network info lookup and print out" 160 | [[ -n $REDUCE_NET ]] && echo -e " -r, using reduced (3) iperf3 locations" 161 | [[ -n $GEEKBENCH_4 ]] && echo -e " running geekbench 4" 162 | [[ -n $GEEKBENCH_5 ]] && echo -e " running geekbench 5" 163 | [[ -n $GEEKBENCH_6 ]] && echo -e " running geekbench 6" 164 | echo -e 165 | echo -e "Local Binary Check:" 166 | ([[ -z $LOCAL_FIO ]] && echo -e " fio not detected, will download precompiled binary") || 167 | ([[ -z $PREFER_BIN ]] && echo -e " fio detected, using local package") || 168 | echo -e " fio detected, but using precompiled binary instead" 169 | ([[ -z $LOCAL_IPERF ]] && echo -e " iperf3 not detected, will download precompiled binary") || 170 | ([[ -z $PREFER_BIN ]] && echo -e " iperf3 detected, using local package") || 171 | echo -e " iperf3 detected, but using precompiled binary instead" 172 | echo -e 173 | echo -e "Detected Connectivity:" 174 | [[ -n $IPV4_CHECK ]] && echo -e " IPv4 connected" || 175 | echo -e " IPv4 not connected" 176 | [[ -n $IPV6_CHECK ]] && echo -e " IPv6 connected" || 177 | echo -e " IPv6 not connected" 178 | echo -e 179 | echo -e "JSON Options:" 180 | [[ -z $JSON ]] && echo -e " none" 181 | [[ $JSON = *j* ]] && echo -e " printing json to screen after test" 182 | [[ $JSON = *w* ]] && echo -e " writing json to file ($JSON_FILE) after test" 183 | [[ $JSON = *s* ]] && echo -e " sharing json YABS results to $JSON_SEND" 184 | echo -e 185 | echo -e "Exiting..." 186 | 187 | exit 0 188 | fi 189 | 190 | # format_size 191 | # Purpose: Formats raw disk and memory sizes from kibibytes (KiB) to largest unit 192 | # Parameters: 193 | # 1. RAW - the raw memory size (RAM/Swap) in kibibytes 194 | # Returns: 195 | # Formatted memory size in KiB, MiB, GiB, or TiB 196 | function format_size { 197 | RAW=$1 # mem size in KiB 198 | RESULT=$RAW 199 | local DENOM=1 200 | local UNIT="KiB" 201 | 202 | # ensure the raw value is a number, otherwise return blank 203 | re='^[0-9]+$' 204 | if ! [[ $RAW =~ $re ]] ; then 205 | echo "" 206 | return 0 207 | fi 208 | 209 | if [ "$RAW" -ge 1073741824 ]; then 210 | DENOM=1073741824 211 | UNIT="TiB" 212 | elif [ "$RAW" -ge 1048576 ]; then 213 | DENOM=1048576 214 | UNIT="GiB" 215 | elif [ "$RAW" -ge 1024 ]; then 216 | DENOM=1024 217 | UNIT="MiB" 218 | fi 219 | 220 | # divide the raw result to get the corresponding formatted result (based on determined unit) 221 | RESULT=$(awk -v a="$RESULT" -v b="$DENOM" 'BEGIN { print a / b }') 222 | # shorten the formatted result to two decimal places (i.e. x.x) 223 | RESULT=$(echo "$RESULT" | awk -F. '{ printf "%0.1f",$1"."substr($2,1,2) }') 224 | # concat formatted result value with units and return result 225 | RESULT="$RESULT $UNIT" 226 | echo "$RESULT" 227 | } 228 | 229 | # gather basic system information (inc. CPU, AES-NI/virt status, RAM + swap + disk size) 230 | echo -e 231 | echo -e "Basic System Information:" 232 | echo -e "---------------------------------" 233 | 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"}') 234 | echo -e "Uptime : $UPTIME" 235 | # check for local lscpu installs 236 | if command -v lscpu >/dev/null 2>&1; then 237 | LOCAL_LSCPU=true 238 | else 239 | unset LOCAL_LSCPU 240 | fi 241 | if [[ $ARCH = *aarch64* || $ARCH = *arm* ]] && [[ -n $LOCAL_LSCPU ]]; then 242 | CPU_PROC=$(lscpu | grep "Model name" | sed 's/Model name: *//g') 243 | else 244 | CPU_PROC=$(awk -F: '/model name/ {name=$2} END {print name}' /proc/cpuinfo | sed 's/^[ \t]*//;s/[ \t]*$//') 245 | fi 246 | echo -e "Processor : $CPU_PROC" 247 | if [[ $ARCH = *aarch64* || $ARCH = *arm* ]] && [[ -n $LOCAL_LSCPU ]]; then 248 | CPU_CORES=$(lscpu | grep "^[[:blank:]]*CPU(s):" | sed 's/CPU(s): *//g') 249 | CPU_FREQ=$(lscpu | grep "CPU max MHz" | sed 's/CPU max MHz: *//g') 250 | [[ -z "$CPU_FREQ" ]] && CPU_FREQ="???" 251 | CPU_FREQ="${CPU_FREQ} MHz" 252 | else 253 | CPU_CORES=$(awk -F: '/model name/ {core++} END {print core}' /proc/cpuinfo) 254 | CPU_FREQ=$(awk -F: ' /cpu MHz/ {freq=$2} END {print freq " MHz"}' /proc/cpuinfo | sed 's/^[ \t]*//;s/[ \t]*$//') 255 | fi 256 | echo -e "CPU cores : $CPU_CORES @ $CPU_FREQ" 257 | CPU_AES=$(grep aes /proc/cpuinfo) 258 | [[ -z "$CPU_AES" ]] && CPU_AES="\xE2\x9D\x8C Disabled" || CPU_AES="\xE2\x9C\x94 Enabled" 259 | echo -e "AES-NI : $CPU_AES" 260 | CPU_VIRT=$(grep 'vmx\|svm' /proc/cpuinfo) 261 | [[ -z "$CPU_VIRT" ]] && CPU_VIRT="\xE2\x9D\x8C Disabled" || CPU_VIRT="\xE2\x9C\x94 Enabled" 262 | echo -e "VM-x/AMD-V : $CPU_VIRT" 263 | TOTAL_RAM_RAW=$(free | awk 'NR==2 {print $2}') 264 | TOTAL_RAM=$(format_size "$TOTAL_RAM_RAW") 265 | echo -e "RAM : $TOTAL_RAM" 266 | TOTAL_SWAP_RAW=$(free | grep Swap | awk '{ print $2 }') 267 | TOTAL_SWAP=$(format_size "$TOTAL_SWAP_RAW") 268 | echo -e "Swap : $TOTAL_SWAP" 269 | # total disk size is calculated by adding all partitions of the types listed below (after the -t flags) 270 | TOTAL_DISK_RAW=$(df -t simfs -t ext2 -t ext3 -t ext4 -t btrfs -t xfs -t vfat -t exfat -t ntfs -t swap --total 2>/dev/null | grep total | awk '{ print $2 }') 271 | TOTAL_DISK=$(format_size "$TOTAL_DISK_RAW") 272 | echo -e "Disk : $TOTAL_DISK" 273 | DISTRO=$(grep 'PRETTY_NAME' /etc/os-release | cut -d '"' -f 2 ) 274 | echo -e "Distro : $DISTRO" 275 | KERNEL=$(uname -r) 276 | echo -e "Kernel : $KERNEL" 277 | VIRT=$(systemd-detect-virt 2>/dev/null) 278 | VIRT=${VIRT^^} || VIRT="UNKNOWN" 279 | echo -e "VM Type : $VIRT" 280 | [[ -z "$IPV4_CHECK" ]] && ONLINE="\xE2\x9D\x8C Offline / " || ONLINE="\xE2\x9C\x94 Online / " 281 | [[ -z "$IPV6_CHECK" ]] && ONLINE+="\xE2\x9D\x8C Offline" || ONLINE+="\xE2\x9C\x94 Online" 282 | echo -e "IPv4/IPv6 : $ONLINE" 283 | 284 | # Function to get information from IP Address using ip-api.com free API 285 | function ip_info() { 286 | # check for curl vs wget 287 | [[ -n $LOCAL_CURL ]] && DL_CMD="curl -s" || DL_CMD="wget -qO-" 288 | 289 | # declare local vars 290 | local ip6me_resp net_type net_ip response country region region_code city isp org as 291 | 292 | ip6me_resp="$($DL_CMD http://ip6.me/api/)" 293 | net_type="$(echo "$ip6me_resp" | cut -d, -f1)" 294 | net_ip="$(echo "$ip6me_resp" | cut -d, -f2)" 295 | 296 | response=$($DL_CMD http://ip-api.com/json/"$net_ip") 297 | 298 | # if no response, skip output 299 | if [[ -z $response ]]; then 300 | return 301 | fi 302 | 303 | country=$(echo "$response" | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^country/ {print $2}' | head -1 | sed 's/^"\(.*\)"$/\1/') 304 | region=$(echo "$response" | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^regionName/ {print $2}' | sed 's/^"\(.*\)"$/\1/') 305 | region_code=$(echo "$response" | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^region/ {print $2}' | head -1 | sed 's/^"\(.*\)"$/\1/') 306 | city=$(echo "$response" | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^city/ {print $2}' | sed 's/^"\(.*\)"$/\1/') 307 | isp=$(echo "$response" | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^isp/ {print $2}' | sed 's/^"\(.*\)"$/\1/') 308 | org=$(echo "$response" | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^org/ {print $2}' | sed 's/^"\(.*\)"$/\1/') 309 | as=$(echo "$response" | sed -e 's/[{}]/''/g' | awk -v RS=',"' -F: '/^as/ {print $2}' | sed 's/^"\(.*\)"$/\1/') 310 | 311 | echo 312 | echo "$net_type Network Information:" 313 | echo "---------------------------------" 314 | 315 | if [[ -n "$isp" ]]; then 316 | echo "ISP : $isp" 317 | else 318 | echo "ISP : Unknown" 319 | fi 320 | if [[ -n "$as" ]]; then 321 | echo "ASN : $as" 322 | else 323 | echo "ASN : Unknown" 324 | fi 325 | if [[ -n "$org" ]]; then 326 | echo "Host : $org" 327 | fi 328 | if [[ -n "$city" && -n "$region" ]]; then 329 | echo "Location : $city, $region ($region_code)" 330 | fi 331 | if [[ -n "$country" ]]; then 332 | echo "Country : $country" 333 | fi 334 | 335 | [[ -n $JSON ]] && JSON_RESULT+=',"ip_info":{"protocol":"'$net_type'","isp":"'$isp'","asn":"'$as'","org":"'$org'","city":"'$city'","region":"'$region'","region_code":"'$region_code'","country":"'$country'"}' 336 | } 337 | 338 | if [[ -n $JSON ]]; then 339 | UPTIME_S=$(awk '{print $1}' /proc/uptime) 340 | IPV4=$([ -n "$IPV4_CHECK" ] && echo "true" || echo "false") 341 | IPV6=$([ -n "$IPV6_CHECK" ] && echo "true" || echo "false") 342 | AES=$([[ "$CPU_AES" = *Enabled* ]] && echo "true" || echo "false") 343 | CPU_VIRT_BOOL=$([[ "$CPU_VIRT" = *Enabled* ]] && echo "true" || echo "false") 344 | JSON_RESULT='{"version":"'$YABS_VERSION'","time":"'$TIME_START'","os":{"arch":"'$ARCH'","distro":"'$DISTRO'","kernel":"'$KERNEL'",' 345 | JSON_RESULT+='"uptime":'$UPTIME_S',"vm":"'$VIRT'"},"net":{"ipv4":'$IPV4',"ipv6":'$IPV6'},"cpu":{"model":"'$CPU_PROC'","cores":'$CPU_CORES',' 346 | 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"}' 347 | fi 348 | 349 | if [ -z $SKIP_NET ]; then 350 | ip_info 351 | fi 352 | 353 | # create a directory in the same location that the script is being run to temporarily store YABS-related files 354 | DATE=$(date -Iseconds | sed -e "s/:/_/g") 355 | YABS_PATH=./$DATE 356 | touch "$DATE.test" 2> /dev/null 357 | # test if the user has write permissions in the current directory and exit if not 358 | if [ ! -f "$DATE.test" ]; then 359 | echo -e 360 | echo -e "You do not have write permission in this directory. Switch to an owned directory and re-run the script.\nExiting..." 361 | exit 1 362 | fi 363 | rm "$DATE.test" 364 | mkdir -p "$YABS_PATH" 365 | 366 | # trap CTRL+C signals to exit script cleanly 367 | trap catch_abort INT 368 | 369 | # catch_abort 370 | # Purpose: This method will catch CTRL+C signals in order to exit the script cleanly and remove 371 | # yabs-related files. 372 | function catch_abort() { 373 | echo -e "\n** Aborting YABS. Cleaning up files...\n" 374 | rm -rf "$YABS_PATH" 375 | unset LC_ALL 376 | exit 0 377 | } 378 | 379 | # format_speed 380 | # Purpose: This method is a convenience function to format the output of the fio disk tests which 381 | # always returns a result in KB/s. If result is >= 1 GB/s, use GB/s. If result is < 1 GB/s 382 | # and >= 1 MB/s, then use MB/s. Otherwise, use KB/s. 383 | # Parameters: 384 | # 1. RAW - the raw disk speed result (in KB/s) 385 | # Returns: 386 | # Formatted disk speed in GB/s, MB/s, or KB/s 387 | function format_speed { 388 | RAW=$1 # disk speed in KB/s 389 | RESULT=$RAW 390 | local DENOM=1 391 | local UNIT="KB/s" 392 | 393 | # ensure raw value is not null, if it is, return blank 394 | if [ -z "$RAW" ]; then 395 | echo "" 396 | return 0 397 | fi 398 | 399 | # check if disk speed >= 1 GB/s 400 | if [ "$RAW" -ge 1000000 ]; then 401 | DENOM=1000000 402 | UNIT="GB/s" 403 | # check if disk speed < 1 GB/s && >= 1 MB/s 404 | elif [ "$RAW" -ge 1000 ]; then 405 | DENOM=1000 406 | UNIT="MB/s" 407 | fi 408 | 409 | # divide the raw result to get the corresponding formatted result (based on determined unit) 410 | RESULT=$(awk -v a="$RESULT" -v b="$DENOM" 'BEGIN { print a / b }') 411 | # shorten the formatted result to two decimal places (i.e. x.xx) 412 | RESULT=$(echo "$RESULT" | awk -F. '{ printf "%0.2f",$1"."substr($2,1,2) }') 413 | # concat formatted result value with units and return result 414 | RESULT="$RESULT $UNIT" 415 | echo "$RESULT" 416 | } 417 | 418 | # format_iops 419 | # Purpose: This method is a convenience function to format the output of the raw IOPS result 420 | # Parameters: 421 | # 1. RAW - the raw IOPS result 422 | # Returns: 423 | # Formatted IOPS (i.e. 8, 123, 1.7k, 275.9k, etc.) 424 | function format_iops { 425 | RAW=$1 # iops 426 | RESULT=$RAW 427 | 428 | # ensure raw value is not null, if it is, return blank 429 | if [ -z "$RAW" ]; then 430 | echo "" 431 | return 0 432 | fi 433 | 434 | # check if IOPS speed > 1k 435 | if [ "$RAW" -ge 1000 ]; then 436 | # divide the raw result by 1k 437 | RESULT=$(awk -v a="$RESULT" 'BEGIN { print a / 1000 }') 438 | # shorten the formatted result to one decimal place (i.e. x.x) 439 | RESULT=$(echo "$RESULT" | awk -F. '{ printf "%0.1f",$1"."substr($2,1,1) }') 440 | RESULT="$RESULT"k 441 | fi 442 | 443 | echo "$RESULT" 444 | } 445 | 446 | # disk_test 447 | # Purpose: This method is designed to test the disk performance of the host using the partition that the 448 | # script is being run from using fio random read/write speed tests. 449 | # Parameters: 450 | # - (none) 451 | function disk_test { 452 | if [[ "$ARCH" = "aarch64" || "$ARCH" = "arm" ]]; then 453 | FIO_SIZE=512M 454 | else 455 | FIO_SIZE=2G 456 | fi 457 | 458 | # run a quick test to generate the fio test file to be used by the actual tests 459 | echo -en "Generating fio test file..." 460 | $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 461 | echo -en "\r\033[0K" 462 | 463 | # get array of block sizes to evaluate 464 | BLOCK_SIZES=("$@") 465 | 466 | for BS in "${BLOCK_SIZES[@]}"; do 467 | # run rand read/write mixed fio test with block size = $BS 468 | echo -en "Running fio random mixed R+W disk test with $BS block size..." 469 | 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") 470 | DISK_IOPS_R=$(echo "$DISK_TEST" | awk -F';' '{print $8}') 471 | DISK_IOPS_W=$(echo "$DISK_TEST" | awk -F';' '{print $49}') 472 | DISK_IOPS=$(awk -v a="$DISK_IOPS_R" -v b="$DISK_IOPS_W" 'BEGIN { print a + b }') 473 | DISK_TEST_R=$(echo "$DISK_TEST" | awk -F';' '{print $7}') 474 | DISK_TEST_W=$(echo "$DISK_TEST" | awk -F';' '{print $48}') 475 | DISK_TEST=$(awk -v a="$DISK_TEST_R" -v b="$DISK_TEST_W" 'BEGIN { print a + b }') 476 | DISK_RESULTS_RAW+=( "$DISK_TEST" "$DISK_TEST_R" "$DISK_TEST_W" "$DISK_IOPS" "$DISK_IOPS_R" "$DISK_IOPS_W" ) 477 | 478 | DISK_IOPS=$(format_iops "$DISK_IOPS") 479 | DISK_IOPS_R=$(format_iops "$DISK_IOPS_R") 480 | DISK_IOPS_W=$(format_iops "$DISK_IOPS_W") 481 | DISK_TEST=$(format_speed "$DISK_TEST") 482 | DISK_TEST_R=$(format_speed "$DISK_TEST_R") 483 | DISK_TEST_W=$(format_speed "$DISK_TEST_W") 484 | 485 | DISK_RESULTS+=( "$DISK_TEST" "$DISK_TEST_R" "$DISK_TEST_W" "$DISK_IOPS" "$DISK_IOPS_R" "$DISK_IOPS_W" ) 486 | echo -en "\r\033[0K" 487 | done 488 | } 489 | 490 | # dd_test 491 | # Purpose: This method is invoked if the fio disk test failed. dd sequential speed tests are 492 | # not indiciative or real-world results, however, some form of disk speed measure 493 | # is better than nothing. 494 | # Parameters: 495 | # - (none) 496 | function dd_test { 497 | I=0 498 | DISK_WRITE_TEST_RES=() 499 | DISK_READ_TEST_RES=() 500 | DISK_WRITE_TEST_AVG=0 501 | DISK_READ_TEST_AVG=0 502 | 503 | # run the disk speed tests (write and read) thrice over 504 | while [ $I -lt 3 ] 505 | do 506 | # write test using dd, "direct" flag is used to test direct I/O for data being stored to disk 507 | 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)}') 508 | VAL=$(echo "$DISK_WRITE_TEST" | cut -d " " -f 1) 509 | [[ "$DISK_WRITE_TEST" == *"GB"* ]] && VAL=$(awk -v a="$VAL" 'BEGIN { print a * 1000 }') 510 | DISK_WRITE_TEST_RES+=( "$DISK_WRITE_TEST" ) 511 | DISK_WRITE_TEST_AVG=$(awk -v a="$DISK_WRITE_TEST_AVG" -v b="$VAL" 'BEGIN { print a + b }') 512 | 513 | # read test using dd using the 1G file written during the write test 514 | DISK_READ_TEST=$(dd if="$DISK_PATH/$DATE.test" of=/dev/null bs=8k |& grep copied | awk '{ print $(NF-1) " " $(NF)}') 515 | VAL=$(echo "$DISK_READ_TEST" | cut -d " " -f 1) 516 | [[ "$DISK_READ_TEST" == *"GB"* ]] && VAL=$(awk -v a="$VAL" 'BEGIN { print a * 1000 }') 517 | DISK_READ_TEST_RES+=( "$DISK_READ_TEST" ) 518 | DISK_READ_TEST_AVG=$(awk -v a="$DISK_READ_TEST_AVG" -v b="$VAL" 'BEGIN { print a + b }') 519 | 520 | I=$(( I + 1 )) 521 | done 522 | # calculate the write and read speed averages using the results from the three runs 523 | DISK_WRITE_TEST_AVG=$(awk -v a="$DISK_WRITE_TEST_AVG" 'BEGIN { print a / 3 }') 524 | DISK_READ_TEST_AVG=$(awk -v a="$DISK_READ_TEST_AVG" 'BEGIN { print a / 3 }') 525 | } 526 | 527 | # check if disk performance is being tested and the host has required space (2G) 528 | AVAIL_SPACE=$(df -k . | awk 'NR==2{print $4}') 529 | if [[ -z "$SKIP_FIO" && "$AVAIL_SPACE" -lt 2097152 && "$ARCH" != "aarch64" && "$ARCH" != "arm" ]]; then # 2GB = 2097152KB 530 | echo -e "\nLess than 2GB of space available. Skipping disk test..." 531 | elif [[ -z "$SKIP_FIO" && "$AVAIL_SPACE" -lt 524288 && ("$ARCH" = "aarch64" || "$ARCH" = "arm") ]]; then # 512MB = 524288KB 532 | echo -e "\nLess than 512MB of space available. Skipping disk test..." 533 | # if the skip disk flag was set, skip the disk performance test, otherwise test disk performance 534 | elif [ -z "$SKIP_FIO" ]; then 535 | # Perform ZFS filesystem detection and determine if we have enough free space according to spa_asize_inflation 536 | ZFSCHECK="/sys/module/zfs/parameters/spa_asize_inflation" 537 | if [[ -f "$ZFSCHECK" ]];then 538 | mul_spa=$(( $(cat /sys/module/zfs/parameters/spa_asize_inflation) * 2 )) 539 | warning=0 540 | poss=() 541 | 542 | for pathls in $(df -Th | awk '{print $7}' | tail -n +2) 543 | do 544 | if [[ "${PWD##"$pathls"}" != "$PWD" ]]; then 545 | poss+=("$pathls") 546 | fi 547 | done 548 | 549 | long="" 550 | m=-1 551 | for x in "${poss[@]}" 552 | do 553 | if [ "${#x}" -gt "$m" ];then 554 | m=${#x} 555 | long=$x 556 | fi 557 | done 558 | 559 | size_b=$(df -Th | grep -w "$long" | grep -i zfs | awk '{print $5}' | tail -c -2 | head -c 1) 560 | free_space=$(df -Th | grep -w "$long" | grep -i zfs | awk '{print $5}' | head -c -2) 561 | 562 | if [[ $size_b == 'T' ]]; then 563 | free_space=$(awk "BEGIN {print int($free_space * 1024)}") 564 | size_b='G' 565 | fi 566 | 567 | if [[ $(df -Th | grep -w "$long") == *"zfs"* ]];then 568 | 569 | if [[ $size_b == 'G' ]]; then 570 | if ((free_space < mul_spa)); then 571 | warning=1 572 | fi 573 | else 574 | warning=1 575 | fi 576 | 577 | fi 578 | 579 | if [[ $warning -eq 1 ]];then 580 | 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" 581 | fi 582 | fi 583 | 584 | echo -en "\nPreparing system for disk tests..." 585 | 586 | # create temp directory to store disk write/read test files 587 | DISK_PATH=$YABS_PATH/disk 588 | mkdir -p "$DISK_PATH" 589 | 590 | if [[ -z "$PREFER_BIN" && -n "$LOCAL_FIO" ]]; then # local fio has been detected, use instead of pre-compiled binary 591 | FIO_CMD=fio 592 | else 593 | # download fio binary 594 | if [[ -n $LOCAL_CURL ]]; then 595 | 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" 596 | else 597 | 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" 598 | fi 599 | 600 | if [ ! -f "$DISK_PATH/fio" ]; then # ensure fio binary download successfully 601 | echo -en "\r\033[0K" 602 | echo -e "Fio binary download failed. Running dd test as fallback...." 603 | DD_FALLBACK=True 604 | else 605 | chmod +x "$DISK_PATH/fio" 606 | FIO_CMD=$DISK_PATH/fio 607 | fi 608 | fi 609 | 610 | if [ -z "$DD_FALLBACK" ]; then # if not falling back on dd tests, run fio test 611 | echo -en "\r\033[0K" 612 | 613 | # init global array to store disk performance values 614 | declare -a DISK_RESULTS DISK_RESULTS_RAW 615 | # disk block sizes to evaluate 616 | BLOCK_SIZES=( "4k" "64k" "512k" "1m" ) 617 | 618 | # execute disk performance test 619 | disk_test "${BLOCK_SIZES[@]}" 620 | fi 621 | 622 | if [[ -n "$DD_FALLBACK" || ${#DISK_RESULTS[@]} -eq 0 ]]; then # fio download failed or test was killed or returned an error, run dd test instead 623 | if [ -z "$DD_FALLBACK" ]; then # print error notice if ended up here due to fio error 624 | echo -e "fio disk speed tests failed. Run manually to determine cause.\nRunning dd test as fallback..." 625 | fi 626 | 627 | dd_test 628 | 629 | # format the speed averages by converting to GB/s if > 1000 MB/s 630 | if [ "$(echo "$DISK_WRITE_TEST_AVG" | cut -d "." -f 1)" -ge 1000 ]; then 631 | DISK_WRITE_TEST_AVG=$(awk -v a="$DISK_WRITE_TEST_AVG" 'BEGIN { print a / 1000 }') 632 | DISK_WRITE_TEST_UNIT="GB/s" 633 | else 634 | DISK_WRITE_TEST_UNIT="MB/s" 635 | fi 636 | if [ "$(echo "$DISK_READ_TEST_AVG" | cut -d "." -f 1)" -ge 1000 ]; then 637 | DISK_READ_TEST_AVG=$(awk -v a="$DISK_READ_TEST_AVG" 'BEGIN { print a / 1000 }') 638 | DISK_READ_TEST_UNIT="GB/s" 639 | else 640 | DISK_READ_TEST_UNIT="MB/s" 641 | fi 642 | 643 | # print dd sequential disk speed test results 644 | echo -e 645 | echo -e "dd Sequential Disk Speed Tests:" 646 | echo -e "---------------------------------" 647 | printf "%-6s | %-6s %-4s | %-6s %-4s | %-6s %-4s | %-6s %-4s\n" "" "Test 1" "" "Test 2" "" "Test 3" "" "Avg" "" 648 | printf "%-6s | %-6s %-4s | %-6s %-4s | %-6s %-4s | %-6s %-4s\n" "" "" "" "" "" "" "" "" "" 649 | 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}" 650 | 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}" 651 | else # fio tests completed successfully, print results 652 | CURRENT_PARTITION=$(df -P . 2>/dev/null | tail -1 | cut -d' ' -f 1) 653 | [[ -n $JSON ]] && JSON_RESULT+=',"partition":"'$CURRENT_PARTITION'","fio":[' 654 | DISK_RESULTS_NUM=$((${#DISK_RESULTS[@]} / 6)) 655 | DISK_COUNT=0 656 | 657 | # print disk speed test results 658 | echo -e "fio Disk Speed Tests (Mixed R/W 50/50) (Partition $CURRENT_PARTITION):" 659 | echo -e "---------------------------------" 660 | 661 | while [[ $DISK_COUNT -lt $DISK_RESULTS_NUM ]] ; do 662 | if [[ $DISK_COUNT -gt 0 ]]; then printf "%-10s | %-20s | %-20s\n" "" "" ""; fi 663 | printf "%-10s | %-11s %8s | %-11s %8s\n" "Block Size" "${BLOCK_SIZES[DISK_COUNT]}" "(IOPS)" "${BLOCK_SIZES[DISK_COUNT+1]}" "(IOPS)" 664 | printf "%-10s | %-11s %8s | %-11s %8s\n" " ------" "---" "---- " "----" "---- " 665 | 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]})" 666 | 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]})" 667 | 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]})" 668 | if [[ -n $JSON ]]; then 669 | 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]} 670 | 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]} 671 | JSON_RESULT+=',"iops_rw":'${DISK_RESULTS_RAW[DISK_COUNT*6+3]}',"speed_units":"KBps"},' 672 | 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]} 673 | 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]} 674 | JSON_RESULT+=',"iops_rw":'${DISK_RESULTS_RAW[(DISK_COUNT+1)*6+3]}',"speed_units":"KBps"},' 675 | fi 676 | DISK_COUNT=$((DISK_COUNT + 2)) 677 | done 678 | [[ -n $JSON ]] && JSON_RESULT=${JSON_RESULT::${#JSON_RESULT}-1} && JSON_RESULT+=']' 679 | fi 680 | fi 681 | 682 | # iperf_test 683 | # Purpose: This method is designed to test the network performance of the host by executing an 684 | # iperf3 test to/from the public iperf server passed to the function. Both directions 685 | # (send and receive) are tested. 686 | # Parameters: 687 | # 1. URL - URL/domain name of the iperf server 688 | # 2. PORTS - the range of ports on which the iperf server operates 689 | # 3. HOST - the friendly name of the iperf server host/owner 690 | # 4. FLAGS - any flags that should be passed to the iperf command 691 | function iperf_test { 692 | URL=$1 693 | PORTS=$2 694 | HOST=$3 695 | FLAGS=$4 696 | 697 | # attempt the iperf send test 3 times, allowing for a slot to become available on the 698 | # server or to throw out any bad/error results 699 | I=1 700 | while [ $I -le 3 ] 701 | do 702 | echo -en "Performing $MODE iperf3 send test to $HOST (Attempt #$I of 3)..." 703 | # select a random iperf port from the range provided 704 | PORT=$(shuf -i "$PORTS" -n 1) 705 | # run the iperf test sending data from the host to the iperf server; includes 706 | # a timeout of 15s in case the iperf server is not responding; uses 8 parallel 707 | # threads for the network test 708 | IPERF_RUN_SEND="$(timeout 15 "$IPERF_CMD" "$FLAGS" -c "$URL" -p "$PORT" -P 8 2> /dev/null)" 709 | # check if iperf exited cleanly and did not return an error 710 | if [[ "$IPERF_RUN_SEND" == *"receiver"* && "$IPERF_RUN_SEND" != *"error"* ]]; then 711 | # test did not result in an error, parse speed result 712 | SPEED=$(echo "${IPERF_RUN_SEND}" | grep SUM | grep receiver | awk '{ print $6 }') 713 | # if speed result is blank or bad (0.00), rerun, otherwise set counter to exit loop 714 | [[ -z $SPEED || "$SPEED" == "0.00" ]] && I=$(( I + 1 )) || I=11 715 | else 716 | # if iperf server is not responding, set counter to exit, otherwise increment, sleep, and rerun 717 | [[ "$IPERF_RUN_SEND" == *"unable to connect"* ]] && I=11 || I=$(( I + 1 )) && sleep 2 718 | fi 719 | echo -en "\r\033[0K" 720 | done 721 | 722 | # small sleep necessary to give iperf server a breather to get ready for a new test 723 | sleep 1 724 | 725 | # attempt the iperf receive test 3 times, allowing for a slot to become available on 726 | # the server or to throw out any bad/error results 727 | J=1 728 | while [ $J -le 3 ] 729 | do 730 | echo -n "Performing $MODE iperf3 recv test from $HOST (Attempt #$J of 3)..." 731 | # select a random iperf port from the range provided 732 | PORT=$(shuf -i "$PORTS" -n 1) 733 | # run the iperf test receiving data from the iperf server to the host; includes 734 | # a timeout of 15s in case the iperf server is not responding; uses 8 parallel 735 | # threads for the network test 736 | IPERF_RUN_RECV="$(timeout 15 "$IPERF_CMD" "$FLAGS" -c "$URL" -p "$PORT" -P 8 -R 2> /dev/null)" 737 | # check if iperf exited cleanly and did not return an error 738 | if [[ "$IPERF_RUN_RECV" == *"receiver"* && "$IPERF_RUN_RECV" != *"error"* ]]; then 739 | # test did not result in an error, parse speed result 740 | SPEED=$(echo "${IPERF_RUN_RECV}" | grep SUM | grep receiver | awk '{ print $6 }') 741 | # if speed result is blank or bad (0.00), rerun, otherwise set counter to exit loop 742 | [[ -z $SPEED || "$SPEED" == "0.00" ]] && J=$(( J + 1 )) || J=11 743 | else 744 | # if iperf server is not responding, set counter to exit, otherwise increment, sleep, and rerun 745 | [[ "$IPERF_RUN_RECV" == *"unable to connect"* ]] && J=11 || J=$(( J + 1 )) && sleep 2 746 | fi 747 | echo -en "\r\033[0K" 748 | done 749 | 750 | # Run a latency test via ping -c1 command -> will return "xx.x ms" 751 | [[ -n $LOCAL_PING ]] && LATENCY_RUN="$(ping -c1 "$URL" 2>/dev/null | grep -o 'time=.*' | sed s/'time='//)" 752 | [[ -z $LATENCY_RUN ]] && LATENCY_RUN="--" 753 | 754 | # parse the resulting send and receive speed results 755 | IPERF_SENDRESULT="$(echo "${IPERF_RUN_SEND}" | grep SUM | grep receiver)" 756 | IPERF_RECVRESULT="$(echo "${IPERF_RUN_RECV}" | grep SUM | grep receiver)" 757 | LATENCY_RESULT="${LATENCY_RUN}" 758 | } 759 | 760 | # launch_iperf 761 | # Purpose: This method is designed to facilitate the execution of iperf network speed tests to 762 | # each public iperf server in the iperf server locations array. 763 | # Parameters: 764 | # 1. MODE - indicates the type of iperf tests to run (IPv4 or IPv6) 765 | function launch_iperf { 766 | MODE=$1 767 | [[ "$MODE" == *"IPv6"* ]] && IPERF_FLAGS="-6" || IPERF_FLAGS="-4" 768 | 769 | # print iperf3 network speed results as they are completed 770 | echo -e 771 | echo -e "iperf3 Network Speed Tests ($MODE):" 772 | echo -e "---------------------------------" 773 | printf "%-15s | %-25s | %-15s | %-15s | %-15s\n" "Provider" "Location (Link)" "Send Speed" "Recv Speed" "Ping" 774 | printf "%-15s | %-25s | %-15s | %-15s | %-15s\n" "-----" "-----" "----" "----" "----" 775 | 776 | # loop through iperf locations array to run iperf test using each public iperf server 777 | for (( i = 0; i < IPERF_LOCS_NUM; i++ )); do 778 | # test if the current iperf location supports the network mode being tested (IPv4/IPv6) 779 | if [[ "${IPERF_LOCS[i*5+4]}" == *"$MODE"* ]]; then 780 | # call the iperf_test function passing the required parameters 781 | iperf_test "${IPERF_LOCS[i*5]}" "${IPERF_LOCS[i*5+1]}" "${IPERF_LOCS[i*5+2]}" "$IPERF_FLAGS" 782 | # parse the send and receive speed results 783 | IPERF_SENDRESULT_VAL=$(echo "$IPERF_SENDRESULT" | awk '{ print $6 }') 784 | IPERF_SENDRESULT_UNIT=$(echo "$IPERF_SENDRESULT" | awk '{ print $7 }') 785 | IPERF_RECVRESULT_VAL=$(echo "$IPERF_RECVRESULT" | awk '{ print $6 }') 786 | IPERF_RECVRESULT_UNIT=$(echo "$IPERF_RECVRESULT" | awk '{ print $7 }') 787 | LATENCY_VAL="${LATENCY_RESULT}" 788 | # if the results are blank, then the server is "busy" and being overutilized 789 | [[ -z $IPERF_SENDRESULT_VAL || "$IPERF_SENDRESULT_VAL" == *"0.00"* ]] && IPERF_SENDRESULT_VAL="busy" && IPERF_SENDRESULT_UNIT="" 790 | [[ -z $IPERF_RECVRESULT_VAL || "$IPERF_RECVRESULT_VAL" == *"0.00"* ]] && IPERF_RECVRESULT_VAL="busy" && IPERF_RECVRESULT_UNIT="" 791 | # print the speed results for the iperf location currently being evaluated 792 | 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" 793 | if [[ -n $JSON ]]; then 794 | JSON_RESULT+='{"mode":"'$MODE'","provider":"'${IPERF_LOCS[i*5+2]}'","loc":"'${IPERF_LOCS[i*5+3]} 795 | JSON_RESULT+='","send":"'$IPERF_SENDRESULT_VAL' '$IPERF_SENDRESULT_UNIT'","recv":"'$IPERF_RECVRESULT_VAL' '$IPERF_RECVRESULT_UNIT'","latency":"'$LATENCY_VAL'"},' 796 | fi 797 | fi 798 | done 799 | } 800 | 801 | # if the skip iperf flag was set, skip the network performance test, otherwise test network performance 802 | if [ -z "$SKIP_IPERF" ]; then 803 | 804 | if [[ -z "$PREFER_BIN" && -n "$LOCAL_IPERF" ]]; then # local iperf has been detected, use instead of pre-compiled binary 805 | IPERF_CMD=iperf3 806 | else 807 | # create a temp directory to house the required iperf binary and library 808 | IPERF_PATH=$YABS_PATH/iperf 809 | mkdir -p "$IPERF_PATH" 810 | 811 | # download iperf3 binary 812 | if [[ -n $LOCAL_CURL ]]; then 813 | 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" 814 | else 815 | 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" 816 | fi 817 | 818 | if [ ! -f "$IPERF_PATH/iperf3" ]; then # ensure iperf3 binary downloaded successfully 819 | IPERF_DL_FAIL=True 820 | else 821 | chmod +x "$IPERF_PATH/iperf3" 822 | IPERF_CMD=$IPERF_PATH/iperf3 823 | fi 824 | fi 825 | 826 | # array containing all currently available iperf3 public servers to use for the network test 827 | # format: "1" "2" "3" "4" "5" \ 828 | # 1. domain name of the iperf server 829 | # 2. range of ports that the iperf server is running on (lowest-highest) 830 | # 3. friendly name of the host/owner of the iperf server 831 | # 4. location and advertised speed link of the iperf server 832 | # 5. network modes supported by the iperf server (IPv4 = IPv4-only, IPv4|IPv6 = IPv4 + IPv6, etc.) 833 | IPERF_LOCS=( \ 834 | "lon.speedtest.clouvider.net" "5200-5209" "Clouvider" "London, UK (10G)" "IPv4|IPv6" \ 835 | "iperf-ams-nl.eranium.net" "5201-5210" "Eranium" "Amsterdam, NL (100G)" "IPv4|IPv6" \ 836 | #"speedtest.extra.telia.fi" "5201-5208" "Telia" "Helsinki, FI (10G)" "IPv4" 837 | # AFR placeholder 838 | "speedtest.uztelecom.uz" "5200-5209" "Uztelecom" "Tashkent, UZ (10G)" "IPv4|IPv6" \ 839 | "speedtest.sin1.sg.leaseweb.net" "5201-5210" "Leaseweb" "Singapore, SG (10G)" "IPv4|IPv6" \ 840 | "la.speedtest.clouvider.net" "5200-5209" "Clouvider" "Los Angeles, CA, US (10G)" "IPv4|IPv6" \ 841 | "speedtest.nyc1.us.leaseweb.net" "5201-5210" "Leaseweb" "NYC, NY, US (10G)" "IPv4|IPv6" \ 842 | "speedtest.sao1.edgoo.net" "9204-9240" "Edgoo" "Sao Paulo, BR (1G)" "IPv4|IPv6" 843 | ) 844 | 845 | # if the "REDUCE_NET" flag is activated, then do a shorter iperf test with only three locations 846 | # (Clouvider London, Clouvider NYC, and Online.net France) 847 | if [ -n "$REDUCE_NET" ]; then 848 | IPERF_LOCS=( \ 849 | "lon.speedtest.clouvider.net" "5200-5209" "Clouvider" "London, UK (10G)" "IPv4|IPv6" \ 850 | "speedtest.sin1.sg.leaseweb.net" "5201-5210" "Leaseweb" "Singapore, SG (10G)" "IPv4|IPv6" \ 851 | "speedtest.nyc1.us.leaseweb.net" "5201-5210" "Leaseweb" "NYC, NY, US (10G)" "IPv4|IPv6" \ 852 | ) 853 | fi 854 | 855 | # get the total number of iperf locations (total array size divided by 5 since each location has 5 elements) 856 | IPERF_LOCS_NUM=${#IPERF_LOCS[@]} 857 | IPERF_LOCS_NUM=$((IPERF_LOCS_NUM / 5)) 858 | 859 | if [ -z "$IPERF_DL_FAIL" ]; then 860 | [[ -n $JSON ]] && JSON_RESULT+=',"iperf":[' 861 | # check if the host has IPv4 connectivity, if so, run iperf3 IPv4 tests 862 | [ -n "$IPV4_CHECK" ] && launch_iperf "IPv4" 863 | # check if the host has IPv6 connectivity, if so, run iperf3 IPv6 tests 864 | [ -n "$IPV6_CHECK" ] && launch_iperf "IPv6" 865 | [[ -n $JSON ]] && JSON_RESULT=${JSON_RESULT::${#JSON_RESULT}-1} && JSON_RESULT+=']' 866 | else 867 | echo -e "\niperf3 binary download failed. Skipping iperf network tests..." 868 | fi 869 | fi 870 | 871 | # launch_geekbench 872 | # Purpose: This method is designed to run the Primate Labs' Geekbench 4/5 Cross-Platform Benchmark utility 873 | # Parameters: 874 | # 1. VERSION - indicates which Geekbench version to run 875 | function launch_geekbench { 876 | VERSION=$1 877 | 878 | # create a temp directory to house all geekbench files 879 | GEEKBENCH_PATH=$YABS_PATH/geekbench_$VERSION 880 | mkdir -p "$GEEKBENCH_PATH" 881 | 882 | GB_URL="" 883 | GB_CMD="" 884 | GB_RUN="" 885 | 886 | # check for curl vs wget 887 | [[ -n $LOCAL_CURL ]] && DL_CMD="curl -s" || DL_CMD="wget -qO-" 888 | 889 | if [[ $VERSION == *4* && ($ARCH = *aarch64* || $ARCH = *arm*) ]]; then 890 | echo -e "\nARM architecture not supported by Geekbench 4, use Geekbench 5 or 6." 891 | elif [[ $VERSION == *4* && $ARCH != *aarch64* && $ARCH != *arm* ]]; then # Geekbench v4 892 | GB_URL="https://cdn.geekbench.com/Geekbench-4.4.4-Linux.tar.gz" 893 | [[ "$ARCH" == *"x86"* ]] && GB_CMD="geekbench_x86_32" || GB_CMD="geekbench4" 894 | GB_RUN="True" 895 | elif [[ $VERSION == *5* || $VERSION == *6* ]]; then # Geekbench v5/6 896 | if [[ $ARCH = *x86* && $GEEKBENCH_4 == *False* ]]; then # don't run Geekbench 5 if on 32-bit arch 897 | echo -e "\nGeekbench $VERSION cannot run on 32-bit architectures. Re-run with -4 flag to use" 898 | echo -e "Geekbench 4, which can support 32-bit architectures. Skipping Geekbench $VERSION." 899 | elif [[ $ARCH = *x86* && $GEEKBENCH_4 == *True* ]]; then 900 | echo -e "\nGeekbench $VERSION cannot run on 32-bit architectures. Skipping test." 901 | else 902 | if [[ $VERSION == *5* ]]; then # Geekbench v5 903 | [[ $ARCH = *aarch64* || $ARCH = *arm* ]] && GB_URL="https://cdn.geekbench.com/Geekbench-5.5.1-LinuxARMPreview.tar.gz" \ 904 | || GB_URL="https://cdn.geekbench.com/Geekbench-5.5.1-Linux.tar.gz" 905 | GB_CMD="geekbench5" 906 | else # Geekbench v6 907 | [[ $ARCH = *aarch64* || $ARCH = *arm* ]] && GB_URL="https://cdn.geekbench.com/Geekbench-6.4.0-LinuxARMPreview.tar.gz" \ 908 | || GB_URL="https://cdn.geekbench.com/Geekbench-6.4.0-Linux.tar.gz" 909 | GB_CMD="geekbench6" 910 | fi 911 | GB_RUN="True" 912 | fi 913 | fi 914 | 915 | if [[ $GB_RUN == *True* ]]; then # run GB test 916 | echo -en "\nRunning GB$VERSION benchmark test... *cue elevator music*" 917 | 918 | # check for local geekbench installed 919 | if command -v "$GB_CMD" &>/dev/null; then 920 | GEEKBENCH_PATH=$(dirname "$(command -v "$GB_CMD")") 921 | else 922 | # download the desired Geekbench tarball and extract to geekbench temp directory 923 | $DL_CMD $GB_URL | tar xz --strip-components=1 -C "$GEEKBENCH_PATH" &>/dev/null 924 | fi 925 | 926 | # unlock if license file detected 927 | test -f "geekbench.license" && "$GEEKBENCH_PATH/$GB_CMD" --unlock "$(cat geekbench.license)" > /dev/null 2>&1 928 | 929 | # run the Geekbench test and grep the test results URL given at the end of the test 930 | GEEKBENCH_TEST=$("$GEEKBENCH_PATH/$GB_CMD" --upload 2>/dev/null | grep "https://browser") 931 | 932 | # ensure the test ran successfully 933 | if [ -z "$GEEKBENCH_TEST" ]; then 934 | # detect if CentOS 7 and print a more helpful error message 935 | if grep -q "CentOS Linux 7" /etc/os-release; then 936 | echo -e "\r\033[0K CentOS 7 and Geekbench have known issues relating to glibc (see issue #71 for details)" 937 | fi 938 | if [[ -z "$IPV4_CHECK" ]]; then 939 | # Geekbench test failed to download because host lacks IPv4 (cdn.geekbench.com = IPv4 only) 940 | echo -e "\r\033[0KGeekbench releases can only be downloaded over IPv4. FTP the Geekbench files and run manually." 941 | elif [[ $VERSION != *4* && $TOTAL_RAM_RAW -le 1048576 ]]; then 942 | # Geekbench 5/6 test failed with low memory (<=1GB) 943 | 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)." 944 | elif [[ $ARCH != *x86* ]]; then 945 | # if the Geekbench test failed for any other reason, exit cleanly and print error message 946 | echo -e "\r\033[0KGeekbench $VERSION test failed. Run manually to determine cause." 947 | fi 948 | else 949 | # if the Geekbench test succeeded, parse the test results URL 950 | GEEKBENCH_URL=$(echo -e "$GEEKBENCH_TEST" | head -1 | awk '{ print $1 }') 951 | GEEKBENCH_URL_CLAIM=$(echo -e "$GEEKBENCH_TEST" | tail -1 | awk '{ print $1 }') 952 | # sleep a bit to wait for results to be made available on the geekbench website 953 | sleep 10 954 | # parse the public results page for the single and multi core geekbench scores 955 | [[ $VERSION == *4* ]] && GEEKBENCH_SCORES=$($DL_CMD "$GEEKBENCH_URL" | grep "span class='score'") || \ 956 | GEEKBENCH_SCORES=$($DL_CMD "$GEEKBENCH_URL" | grep "div class='score'") 957 | 958 | GEEKBENCH_SCORES_SINGLE=$(echo "$GEEKBENCH_SCORES" | awk -v FS="(>|<)" '{ print $3 }' | head -n 1) 959 | GEEKBENCH_SCORES_MULTI=$(echo "$GEEKBENCH_SCORES" | awk -v FS="(>|<)" '{ print $3 }' | tail -n 1) 960 | 961 | # print the Geekbench results 962 | echo -en "\r\033[0K" 963 | echo -e "Geekbench $VERSION Benchmark Test:" 964 | echo -e "---------------------------------" 965 | printf "%-15s | %-30s\n" "Test" "Value" 966 | printf "%-15s | %-30s\n" "" "" 967 | printf "%-15s | %-30s\n" "Single Core" "$GEEKBENCH_SCORES_SINGLE" 968 | printf "%-15s | %-30s\n" "Multi Core" "$GEEKBENCH_SCORES_MULTI" 969 | printf "%-15s | %-30s\n" "Full Test" "$GEEKBENCH_URL" 970 | 971 | if [[ -n $JSON ]]; then 972 | JSON_RESULT+='{"version":'$VERSION',"single":'$GEEKBENCH_SCORES_SINGLE',"multi":'$GEEKBENCH_SCORES_MULTI 973 | JSON_RESULT+=',"url":"'$GEEKBENCH_URL'"},' 974 | fi 975 | 976 | # write the geekbench claim URL to a file so the user can add the results to their profile (if desired) 977 | [ -n "$GEEKBENCH_URL_CLAIM" ] && echo -e "$GEEKBENCH_URL_CLAIM" >> geekbench_claim.url 2> /dev/null 978 | fi 979 | fi 980 | } 981 | 982 | # if the skip geekbench flag was set, skip the system performance test, otherwise test system performance 983 | if [ -z "$SKIP_GEEKBENCH" ]; then 984 | [[ -n $JSON ]] && JSON_RESULT+=",\"geekbench\":[" 985 | if [[ $GEEKBENCH_4 == *True* ]]; then 986 | launch_geekbench 4 987 | fi 988 | 989 | if [[ $GEEKBENCH_5 == *True* ]]; then 990 | launch_geekbench 5 991 | fi 992 | 993 | if [[ $GEEKBENCH_6 == *True* ]]; then 994 | launch_geekbench 6 995 | fi 996 | [[ -n $JSON ]] && [[ "${JSON_RESULT: -1}" == ',' ]] && JSON_RESULT="${JSON_RESULT%,}" 997 | [[ -n $JSON ]] && JSON_RESULT+="]" 998 | fi 999 | 1000 | # finished all tests, clean up all YABS files and exit 1001 | echo -e 1002 | rm -rf "$YABS_PATH" 1003 | 1004 | YABS_END_TIME=$(date +%s) 1005 | 1006 | # calculate_time_taken 1007 | # Purpose: This method is designed to find the time taken for the completion of a YABS run. 1008 | # Parameters: 1009 | # 1. YABS_END_TIME - time when GB has completed and all files are removed 1010 | # 2. YABS_START_TIME - time when YABS is started 1011 | function calculate_time_taken() { 1012 | end_time=$1 1013 | start_time=$2 1014 | 1015 | time_taken=$(( end_time - start_time )) 1016 | if [ ${time_taken} -gt 60 ]; then 1017 | min=$(( time_taken / 60 )) 1018 | sec=$(( time_taken % 60 )) 1019 | echo "YABS completed in ${min} min ${sec} sec" 1020 | else 1021 | echo "YABS completed in ${time_taken} sec" 1022 | fi 1023 | [[ -n $JSON ]] && JSON_RESULT+=",\"runtime\":{\"start\":$start_time,\"end\":$end_time,\"elapsed\":$time_taken}" 1024 | } 1025 | 1026 | calculate_time_taken "$YABS_END_TIME" "$YABS_START_TIME" 1027 | 1028 | if [[ -n $JSON ]]; then 1029 | JSON_RESULT+="}" 1030 | 1031 | # write json results to file 1032 | if [[ $JSON = *w* ]]; then 1033 | echo "$JSON_RESULT" > "$JSON_FILE" 1034 | fi 1035 | 1036 | # send json results 1037 | if [[ $JSON = *s* ]]; then 1038 | IFS=',' read -r -a JSON_SITES <<< "$JSON_SEND" 1039 | for JSON_SITE in "${JSON_SITES[@]}" 1040 | do 1041 | if [[ -n $LOCAL_CURL ]]; then 1042 | curl -s -H "Content-Type:application/json" -X POST --data ''"$JSON_RESULT"'' "$JSON_SITE" 1043 | else 1044 | wget -qO- --post-data=''"$JSON_RESULT"'' --header='Content-Type:application/json' "$JSON_SITE" 1045 | fi 1046 | done 1047 | fi 1048 | 1049 | # print json result to screen 1050 | if [[ $JSON = *j* ]]; then 1051 | echo -e 1052 | echo "$JSON_RESULT" 1053 | fi 1054 | fi 1055 | 1056 | # reset locale settings 1057 | unset LC_ALL 1058 | --------------------------------------------------------------------------------