: ${SPDK_VHOST_VERBOSE=false} : ${VHOST_DIR="$HOME/vhost_test"} : ${QEMU_BIN="qemu-system-x86_64"} : ${QEMU_IMG_BIN="qemu-img"} TEST_DIR=$(readlink -f $rootdir/..) VM_DIR=$VHOST_DIR/vms TARGET_DIR=$VHOST_DIR/vhost VM_PASSWORD="root" #TODO: Move vhost_vm_image.qcow2 into VHOST_DIR on test systems. VM_IMAGE=$HOME/vhost_vm_image.qcow2 if ! hash $QEMU_IMG_BIN $QEMU_BIN; then error 'QEMU is not installed on this system. Unable to run vhost tests.' exit 1 fi mkdir -p $VHOST_DIR mkdir -p $VM_DIR mkdir -p $TARGET_DIR # # Source config describing QEMU and VHOST cores and NUMA # source $rootdir/test/vhost/common/autotest.config function vhosttestinit() { if [ "$TEST_MODE" == "iso" ]; then $rootdir/scripts/setup.sh # Look for the VM image if [[ ! -f $VM_IMAGE ]]; then echo "VM image not found at $VM_IMAGE" echo "Download to $HOME? [yn]" read -r download if [ "$download" = "y" ]; then curl https://ci.spdk.io/download/test_resources/vhost_vm_image.tar.gz | tar xz -C $HOME fi fi fi # Look for the VM image if [[ ! -f $VM_IMAGE ]]; then error "VM image not found at $VM_IMAGE" exit 1 fi } function vhosttestfini() { if [ "$TEST_MODE" == "iso" ]; then $rootdir/scripts/setup.sh reset fi } function message() { local verbose_out if ! $SPDK_VHOST_VERBOSE; then verbose_out="" elif [[ ${FUNCNAME[2]} == "source" ]]; then verbose_out=" (file $(basename ${BASH_SOURCE[1]}):${BASH_LINENO[1]})" else verbose_out=" (function ${FUNCNAME[2]}:${BASH_LINENO[1]})" fi local msg_type="$1" shift echo -e "${msg_type}${verbose_out}: $*" } function fail() { echo "===========" >&2 message "FAIL" "$@" >&2 echo "===========" >&2 exit 1 } function error() { echo "===========" >&2 message "ERROR" "$@" >&2 echo "===========" >&2 # Don't 'return 1' since the stack trace will be incomplete (why?) missing upper command. false } function warning() { message "WARN" "$@" >&2 } function notice() { message "INFO" "$@" } function get_vhost_dir() { local vhost_name="$1" if [[ -z "$vhost_name" ]]; then error "vhost name must be provided to get_vhost_dir" return 1 fi echo "$TARGET_DIR/${vhost_name}" } function vhost_run() { local vhost_name="$1" local run_gen_nvme=true if [[ -z "$vhost_name" ]]; then error "vhost name must be provided to vhost_run" return 1 fi shift if [[ "$1" == "--no-gen-nvme" ]]; then notice "Skipping gen_nvmf.sh NVMe bdev configuration" run_gen_nvme=false shift fi local vhost_dir vhost_dir="$(get_vhost_dir $vhost_name)" local vhost_app="$rootdir/app/vhost/vhost" local vhost_log_file="$vhost_dir/vhost.log" local vhost_pid_file="$vhost_dir/vhost.pid" local vhost_socket="$vhost_dir/usvhost" notice "starting vhost app in background" [[ -r "$vhost_pid_file" ]] && vhost_kill $vhost_name [[ -d $vhost_dir ]] && rm -f $vhost_dir/* mkdir -p $vhost_dir if [[ ! -x $vhost_app ]]; then error "application not found: $vhost_app" return 1 fi local cmd="$vhost_app -r $vhost_dir/rpc.sock $*" notice "Loging to: $vhost_log_file" notice "Socket: $vhost_socket" notice "Command: $cmd" timing_enter vhost_start cd $vhost_dir; $cmd & vhost_pid=$! echo $vhost_pid > $vhost_pid_file notice "waiting for app to run..." waitforlisten "$vhost_pid" "$vhost_dir/rpc.sock" #do not generate nvmes if pci access is disabled if [[ "$cmd" != *"--no-pci"* ]] && [[ "$cmd" != *"-u"* ]] && $run_gen_nvme; then $rootdir/scripts/gen_nvme.sh "--json" | $rootdir/scripts/rpc.py\ -s $vhost_dir/rpc.sock load_subsystem_config fi notice "vhost started - pid=$vhost_pid" timing_exit vhost_start } function vhost_kill() { local rc=0 local vhost_name="$1" if [[ -z "$vhost_name" ]]; then error "Must provide vhost name to vhost_kill" return 0 fi local vhost_dir vhost_dir="$(get_vhost_dir $vhost_name)" local vhost_pid_file="$vhost_dir/vhost.pid" if [[ ! -r $vhost_pid_file ]]; then warning "no vhost pid file found" return 0 fi timing_enter vhost_kill local vhost_pid vhost_pid="$(cat $vhost_pid_file)" notice "killing vhost (PID $vhost_pid) app" if kill -INT $vhost_pid > /dev/null; then notice "sent SIGINT to vhost app - waiting 60 seconds to exit" for ((i=0; i<60; i++)); do if kill -0 $vhost_pid; then echo "." sleep 1 else break fi done if kill -0 $vhost_pid; then error "ERROR: vhost was NOT killed - sending SIGABRT" kill -ABRT $vhost_pid rm $vhost_pid_file rc=1 else while kill -0 $vhost_pid; do echo "." done fi elif kill -0 $vhost_pid; then error "vhost NOT killed - you need to kill it manually" rc=1 else notice "vhost was not running" fi timing_exit vhost_kill if [[ $rc == 0 ]]; then rm $vhost_pid_file fi rm -rf "$vhost_dir" return $rc } function vhost_rpc { local vhost_name="$1" if [[ -z "$vhost_name" ]]; then error "vhost name must be provided to vhost_rpc" return 1 fi shift $rootdir/scripts/rpc.py -s $(get_vhost_dir $vhost_name)/rpc.sock "$@" } ### # Mgmt functions ### function assert_number() { [[ "$1" =~ [0-9]+ ]] && return 0 error "Invalid or missing paramter: need number but got '$1'" return 1; } # Run command on vm with given password # First argument - vm number # Second argument - ssh password for vm # function vm_sshpass() { vm_num_is_valid $1 || return 1 local ssh_cmd ssh_cmd="sshpass -p $2 ssh \ -o UserKnownHostsFile=/dev/null \ -o StrictHostKeyChecking=no \ -o User=root \ -p $(vm_ssh_socket $1) $VM_SSH_OPTIONS 127.0.0.1" shift 2 $ssh_cmd "$@" } # Helper to validate VM number # param $1 VM number # function vm_num_is_valid() { [[ "$1" =~ ^[0-9]+$ ]] && return 0 error "Invalid or missing paramter: vm number '$1'" return 1; } # Print network socket for given VM number # param $1 virtual machine number # function vm_ssh_socket() { vm_num_is_valid $1 || return 1 local vm_dir="$VM_DIR/$1" cat $vm_dir/ssh_socket } function vm_fio_socket() { vm_num_is_valid $1 || return 1 local vm_dir="$VM_DIR/$1" cat $vm_dir/fio_socket } # Execute command on given VM # param $1 virtual machine number # function vm_exec() { vm_num_is_valid $1 || return 1 local vm_num="$1" shift sshpass -p "$VM_PASSWORD" ssh \ -o UserKnownHostsFile=/dev/null \ -o StrictHostKeyChecking=no \ -o User=root \ -p $(vm_ssh_socket $vm_num) $VM_SSH_OPTIONS 127.0.0.1 \ "$@" } # Execute scp command on given VM # param $1 virtual machine number # function vm_scp() { vm_num_is_valid $1 || return 1 local vm_num="$1" shift sshpass -p "$VM_PASSWORD" scp \ -o UserKnownHostsFile=/dev/null \ -o StrictHostKeyChecking=no \ -o User=root \ -P $(vm_ssh_socket $vm_num) $VM_SSH_OPTIONS \ "$@" } # check if specified VM is running # param $1 VM num function vm_is_running() { vm_num_is_valid $1 || return 1 local vm_dir="$VM_DIR/$1" if [[ ! -r $vm_dir/qemu.pid ]]; then return 1 fi local vm_pid vm_pid="$(cat $vm_dir/qemu.pid)" if /bin/kill -0 $vm_pid; then return 0 else if [[ $EUID -ne 0 ]]; then warning "not root - assuming VM running since can't be checked" return 0 fi # not running - remove pid file rm $vm_dir/qemu.pid return 1 fi } # check if specified VM is running # param $1 VM num function vm_os_booted() { vm_num_is_valid $1 || return 1 local vm_dir="$VM_DIR/$1" if [[ ! -r $vm_dir/qemu.pid ]]; then error "VM $1 is not running" return 1 fi if ! VM_SSH_OPTIONS="-o ControlMaster=no" vm_exec $1 "true" 2>/dev/null; then # Shutdown existing master. Ignore errors as it might not exist. VM_SSH_OPTIONS="-O exit" vm_exec $1 "true" 2>/dev/null return 1 fi return 0 } # Shutdown given VM # param $1 virtual machine number # return non-zero in case of error. function vm_shutdown() { vm_num_is_valid $1 || return 1 local vm_dir="$VM_DIR/$1" if [[ ! -d "$vm_dir" ]]; then error "VM$1 ($vm_dir) not exist - setup it first" return 1 fi if ! vm_is_running $1; then notice "VM$1 ($vm_dir) is not running" return 0 fi # Temporarily disabling exit flag for next ssh command, since it will # "fail" due to shutdown notice "Shutting down virtual machine $vm_dir" set +e vm_exec $1 "nohup sh -c 'shutdown -h -P now'" || true notice "VM$1 is shutting down - wait a while to complete" set -e } # Kill given VM # param $1 virtual machine number # function vm_kill() { vm_num_is_valid $1 || return 1 local vm_dir="$VM_DIR/$1" if [[ ! -r $vm_dir/qemu.pid ]]; then return 0 fi local vm_pid vm_pid="$(cat $vm_dir/qemu.pid)" notice "Killing virtual machine $vm_dir (pid=$vm_pid)" # First kill should fail, second one must fail if /bin/kill $vm_pid; then notice "process $vm_pid killed" rm $vm_dir/qemu.pid rm -rf $vm_dir elif vm_is_running $1; then error "Process $vm_pid NOT killed" return 1 fi } # List all VM numbers in VM_DIR # function vm_list_all() { local vms vms="$(shopt -s nullglob; echo $VM_DIR/[0-9]*)" if [[ -n "$vms" ]]; then basename --multiple $vms fi } # Kills all VM in $VM_DIR # function vm_kill_all() { local vm for vm in $(vm_list_all); do vm_kill $vm done rm -rf $VM_DIR } # Shutdown all VM in $VM_DIR # function vm_shutdown_all() { # XXX: temporarily disable to debug shutdown issue # xtrace_disable local vms vms=$(vm_list_all) local vm for vm in $vms; do vm_shutdown $vm done notice "Waiting for VMs to shutdown..." local timeo=30 while [[ $timeo -gt 0 ]]; do local all_vms_down=1 for vm in $vms; do if vm_is_running $vm; then all_vms_down=0 break fi done if [[ $all_vms_down == 1 ]]; then notice "All VMs successfully shut down" xtrace_restore return 0 fi ((timeo-=1)) sleep 1 done rm -rf $VM_DIR xtrace_restore } function vm_setup() { xtrace_disable local OPTIND optchar vm_num local os="" local os_mode="" local qemu_args="" local disk_type_g=NOT_DEFINED local read_only="false" local disks="" local raw_cache="" local vm_incoming="" local vm_migrate_to="" local force_vm="" local guest_memory=1024 local queue_number="" local vhost_dir vhost_dir="$(get_vhost_dir 0)" while getopts ':-:' optchar; do case "$optchar" in -) case "$OPTARG" in os=*) os="${OPTARG#*=}" ;; os-mode=*) os_mode="${OPTARG#*=}" ;; qemu-args=*) qemu_args="${qemu_args} ${OPTARG#*=}" ;; disk-type=*) disk_type_g="${OPTARG#*=}" ;; read-only=*) read_only="${OPTARG#*=}" ;; disks=*) disks="${OPTARG#*=}" ;; raw-cache=*) raw_cache=",cache${OPTARG#*=}" ;; force=*) force_vm=${OPTARG#*=} ;; memory=*) guest_memory=${OPTARG#*=} ;; queue_num=*) queue_number=${OPTARG#*=} ;; incoming=*) vm_incoming="${OPTARG#*=}" ;; migrate-to=*) vm_migrate_to="${OPTARG#*=}" ;; vhost-name=*) vhost_dir="$(get_vhost_dir ${OPTARG#*=})" ;; spdk-boot=*) local boot_from="${OPTARG#*=}" ;; *) error "unknown argument $OPTARG" return 1 esac ;; *) error "vm_create Unknown param $OPTARG" return 1 ;; esac done # Find next directory we can use if [[ -n $force_vm ]]; then vm_num=$force_vm vm_num_is_valid $vm_num || return 1 local vm_dir="$VM_DIR/$vm_num" [[ -d $vm_dir ]] && warning "removing existing VM in '$vm_dir'" else local vm_dir="" set +x for (( i=0; i<=256; i++)); do local vm_dir="$VM_DIR/$i" [[ ! -d $vm_dir ]] && break done xtrace_restore vm_num=$i fi if [[ $vm_num -eq 256 ]]; then error "no free VM found. do some cleanup (256 VMs created, are you insane?)" return 1 fi if [[ -n "$vm_migrate_to" && -n "$vm_incoming" ]]; then error "'--incoming' and '--migrate-to' cannot be used together" return 1 elif [[ -n "$vm_incoming" ]]; then if [[ -n "$os_mode" || -n "$os" ]]; then error "'--incoming' can't be used together with '--os' nor '--os-mode'" return 1 fi os_mode="original" os="$VM_DIR/$vm_incoming/os.qcow2" elif [[ -n "$vm_migrate_to" ]]; then [[ "$os_mode" != "backing" ]] && warning "Using 'backing' mode for OS since '--migrate-to' is used" os_mode=backing fi notice "Creating new VM in $vm_dir" mkdir -p $vm_dir if [[ "$os_mode" == "backing" ]]; then notice "Creating backing file for OS image file: $os" if ! $QEMU_IMG_BIN create -f qcow2 -b $os $vm_dir/os.qcow2; then error "Failed to create OS backing file in '$vm_dir/os.qcow2' using '$os'" return 1 fi local os=$vm_dir/os.qcow2 elif [[ "$os_mode" == "original" ]]; then warning "Using original OS image file: $os" elif [[ "$os_mode" != "snapshot" ]]; then if [[ -z "$os_mode" ]]; then notice "No '--os-mode' parameter provided - using 'snapshot'" os_mode="snapshot" else error "Invalid '--os-mode=$os_mode'" return 1 fi fi # WARNING: # each cmd+= must contain ' ${eol}' at the end # local eol="\\\\\n " local qemu_mask_param="VM_${vm_num}_qemu_mask" local qemu_numa_node_param="VM_${vm_num}_qemu_numa_node" if [[ -z "${!qemu_mask_param}" ]] || [[ -z "${!qemu_numa_node_param}" ]]; then error "Parameters ${qemu_mask_param} or ${qemu_numa_node_param} not found in autotest.config file" return 1 fi local task_mask=${!qemu_mask_param} notice "TASK MASK: $task_mask" local cmd="taskset -a -c $task_mask $QEMU_BIN ${eol}" local vm_socket_offset=$(( 10000 + 100 * vm_num )) local ssh_socket=$(( vm_socket_offset + 0 )) local fio_socket=$(( vm_socket_offset + 1 )) local monitor_port=$(( vm_socket_offset + 2 )) local migration_port=$(( vm_socket_offset + 3 )) local gdbserver_socket=$(( vm_socket_offset + 4 )) local vnc_socket=$(( 100 + vm_num )) local qemu_pid_file="$vm_dir/qemu.pid" local cpu_num=0 set +x # cpu list for taskset can be comma separated or range # or both at the same time, so first split on commas cpu_list=$(echo $task_mask | tr "," "\n") queue_number=0 for c in $cpu_list; do # if range is detected - count how many cpus if [[ $c =~ [0-9]+-[0-9]+ ]]; then val=$((c-1)) val=${val#-} else val=1 fi cpu_num=$((cpu_num+val)) queue_number=$((queue_number+val)) done if [ -z $queue_number ]; then queue_number=$cpu_num fi xtrace_restore local node_num=${!qemu_numa_node_param} local boot_disk_present=false notice "NUMA NODE: $node_num" cmd+="-m $guest_memory --enable-kvm -cpu host -smp $cpu_num -vga std -vnc :$vnc_socket -daemonize ${eol}" cmd+="-object memory-backend-file,id=mem,size=${guest_memory}M,mem-path=/dev/hugepages,share=on,prealloc=yes,host-nodes=$node_num,policy=bind ${eol}" [[ $os_mode == snapshot ]] && cmd+="-snapshot ${eol}" [[ -n "$vm_incoming" ]] && cmd+=" -incoming tcp:0:$migration_port ${eol}" cmd+="-monitor telnet:127.0.0.1:$monitor_port,server,nowait ${eol}" cmd+="-numa node,memdev=mem ${eol}" cmd+="-pidfile $qemu_pid_file ${eol}" cmd+="-serial file:$vm_dir/serial.log ${eol}" cmd+="-D $vm_dir/qemu.log ${eol}" cmd+="-chardev file,path=$vm_dir/seabios.log,id=seabios -device isa-debugcon,iobase=0x402,chardev=seabios ${eol}" cmd+="-net user,hostfwd=tcp::$ssh_socket-:22,hostfwd=tcp::$fio_socket-:8765 ${eol}" cmd+="-net nic ${eol}" if [[ -z "$boot_from" ]]; then cmd+="-drive file=$os,if=none,id=os_disk ${eol}" cmd+="-device ide-hd,drive=os_disk,bootindex=0 ${eol}" fi if [[ $disks == '' ]] && [[ $disk_type_g == virtio* ]]; then disks=1 fi for disk in ${disks//:/ }; do if [[ $disk = *","* ]]; then disk_type=${disk#*,} disk=${disk%,*} else disk_type=$disk_type_g fi case $disk_type in virtio) local raw_name="RAWSCSI" local raw_disk=$vm_dir/test.img if [[ -n $disk ]]; then [[ ! -b $disk ]] && touch $disk local raw_disk raw_disk=$(readlink -f $disk) fi # Create disk file if it not exist or it is smaller than 1G if { [[ -f $raw_disk ]] && [[ $(stat --printf="%s" $raw_disk) -lt $((1024 * 1024 * 1024)) ]]; } || \ [[ ! -e $raw_disk ]]; then if [[ $raw_disk =~ /dev/.* ]]; then error \ "ERROR: Virtio disk point to missing device ($raw_disk) -\n" \ " this is probably not what you want." return 1 fi notice "Creating Virtio disc $raw_disk" dd if=/dev/zero of=$raw_disk bs=1024k count=1024 else notice "Using existing image $raw_disk" fi cmd+="-device virtio-scsi-pci,num_queues=$queue_number ${eol}" cmd+="-device scsi-hd,drive=hd$i,vendor=$raw_name ${eol}" cmd+="-drive if=none,id=hd$i,file=$raw_disk,format=raw$raw_cache ${eol}" ;; spdk_vhost_scsi) notice "using socket $vhost_dir/naa.$disk.$vm_num" cmd+="-chardev socket,id=char_$disk,path=$vhost_dir/naa.$disk.$vm_num ${eol}" cmd+="-device vhost-user-scsi-pci,id=scsi_$disk,num_queues=$queue_number,chardev=char_$disk" if [[ "$disk" == "$boot_from" ]]; then cmd+=",bootindex=0" boot_disk_present=true fi cmd+=" ${eol}" ;; spdk_vhost_blk) notice "using socket $vhost_dir/naa.$disk.$vm_num" cmd+="-chardev socket,id=char_$disk,path=$vhost_dir/naa.$disk.$vm_num ${eol}" cmd+="-device vhost-user-blk-pci,num-queues=$queue_number,chardev=char_$disk" if [[ "$disk" == "$boot_from" ]]; then cmd+=",bootindex=0" boot_disk_present=true fi cmd+=" ${eol}" ;; kernel_vhost) if [[ -z $disk ]]; then error "need WWN for $disk_type" return 1 elif [[ ! $disk =~ ^[[:alpha:]]{3}[.][[:xdigit:]]+$ ]]; then error "$disk_type - disk(wnn)=$disk does not look like WNN number" return 1 fi notice "Using kernel vhost disk wwn=$disk" cmd+=" -device vhost-scsi-pci,wwpn=$disk,num_queues=$queue_number ${eol}" ;; *) error "unknown mode '$disk_type', use: virtio, spdk_vhost_scsi, spdk_vhost_blk or kernel_vhost" return 1 esac done if [[ -n $boot_from ]] && [[ $boot_disk_present == false ]]; then error "Boot from $boot_from is selected but device is not present" return 1 fi [[ -n $qemu_args ]] && cmd+=" $qemu_args ${eol}" # remove last $eol cmd="${cmd%\\\\\\n }" notice "Saving to $vm_dir/run.sh" ( echo '#!/bin/bash' echo 'if [[ $EUID -ne 0 ]]; then ' echo ' echo "Go away user come back as root"' echo ' exit 1' echo 'fi'; echo echo -e "qemu_cmd=\"$cmd\""; echo echo "echo 'Running VM in $vm_dir'" echo "rm -f $qemu_pid_file" echo '$qemu_cmd' echo "echo 'Waiting for QEMU pid file'" echo "sleep 1" echo "[[ ! -f $qemu_pid_file ]] && sleep 1" echo "[[ ! -f $qemu_pid_file ]] && echo 'ERROR: no qemu pid file found' && exit 1" echo echo "chmod +r $vm_dir/*" echo echo "echo '=== qemu.log ==='" echo "cat $vm_dir/qemu.log" echo "echo '=== qemu.log ==='" echo '# EOF' ) > $vm_dir/run.sh chmod +x $vm_dir/run.sh # Save generated sockets redirection echo $ssh_socket > $vm_dir/ssh_socket echo $fio_socket > $vm_dir/fio_socket echo $monitor_port > $vm_dir/monitor_port rm -f $vm_dir/migration_port [[ -z $vm_incoming ]] || echo $migration_port > $vm_dir/migration_port echo $gdbserver_socket > $vm_dir/gdbserver_socket echo $vnc_socket >> $vm_dir/vnc_socket [[ -z $vm_incoming ]] || ln -fs $VM_DIR/$vm_incoming $vm_dir/vm_incoming [[ -z $vm_migrate_to ]] || ln -fs $VM_DIR/$vm_migrate_to $vm_dir/vm_migrate_to } function vm_run() { local OPTIND optchar vm local run_all=false local vms_to_run="" while getopts 'a-:' optchar; do case "$optchar" in a) run_all=true ;; *) error "Unknown param $OPTARG" return 1 ;; esac done if $run_all; then vms_to_run="$(vm_list_all)" else shift $((OPTIND-1)) for vm in "$@"; do vm_num_is_valid $1 || return 1 if [[ ! -x $VM_DIR/$vm/run.sh ]]; then error "VM$vm not defined - setup it first" return 1 fi vms_to_run+=" $vm" done fi for vm in $vms_to_run; do if vm_is_running $vm; then warning "VM$vm ($VM_DIR/$vm) already running" continue fi notice "running $VM_DIR/$vm/run.sh" if ! $VM_DIR/$vm/run.sh; then error "FAILED to run vm $vm" return 1 fi done } function vm_print_logs() { vm_num=$1 warning "================" warning "QEMU LOG:" if [[ -r $VM_DIR/$vm_num/qemu.log ]]; then cat $VM_DIR/$vm_num/qemu.log else warning "LOG qemu.log not found" fi warning "VM LOG:" if [[ -r $VM_DIR/$vm_num/serial.log ]]; then cat $VM_DIR/$vm_num/serial.log else warning "LOG serial.log not found" fi warning "SEABIOS LOG:" if [[ -r $VM_DIR/$vm_num/seabios.log ]]; then cat $VM_DIR/$vm_num/seabios.log else warning "LOG seabios.log not found" fi warning "================" } # Wait for all created VMs to boot. # param $1 max wait time function vm_wait_for_boot() { assert_number $1 xtrace_disable local all_booted=false local timeout_time=$1 [[ $timeout_time -lt 10 ]] && timeout_time=10 local timeout_time timeout_time=$(date -d "+$timeout_time seconds" +%s) notice "Waiting for VMs to boot" shift if [[ "$*" == "" ]]; then local vms_to_check="$VM_DIR/[0-9]*" else local vms_to_check="" for vm in "$@"; do vms_to_check+=" $VM_DIR/$vm" done fi for vm in $vms_to_check; do local vm_num vm_num=$(basename $vm) local i=0 notice "waiting for VM$vm_num ($vm)" while ! vm_os_booted $vm_num; do if ! vm_is_running $vm_num; then warning "VM $vm_num is not running" vm_print_logs $vm_num xtrace_restore return 1 fi if [[ $(date +%s) -gt $timeout_time ]]; then warning "timeout waiting for machines to boot" vm_print_logs $vm_num xtrace_restore return 1 fi if (( i > 30 )); then local i=0 echo fi echo -n "." sleep 1 done echo "" notice "VM$vm_num ready" #Change Timeout for stopping services to prevent lengthy powerdowns #Check that remote system is not Cygwin in case of Windows VMs local vm_os vm_os=$(vm_exec $vm_num "uname -o") if [[ "$vm_os" != "Cygwin" ]]; then vm_exec $vm_num "echo 'DefaultTimeoutStopSec=10' >> /etc/systemd/system.conf; systemctl daemon-reexec" fi done notice "all VMs ready" xtrace_restore return 0 } function vm_start_fio_server() { local OPTIND optchar local readonly='' while getopts ':-:' optchar; do case "$optchar" in -) case "$OPTARG" in fio-bin=*) local fio_bin="${OPTARG#*=}" ;; readonly) local readonly="--readonly" ;; *) error "Invalid argument '$OPTARG'" && return 1;; esac ;; *) error "Invalid argument '$OPTARG'" && return 1;; esac done shift $(( OPTIND - 1 )) for vm_num in "$@"; do notice "Starting fio server on VM$vm_num" if [[ $fio_bin != "" ]]; then vm_exec $vm_num 'cat > /root/fio; chmod +x /root/fio' < $fio_bin vm_exec $vm_num /root/fio $readonly --eta=never --server --daemonize=/root/fio.pid else vm_exec $vm_num fio $readonly --eta=never --server --daemonize=/root/fio.pid fi done } function vm_check_scsi_location() { # Script to find wanted disc local script='shopt -s nullglob; for entry in /sys/block/sd*; do disk_type="$(cat $entry/device/vendor)"; if [[ $disk_type == INTEL* ]] || [[ $disk_type == RAWSCSI* ]] || [[ $disk_type == LIO-ORG* ]]; then fname=$(basename $entry); echo -n " $fname"; fi; done' SCSI_DISK="$(echo "$script" | vm_exec $1 bash -s)" if [[ -z "$SCSI_DISK" ]]; then error "no test disk found!" return 1 fi } # Script to perform scsi device reset on all disks in VM # param $1 VM num # param $2..$n Disks to perform reset on function vm_reset_scsi_devices() { for disk in "${@:2}"; do notice "VM$1 Performing device reset on disk $disk" vm_exec $1 sg_reset /dev/$disk -vNd done } function vm_check_blk_location() { local script='shopt -s nullglob; cd /sys/block; echo vd*' SCSI_DISK="$(echo "$script" | vm_exec $1 bash -s)" if [[ -z "$SCSI_DISK" ]]; then error "no blk test disk found!" return 1 fi } function run_fio() { local arg local job_file="" local fio_bin="" local vms=() local out="" local fio_disks="" local vm local run_server_mode=true for arg in "$@"; do case "$arg" in --job-file=*) local job_file="${arg#*=}" ;; --fio-bin=*) local fio_bin="${arg#*=}" ;; --vm=*) vms+=( "${arg#*=}" ) ;; --out=*) local out="${arg#*=}" mkdir -p $out ;; --local) run_server_mode=false ;; --json) json="--json" ;; *) error "Invalid argument '$arg'" return 1 ;; esac done if [[ -n "$fio_bin" && ! -r "$fio_bin" ]]; then error "FIO binary '$fio_bin' does not exist" return 1 fi if [[ ! -r "$job_file" ]]; then error "Fio job '$job_file' does not exist" return 1 fi local job_fname job_fname=$(basename "$job_file") # prepare job file for each VM for vm in "${vms[@]}"; do local vm_num=${vm%%:*} local vmdisks=${vm#*:} sed "s@filename=@filename=$vmdisks@" $job_file | vm_exec $vm_num "cat > /root/$job_fname" fio_disks+="127.0.0.1:$(vm_fio_socket $vm_num):$vmdisks," vm_exec $vm_num cat /root/$job_fname if ! $run_server_mode; then if [[ -n "$fio_bin" ]]; then vm_exec $vm_num 'cat > /root/fio; chmod +x /root/fio' < $fio_bin fi notice "Running local fio on VM $vm_num" vm_exec $vm_num "nohup /root/fio /root/$job_fname 1>/root/$job_fname.out 2>/root/$job_fname.out /root/fio.pid" fi done if ! $run_server_mode; then # Give FIO time to run sleep 0.5 return 0 fi $rootdir/test/vhost/common/run_fio.py --job-file=/root/$job_fname \ $([[ -n "$fio_bin" ]] && echo "--fio-bin=$fio_bin") \ --out=$out $json ${fio_disks%,} } # Shutdown or kill any running VM and SPDK APP. # function at_app_exit() { local vhost_name notice "APP EXITING" notice "killing all VMs" vm_kill_all # Kill vhost application notice "killing vhost app" for vhost_name in "$TARGET_DIR"/*; do vhost_kill $vhost_name done notice "EXIT DONE" } function error_exit() { trap - ERR print_backtrace set +e error "Error on $1 $2" at_app_exit exit 1 }