-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
vbox-nic-bench.sh: formatting fixes really, but the diffstat tells
a different story. benchmarks/vbox-nic-bench.sh | 336 +++++++++++-------------------------------- 1 file changed, 87 insertions(+), 249 deletions(-) Signed-off-by: Christian Kujau <[email protected]>
- Loading branch information
Showing
1 changed file
with
87 additions
and
249 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,265 +1,103 @@ | ||
#!/bin/sh | ||
# | ||
# (c)2015 Christian Kujau <[email protected]> | ||
# Some kind of network benchmark for VirtualBox machines | ||
# (c)2017-2019 Christian Kujau <[email protected]> | ||
# | ||
# Needed: | ||
# * Guest VM with static IP addresses all set up. The trick is to get the VM | ||
# to aquire network connectivity, even if the interfaces changes. Magic boot | ||
# routines trying to guess the correct NIC may cause trouble. | ||
# * password-less SSH (root) login to the guest VM | ||
# * iperf2 installed on the host and the guest VM | ||
# * netcat(1) (use the MacPorts variant for a MacOS X host!) | ||
# | ||
|
||
# unset me! | ||
# DEBUG=echo | ||
|
||
# https://www.virtualbox.org/manual/ch06.html#nichardware | ||
# 6.1. Virtual networking hardware | ||
# > vboxmanage --help | grep -A2 nictype | ||
# --nictype1 Am79C970A - pcnet II | ||
# Am79C973 - pcnet III - default | ||
# 82540EM - MT Desktop - Windows Vista | ||
# 82543GC - T Server - Windows XP | ||
# 82545EM - MT Server - OVF imports | ||
# virtio - virtio-net - KVM | ||
# | ||
# Am79C970A - AMD PCNet PCI II | ||
# Am79C973 - AMD PCNet FAST III (default) | ||
# 82540EM - Intel PRO/1000 MT Desktop (works with Windows Vista) | ||
# 82543GC - Intel PRO/1000 T Server (recognized by Windows XP) | ||
# 82545EM - Intel PRO/1000 MT (for OVF imports from other platforms) | ||
# virtio - virtio-net (supported by Linux 2.6.25 and Windows, see | ||
# http://www.linux-kvm.org/page/WindowsGuestDrivers) | ||
# | ||
TYPES="Am79C970A virtio" # test | ||
TYPES="Am79C970A Am79C973 82540EM 82543GC 82545EM virtio" | ||
|
||
_help() { | ||
echo "Usage: $(basename $0) [vm] [time]" | ||
echo " $(basename $0) [report] [file]" | ||
exit 1 | ||
} | ||
|
||
case $1 in | ||
report) | ||
[ -f "$2" ] && REPORT="$2" || _help | ||
|
||
echo "### By NIC type:" | ||
for t in $TYPES; do | ||
for m in hostonly bridged natnetwork nat; do | ||
printf "NIC: $t / MODE: $m " | ||
RESULT=$(grep -A7 "iperf: $m / NIC: $t" "$REPORT" | awk '/Bytes\/sec/ {print $(NF-1), $NF}') | ||
[ -n "$RESULT" ] && echo "$RESULT" || echo "-" | ||
done | sort -rnk6 | ||
echo | ||
done | ||
NICTYPES="Am79C970A Am79C973 82540EM 82543GC 82545EM virtio" | ||
|
||
if [ $# -ne 3 ]; then | ||
echo "Usage: $(basename $0) [vm1] [vm2] [num]" | ||
echo " $(basename $0) [report] [file.log] [num]" | ||
echo "" | ||
echo " NOTES:" | ||
echo " * An iperf3 server MUST be started on vm1 after boot." | ||
echo " * Password-less logins to and between the VMS needed." | ||
echo " * Set [num] to 0 for a dry-run" | ||
echo "" | ||
echo "" | ||
exit 1 | ||
else | ||
VM1="$1" | ||
VM2="$2" | ||
NUM="$3" | ||
fi | ||
|
||
echo | ||
echo "### By network mode:" | ||
for m in hostonly bridged natnetwork nat; do | ||
for t in $TYPES; do | ||
printf "NIC: $t / MODE: $m " | ||
RESULT=$(grep -A7 "iperf: $m / NIC: $t" "$REPORT" | awk '/Bytes\/sec/ {print $(NF-1), $NF}') | ||
[ -n "$RESULT" ] && echo "$RESULT" || echo "-" | ||
done | sort -rnk6 | ||
echo | ||
# Short-circuit for reporting mode, because we were too | ||
# lazy to think this through... | ||
if [ "$1" = "report" ]; then | ||
awk '/Running/ {print $6, $8}' "$2" | while read n1 n2; do | ||
# This only works when NUM is the same value that we have tested with! | ||
awk "/$n1 - $n2/ {sum+=\$10} END {print \"$n1 - $n2\t\", sum / 2 / $NUM, \"MB/s\"}" "$2" | ||
done | ||
exit $? | ||
;; | ||
|
||
[a-z]*) | ||
$DEBUG VBoxManage showvminfo "$1" > /dev/null 2>&1 || _help | ||
VM="$1" | ||
TIME="${2:-10}" # How long to run "iperf" | ||
;; | ||
|
||
*) | ||
_help | ||
;; | ||
esac | ||
|
||
_stop_vm() { | ||
echo "INFO: Sending shutdown signal to "$VM"..." | ||
$DEBUG VBoxManage controlvm "$VM" acpipowerbutton | ||
|
||
echo "INFO: Wait until $VM is powered off..." | ||
i=0 | ||
state="running" | ||
ERROR=0 | ||
|
||
# Bail out after 2 minutes | ||
while true; do | ||
state=`VBoxManage showvminfo "$VM" --machinereadable | awk -F= '/^VMState=/ {print $2}' | sed 's/"//g'` | ||
if [ "$state" = "poweroff" ]; then | ||
echo "INFO: vm $VM state: $state" | ||
break | ||
else | ||
echo "INFO: vm $VM state: $state" | ||
fi | ||
|
||
if [ $i -gt 40 ]; then | ||
echo "ERROR: timeout reached, sending poweroff..." | ||
$DEBUG VBoxManage controlvm "$VM" poweroff || ERROR=1 | ||
fi | ||
fi | ||
# Dry-run? | ||
[ $NUM = 0 ] && DEBUG=echo | ||
|
||
sleep 3 | ||
i=$((i+1)) | ||
done | ||
die() { | ||
echo "$@" | ||
exit 2 | ||
} | ||
|
||
wait_until_ssh() { | ||
j=0 | ||
state="running" | ||
ERROR=0 | ||
|
||
while true; do | ||
# NOTE: On MacOS X, the stock netcat version does not timeout even when | ||
# the -w1 option is given. We'll use the MacPorts version for that. | ||
PATH=/opt/local/bin:$PATH nc -w1 -z "$VM" 22 > /dev/null 2>&1 && break | ||
echo "INFO: vm $VM still not reachable via SSH" | ||
|
||
# Bail out after 2 minutes | ||
if [ $j -gt 40 ]; then | ||
ERROR=1 | ||
echo "ERROR: timeout reached!" | ||
break | ||
# We really need this only for the first run, I guess | ||
ison() { | ||
# running or poweroff/aborted/...? | ||
STATE=$(VBoxManage showvminfo "$1" --machinereadable | awk -F= '/VMState=/ {print $2}' | sed 's/\"//g') | ||
if [ "$STATE" = "running" ]; then | ||
true | ||
else | ||
false | ||
fi | ||
|
||
sleep 3 | ||
j=$((j+1)) | ||
done | ||
} | ||
|
||
$DEBUG _stop_vm | ||
if [ $ERROR = 1 ]; then | ||
echo "ERROR: could not stop VM, bailing out" | ||
exit 3 | ||
fi | ||
$DEBUG sleep 2 | ||
|
||
echo "INFO: Prepare NAT networking..." | ||
$DEBUG VBoxManage natnetwork remove --netname natnet1 | ||
$DEBUG VBoxManage natnetwork add --netname natnet1 --network 192.168.15.0/24 | ||
$DEBUG VBoxManage natnetwork stop --netname natnet1 | ||
$DEBUG VBoxManage natnetwork modify --netname natnet1 --dhcp off \ | ||
--port-forward-4 'iperf:tcp:[127.0.0.1]:15001:[192.168.15.4]:5001' \ | ||
--port-forward-4 'ssh:tcp:[127.0.0.1]:15002:[192.168.15.4]:22' \ | ||
--port-forward-4 'foo:tcp:[127.0.0.1]:15003:[192.168.15.4]:1234' | ||
$DEBUG VBoxManage natnetwork start --netname natnet1 | ||
|
||
for nic in $TYPES; do | ||
$DEBUG _stop_vm | ||
[ $ERROR = 1 ] && continue | ||
|
||
$DEBUG sleep 2 | ||
|
||
echo | ||
echo "####### NIC: $nic" | ||
|
||
echo "The VM $VM should be powered off now, let's configure 4 NICs..." | ||
$DEBUG VBoxManage modifyvm "$VM" --natpf4 delete iperf --natpf4 delete ssh --natpf4 delete foo | ||
|
||
# VirtualBox MAC address prefix is 08-00-27. Our NIC1 should already be configured and we don't | ||
# want to change the MAC address here. Let's change it only for NIC2/3/4 | ||
|
||
# We also need to find out our primay network interface for "bridged" mode. | ||
case $(uname -s) in | ||
Darwin) | ||
DEV=$(netstat -rn -f inet | awk '/^default/ {print $NF}') | ||
;; | ||
|
||
Linux) | ||
DEV=$(netstat -rn -A inet | awk '/^0.0.0.0/ {print $NF}') | ||
;; | ||
|
||
*) | ||
echo "Which platform are we on?" | ||
exit 2 | ||
esac | ||
|
||
$DEBUG VBoxManage modifyvm "$VM" \ | ||
--nic1 hostonly --hostonlyadapter1 vboxnet0 --nictype1 "$nic" \ | ||
--nic2 bridged --bridgeadapter2 $DEV --nictype2 "$nic" --macaddress2 080027e20002 \ | ||
--nic3 natnetwork --nat-network3 natnet1 --nictype3 "$nic" --macaddress3 080027e20003 \ | ||
--nic4 nat --natnet4 10.0.2.0/24 --nictype4 "$nic" --macaddress4 080027e20004 \ | ||
--natpf4 "iperf,tcp,127.0.0.1,25001,10.0.2.123,5001" \ | ||
--natpf4 "ssh,tcp,127.0.0.1,25002,10.0.2.123,22" \ | ||
--natpf4 "foo,tcp,127.0.0.1,25003,10.0.2.123,1234" | ||
$DEBUG sleep 2 | ||
|
||
# This should give us 4 NICs in the VM: | ||
# | ||
# NIC1 - eth0 - hostonly - 192.168.56.0/24 (default) | ||
# NIC2 - eth1 - bridged - 192.168.0.0/24 | ||
# NIC3 - eth2 - natnetwork - 192.168.15.0/24 | ||
# NIC4 - eth3 - nat - 10.0.2.0/24 | ||
# | ||
# FIXME: for now we have static NIC names and static IP addresses. More logic | ||
# is needed to have all this configured dynamically. | ||
# | ||
# - /etc/udev/rules.d/70-persistent-net.rules | ||
# SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:??:??:??", KERNEL=="eth*", NAME="eth0" | ||
# SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:e2:00:02", KERNEL=="eth*", NAME="eth1" | ||
# SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:e2:00:03", KERNEL=="eth*", NAME="eth2" | ||
# SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="08:00:27:e2:00:04", KERNEL=="eth*", NAME="eth3" | ||
# | ||
# - /etc/network/interfaces | ||
# | ||
# auto lo eth0 eth1 eth2 eth3 | ||
# # host-only | ||
# iface eth0 inet dhcp | ||
# | ||
# # bridged | ||
# # The /32 may be needed when we're in the same network as eth0 and we | ||
# # don't want a second network route | ||
# iface eth1 inet static | ||
# address 192.168.0.123 | ||
# netmask 255.255.255.255 | ||
# | ||
# # nat-network | ||
# iface eth2 inet static | ||
# address 192.168.15.123 | ||
# netmask 255.255.255.0 | ||
# | ||
# # nat | ||
# iface eth3 inet static | ||
# address 10.0.2.123 | ||
# netmask 255.255.255.0 | ||
# | ||
|
||
echo "Start the VM..." | ||
$DEBUG VBoxManage startvm "$VM" --type headless | ||
|
||
echo "Wait until SSH comes up..." | ||
$DEBUG wait_until_ssh | ||
[ $ERROR = 1 ] && continue | ||
$DEBUG sleep 2 | ||
|
||
echo "The VM should be accessible via SSH now." | ||
echo | ||
$DEBUG ssh "$VM" "uname -a; lspci | grep net; iperf --server --daemon > /var/log/iperf-server_"$nic".log" | ||
$DEBUG sleep 2 | ||
|
||
echo | ||
echo "### iperf: hostonly / NIC: $nic" | ||
$DEBUG iperf -f M -t $TIME -c "$VM" | ||
|
||
echo | ||
echo "### iperf: bridged / NIC: $nic" | ||
[ -z "$DEBUG" ] && IP_BR=$(ssh "$VM" "ip -4 addr show eth1 | awk '/inet/ {print \$2}' | sed 's|/[0-9]*||'") | ||
$DEBUG iperf -f M -t $TIME -c "$IP_BR" | ||
|
||
echo | ||
echo "### iperf: natnetwork / NIC: $nic" | ||
$DEBUG iperf -f M -t $TIME -c 127.0.0.1 -p 15001 | ||
|
||
echo | ||
echo "### iperf: nat / NIC: $nic" | ||
$DEBUG iperf -f M -t $TIME -c 127.0.0.1 -p 25001 | ||
echo | ||
|
||
echo "Saving the VM's dmesg..." | ||
$DEBUG ssh "$VM" dmesg > vbox_"$VM"_"$nic"_dmesg.txt | ||
|
||
$DEBUG sleep 2 | ||
|
||
$DEBUG _stop_vm | ||
[ $ERROR = 1 ] && continue | ||
|
||
$DEBUG sleep 2 | ||
echo | ||
for nic2 in $NICTYPES; do | ||
echo "### NIC2: $nic2 -- shutting down $VM2" | ||
ison $VM2 && ( $DEBUG VBoxManage controlvm $VM2 acpipowerbutton || die "VBoxManage controlvm $VM2 acpipowerbutton FAILED" ) | ||
$DEBUG sleep 20 | ||
|
||
echo "### NIC2: $nic2 -- setting NIC to $nic2 on $VM2" | ||
$DEBUG VBoxManage modifyvm $VM2 --nictype1 "$nic2" || die "VBoxManage modifyvm $VM2 --nictype1 "$nic2" FAILED" | ||
$DEBUG VBoxManage showvminfo $VM2 --machinereadable | grep -A1 -w nic1 | ||
|
||
echo "### NIC2: $nic2 -- starting $VM2" | ||
$DEBUG VBoxManage startvm $VM2 --type headless || die "VBoxManage startvm $VM2 --type headless FAILED" | ||
# $DEBUG VBoxHeadless --startvm $VM2 & | ||
# [ $? = 0 ] || die "VBoxHeadless --startvm $VM2 FAILED" | ||
$DEBUG sleep 30 | ||
|
||
for nic1 in $NICTYPES; do | ||
echo "### NIC1: $nic1 -- shutting down $VM1" | ||
ison $VM1 && ( $DEBUG VBoxManage controlvm $VM1 acpipowerbutton || die "VBoxManage controlvm $VM1 acpipowerbutton FAILED" ) | ||
$DEBUG sleep 20 | ||
|
||
echo "### NIC1: $nic1 -- setting NIC to $nic1 on $VM1" | ||
ison $VM1 || ( $DEBUG VBoxManage modifyvm $VM1 --nictype1 "$nic1" || die "VBoxManage modifyvm $VM1 --nictype1 "$nic1" FAILED" ) | ||
$DEBUG VBoxManage showvminfo $VM1 --machinereadable | grep -A1 -w nic1 | ||
|
||
echo "### NIC1: $nic1 -- starting $VM1" | ||
$DEBUG VBoxManage startvm $VM1 --type headless || die "VBoxManage startvm $VM1 --type headless FAILED" | ||
# $DEBUG VBoxHeadless --startvm $VM1 & | ||
# [ $? = 0 ] || die "VBoxHeadless --startvm $VM1 FAILED" | ||
$DEBUG sleep 30 | ||
|
||
echo "### Running iperf3 tests. NIC1: $nic1 NIC2: $nic2" | ||
a=1 | ||
while [ $a -le $NUM ] || [ $NUM = 0 ]; do | ||
echo "### RUN $a of $NUM" | ||
# NAT # $DEBUG ssh -p2001 192.168.56.1 "iperf3 -f M -c 10.0.2.5" || die "ssh -p2001 192.168.56.1 ... FAILED" | ||
$DEBUG ssh $VM2 "iperf3 -f M -T \"$nic1 - $nic2\" -c $VM1" || die "ssh $VM2 -- FAILED" | ||
[ -z "$DEBUG" ] && a=$((a++1)) || break | ||
done | egrep 'RUN|ssh|sender|receiver' # The "ssh" is only matched in $DEBUG mode | ||
echo | ||
done | ||
done |