diff options
Diffstat (limited to 'src/spdk/test/vhost/windows')
-rwxr-xr-x | src/spdk/test/vhost/windows/windows.sh | 141 | ||||
-rw-r--r-- | src/spdk/test/vhost/windows/windows_fs_test.ps1 | 78 | ||||
-rw-r--r-- | src/spdk/test/vhost/windows/windows_scsi_compliance.ps1 | 73 | ||||
-rwxr-xr-x | src/spdk/test/vhost/windows/windows_scsi_compliance.py | 147 | ||||
-rwxr-xr-x | src/spdk/test/vhost/windows/windows_scsi_compliance.sh | 89 |
5 files changed, 528 insertions, 0 deletions
diff --git a/src/spdk/test/vhost/windows/windows.sh b/src/spdk/test/vhost/windows/windows.sh new file mode 100755 index 000000000..6bf8573f7 --- /dev/null +++ b/src/spdk/test/vhost/windows/windows.sh @@ -0,0 +1,141 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/vhost/common.sh + +rpc_py="$rootdir/scripts/rpc.py -s $(get_vhost_dir 0)/rpc.sock" +ctrl_type="spdk_vhost_scsi" +ssh_pass="" +vm_num="0" +vm_image="/home/sys_sgsw/windows_server.qcow2" + +function usage() { + [[ -n $2 ]] && ( + echo "$2" + echo "" + ) + echo "Windows Server automated test" + echo "Usage: $(basename $1) [OPTIONS]" + echo "--vm-ssh-pass=PASSWORD Text password for the VM" + echo "--vm-image=PATH Path to qcow2 image of Windows VM" + echo "--ctrl-type=TYPE Controller type to use for test:" + echo " spdk_vhost_scsi - use spdk vhost scsi" + echo " spdk_vhost_blk - use spdk vhost block" + echo "-x set -x for script debug" + echo "-h, --help Print help and exit" + + exit 0 +} + +while getopts 'xh-:' optchar; do + case "$optchar" in + -) + case "$OPTARG" in + help) usage $0 ;; + vm-ssh-pass=*) ssh_pass="${OPTARG#*=}" ;; + vm-image=*) vm_image="${OPTARG#*=}" ;; + ctrl-type=*) ctrl_type="${OPTARG#*=}" ;; + esac + ;; + h) usage $0 ;; + x) + set -x + x="-x" + ;; + *) usage $0 "Invalid argument '$OPTARG'" ;; + esac +done + +# For some reason there is a problem between using SSH key authentication +# and Windows UAC. Some of the powershell commands fail due to lack of +# permissons, despite script running in elevated mode. +# There are some clues about this setup that suggest this might not work properly: +# https://superuser.com/questions/181581/how-can-i-run-something-as-administrator-via-cygwins-ssh +# https://cygwin.com/ml/cygwin/2004-09/msg00087.html +# But they apply to rather old Windows distributions. +# Potentially using Windows Server 2016 and newer may solve the issue +# due to OpenSSH being available directly from Windows Store. +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 "$@" +} + +if [[ -z "$ssh_pass" ]]; then + error "Please specify --vm-ssh-pass parameter" +fi + +trap 'error_exit "${FUNCNAME}" "${LINENO}"; rm -f $aio_file' SIGTERM SIGABRT ERR + +vm_kill_all + +# Run vhost without debug! +# Windows Virtio drivers use indirect descriptors without negotiating +# their feature flag, which is explicitly forbidden by the Virtio 1.0 spec. +# "(2.4.5.3.1 Driver Requirements: Indirect Descriptors) +# The driver MUST NOT set the VIRTQ_DESC_F_INDIRECT flag unless the +# VIRTIO_F_INDIRECT_DESC feature was negotiated.". +# Violating this rule doesn't cause any issues for SPDK vhost, +# but triggers an assert, so we can only run Windows VMs with non-debug SPDK builds. +notice "running SPDK vhost" +vhost_run 0 +notice "..." + +# Prepare bdevs for later vhost controllers use +# Nvme bdev is automatically constructed during vhost_run +# by using scripts/gen_nvme.sh. No need to add it manually. +# Using various sizes to better identify bdevs if no name in BLK +# is available +# TODO: use a param for blocksize for AIO and Malloc bdevs +aio_file="$SPDK_TEST_STORAGE/aio_disk" +dd if=/dev/zero of=$aio_file bs=1M count=512 +$rpc_py bdev_aio_create $aio_file Aio0 512 +$rpc_py bdev_malloc_create -b Malloc0 256 512 +$rpc_py bdev_get_bdevs + +# Create vhost controllers +# Prepare VM setup command +setup_cmd="vm_setup --force=0 --memory=8192" +setup_cmd+=" --os=$vm_image" + +if [[ "$ctrl_type" == "spdk_vhost_scsi" ]]; then + $rpc_py vhost_create_scsi_controller naa.0.0 + $rpc_py vhost_scsi_controller_add_target naa.0.0 0 Nvme0n1 + $rpc_py vhost_scsi_controller_add_target naa.0.0 1 Malloc0 + $rpc_py vhost_scsi_controller_add_target naa.0.0 2 Aio0 + setup_cmd+=" --disk-type=spdk_vhost_scsi --disks=0" +elif [[ "$ctrl_type" == "spdk_vhost_blk" ]]; then + $rpc_py vhost_create_blk_controller naa.0.0 Nvme0n1 + $rpc_py vhost_create_blk_controller naa.1.0 Malloc0 + $rpc_py vhost_create_blk_controller naa.2.0 Aio0 + setup_cmd+=" --disk-type=spdk_vhost_blk --disks=0:1:2" +fi +$rpc_py vhost_get_controllers +$setup_cmd + +# Spin up VM +vm_run "$vm_num" +vm_wait_for_boot "300" "$vm_num" + +vm_sshpass "$vm_num" "$ssh_pass" "mkdir /cygdrive/c/fs_test" +vm_scp "$vm_num" "$testdir/windows_fs_test.ps1" "127.0.0.1:/cygdrive/c/fs_test" +vm_sshpass "$vm_num" "$ssh_pass" "cd /cygdrive/c/fs_test; powershell.exe -file windows_fs_test.ps1" + +notice "Shutting down Windows VM..." +# Killing, actually. #TODO: implement vm_windwows_shutdown() function +vm_kill $vm_num + +notice "Shutting down SPDK vhost app..." +vhost_kill 0 + +rm -f $aio_file diff --git a/src/spdk/test/vhost/windows/windows_fs_test.ps1 b/src/spdk/test/vhost/windows/windows_fs_test.ps1 new file mode 100644 index 000000000..cda1b53f2 --- /dev/null +++ b/src/spdk/test/vhost/windows/windows_fs_test.ps1 @@ -0,0 +1,78 @@ +# Get the ID and security principal of the current user account +$myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent() +$myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID) + +# Get the security principal for the Administrator role +$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator + +# Check to see if we are currently running "as Administrator" +if ($myWindowsPrincipal.IsInRole($adminRole)) { + # We are running "as Administrator" - so change the title and background color to indicate this + $Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)" + $Host.UI.RawUI.BackgroundColor = "DarkBlue" + clear-host +} else + { + # We are not running "as Administrator" - so relaunch as administrator + + # Create a new process object that starts PowerShell + $newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell"; + + # Specify the current script path and name as a parameter + $newProcess.Arguments = $myInvocation.MyCommand.Definition; + + # Indicate that the process should be elevated + $newProcess.Verb = "runas"; + + # Start the new process + [System.Diagnostics.Process]::Start($newProcess); + + # Exit from the current, unelevated, process + exit +} + +# Set bash -e equivalent +$ErrorActionPreference = "Stop" + +$filesystems=@("NTFS", "FAT32", "FAT") +$disks = get-disk | Where-Object FriendlyName -NotMatch "QEMU" +Start-Sleep 2 +foreach($disk in $disks) +{ + $size = $disk.Size + $number = $disk.Number + $serial = $disk.SerialNumber + $model = $disk.model.Trim() + $size = $size -replace " ", "_" + $model = $model -replace " ", "_" + + $label = "${number}_${model}_${serial}_${size}" + echo "Running tests for disk $label" + start-sleep 2 + + Try { + Initialize-Disk -Number $disk.Number -PartitionStyle MBR + } Catch { + Clear-Disk -Number $disk.Number -RemoveData -Confirm:$false + Initialize-Disk -Number $disk.Number -PartitionStyle MBR + } + echo "`tDisk initialized" + start-sleep 2 + + $part = New-Partition -DiskNumber $disk.Number -UseMaximumSize -AssignDriveLetter + echo "`tCreated partition $($part.DriveLetter)" + start-sleep 2 + + foreach($fs in $filesystems) { + echo "`tTrying to format $($part.DriveLetter) with $fs" + Try { + $vol = Format-Volume -DriveLetter $part.DriveLetter -FileSystem $fs -Confirm:$false + } Catch [Exception] { + echo $_.Exception.GetType().FullName, $_.Exception.Message + echo $_.Exception | format-list -force + exit 1 + } + echo "`tPartition $($part.DriveLetter) formatted with $fs filesystem" + start-sleep 2 + } +} diff --git a/src/spdk/test/vhost/windows/windows_scsi_compliance.ps1 b/src/spdk/test/vhost/windows/windows_scsi_compliance.ps1 new file mode 100644 index 000000000..80d86e805 --- /dev/null +++ b/src/spdk/test/vhost/windows/windows_scsi_compliance.ps1 @@ -0,0 +1,73 @@ +# Get the ID and security principal of the current user account +$myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent() +$myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID) + +# Get the security principal for the Administrator role +$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator + +# Check to see if we are currently running "as Administrator" +if ($myWindowsPrincipal.IsInRole($adminRole)) + { + # We are running "as Administrator" - so change the title and background color to indicate this + $Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)" + $Host.UI.RawUI.BackgroundColor = "DarkBlue" + clear-host + } +else + { + # We are not running "as Administrator" - so relaunch as administrator + + # Create a new process object that starts PowerShell + $newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell"; + + # Specify the current script path and name as a parameter + $newProcess.Arguments = $myInvocation.MyCommand.Definition; + + # Indicate that the process should be elevated + $newProcess.Verb = "runas"; + + # Start the new process + [System.Diagnostics.Process]::Start($newProcess); + + # Exit from the current, unelevated, process + exit + } +# Run your code that needs to be elevated here +get-disk | Where-Object FriendlyName -NotMatch "QEMU" | Initialize-Disk -PartitionStyle MBR +Start-Sleep 2 +get-disk | Where-Object FriendlyName -NotMatch "QEMU" | Clear-Disk -RemoveData -Confirm:$false +Start-Sleep 2 +get-disk | Where-Object FriendlyName -NotMatch "QEMU" | Initialize-Disk -PartitionStyle MBR +Start-Sleep 2 + +$disks = get-disk | Where-Object FriendlyName -NotMatch "QEMU" +Start-Sleep 2 +foreach($disk in $disks) +{ + + $phy_bs = $disk.PhysicalSectorSize + $model = $disk.model + $serial = $disk.SerialNumber + + $label = "" + $label += $model.Trim() + "_" + $serial + "_" + $phy_bs + $label = $label -replace " ", "_" + echo $label + start-sleep 2 + + $part = New-Partition -DiskNumber $disk.Number -UseMaximumSize -AssignDriveLetter + echo $part.DriveLetter + start-sleep 2 + + $vol = Format-Volume -DriveLetter $part.DriveLetter -FileSystem NTFS -Confirm:$false + echo $vol + start-sleep 2 + + cd C:\SCSI + .\scsicompliancetest.exe \\.\$($vol.DriveLetter): -full | tee "C:\SCSI\WIN_SCSI_1_$label.log" + start-sleep 2 + mv .\scsicompliance.log.wtl ".\WIN_SCSI_1_$label.wtl" + .\scsicompliance.exe /Device \\.\$($vol.DriveLetter): /Operation Test /Scenario Common | tee "C:\SCSI\WIN_SCSI_2_$label.log" + start-sleep 2 + mv .\scsicompliance.wtl ".\WIN_SCSI_2_$label.wtl" +} diff --git a/src/spdk/test/vhost/windows/windows_scsi_compliance.py b/src/spdk/test/vhost/windows/windows_scsi_compliance.py new file mode 100755 index 000000000..a0f4ea63c --- /dev/null +++ b/src/spdk/test/vhost/windows/windows_scsi_compliance.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python3 +import os +import sys +import re +import pprint +import collections + +os.chdir(os.path.join(os.path.dirname(sys.argv[0]), "results")) + +scsi_logs = filter(lambda x: x.endswith(".log"), os.listdir("./")) +scsi_1_pattern = re.compile(r"(ASSERTION\s[1-9][\d+]?\.\d+\s)(.+\s)([\w\W]+?)(Result:\s)(\w+)", re.I | re.M) +scsi_2_pattern = re.compile(r"(?:Start:\s)(ASSERTION:\s)?(.+)(?:,.+=\s)([\w\W]+?)(End:\s)(\w+)(,.*)", re.I | re.M) +fails = [] +warns = [] + +expected_warns = [ + "MODE_SELECT_6_MODE_SENSE_6_Checking_Parameters_Savable_PS_bit", + "MODE_SELECT_10_MODE_SENSE_10_Checking_Parameters_Savable_PS_bit", + "MODE_SELECT_10_Changing_WCE", + "MODE_SELECT_10_MODE_SENSE_10_Checking_that_WCE_has_been_cleared", + "MODE_SELECT_10_MODE_SENSE_10_Checking_that_Saved_Values_have_changed", + "MODE_SELECT_10_setting_WCE", + "MODE_SELECT_10_MODE_SENSE_10_Checking_that_WCE_has_been_set", + "MODE_SELECT_10_Attempting_to_restore_original_values", + "MODE_SELECT_10_MODE_SENSE_10_Verifying_values_were_restored", + "ASSERTION_VERIFY_16_Support_Test", +] + +expected_fails = [ + "ASSERTION_READ_6_Read-With-Disk-Cache-Cleared_Test", + "ASSERTION_READ_10_Read-With-Disk-Cache-Cleared_Test", + "ASSERTION_READ_16_Read-With-Disk-Cache-Cleared_Test", + "ASSERTION_INQUIRY_Checking_Identification_Descriptors_in_VPD_page_0x83", + "ASSERTION_VERIFY_10_Support_Test", +] + +results = {"1": collections.OrderedDict(), + "2": collections.OrderedDict()} + +for log in scsi_logs: + # Choose regex pattern depending on tests version + pattern = scsi_1_pattern if "WIN_SCSI_1" in log else scsi_2_pattern + + # Read log file contents + try: + with open(log, 'r') as fh: + fh = open(log, 'r') + log_text = fh.read() + # Dir name for saving split result files of currently processed log file + d_name = log.split(".")[0] + try: + os.mkdir(d_name) + except OSError: + pass + except IOError as e: + print("ERROR: While opening log file: {log_file}".format(log_file=log)) + exit(1) + + # Parse log file contents + matches_found = re.findall(pattern, log_text) + if len(matches_found) < 1: + print("ERROR: No results found in file {log_file}!".format(log_file=log)) + exit(1) + + # Go through output for each test from log file; parse and save to dict + for m in matches_found: + test_name = re.sub(r"\s+", "_", (m[0] + m[1]).strip()) + test_name = re.sub(r"[():]", "", test_name) + test_name = test_name[0:-1] if "." in test_name[-1] else test_name + tc_result = m[4].upper() + + if "FAIL" in tc_result.upper(): + fails.append([log, test_name, tc_result]) + elif "WARN" in tc_result.upper(): + warns.append([log, test_name, tc_result]) + + # Save output to separate file + with open(os.path.join("./", d_name, test_name), 'w') as fh: + for line in m: + fh.write(line) + + # Also save in dictionary for later use in generating HTML results summary + ver = "1" if "WIN_SCSI_1" in log else "2" + try: + results[ver][test_name][d_name] = tc_result + except KeyError: + results[ver][test_name] = collections.OrderedDict() + results[ver][test_name][d_name] = tc_result + + +# Generate HTML file with results table +with open(os.path.join("./", "results.html"), 'a') as fh: + html = "<html>" + for suite_ver in results.keys(): + html += """"<h2> WIN_SCSI_{ver} </h2> + <table bgcolor=\"#ffffff\" border=\"1px solid black;>\"""".format(ver=suite_ver) + + # Print header + html += "<tr><th>Test name</th>" + disks_header = set() + + for _ in results[suite_ver].keys(): + for disk in results[suite_ver][_].keys(): + disks_header.add(disk) + + for disk in disks_header: + html += "<th>{disk}</th>".format(disk=disk) + html += "</tr>" + + # Print results + for test in results[suite_ver].keys(): + html += "<tr><td>{f_name}</td>".format(f_name=test) + for disk in disks_header: + try: + result = results[suite_ver][test][disk] + + html += "<td" + if "PASS" in result: + html += " bgcolor=\"#99ff33\">" + else: + html += " bgcolor=\"#ff5050\">" + + html += "<a href={file}>{result}</a>".format(result=result, file=os.path.join("./", disk, test)) + html += "</td>" + + except KeyError: + html += "<td bgcolor=\"#ffff99\"></br></td>" + html += "</tr>" + html += "</table></br>" + html += "</html>" + fh.write(html) + +if warns: + not_expected_warns = [w for w in warns if w[1] not in expected_warns and "WIN_SCSI_2" in w[0]] + print("INFO: Windows SCSI compliance warnings:") + pprint.pprint(warns, width=150) + +if fails: + not_expected_fails = [f for f in fails if f[1] not in expected_fails and "WIN_SCSI_2" in f[0]] + print("INFO: Windows SCSI compliance fails:") + pprint.pprint(fails, width=150) + +if not_expected_warns or not_expected_fails: + print("Not expected fails / warnings:") + pprint.pprint(not_expected_warns, width=150) + pprint.pprint(not_expected_fails, width=150) + exit(1) diff --git a/src/spdk/test/vhost/windows/windows_scsi_compliance.sh b/src/spdk/test/vhost/windows/windows_scsi_compliance.sh new file mode 100755 index 000000000..d7c854592 --- /dev/null +++ b/src/spdk/test/vhost/windows/windows_scsi_compliance.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash + +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/vhost/common.sh + +# Tested with windows vm with OS Name: Microsoft Windows Server 2012 R2 Datacenter +# and OS Version: 6.3.9600 N/A Build 9600 +# In order to run this test with windows vm +# windows virtio scsi driver must be installed +WINDOWS_IMG="/home/sys_sgsw/windows_scsi_compliance/windows_vm_image.qcow2" +aio_file="$SPDK_TEST_STORAGE/aio_disk" +ssh_pass="" +vm_num=1 +keep_results_dir=false +rpc_py="$rootdir/scripts/rpc.py -s $(get_vhost_dir 0)/rpc.sock" + +function usage() { + [[ -n $2 ]] && ( + echo "$2" + echo "" + ) + echo "Windows Server scsi compliance test" + echo "Usage: $(basename $1) [OPTIONS]" + echo " --vm-ssh-pass=PASSWORD Text password for the VM" + echo " --vm-image-path Path of windows image" + echo " --keep_results Do not delete dir with results" + + exit 0 +} + +while getopts 'h-:' optchar; do + case "$optchar" in + -) + case "$OPTARG" in + help) usage $0 ;; + vm-ssh-pass=*) ssh_pass="${OPTARG#*=}" ;; + vm-image-path=*) WINDOWS_IMG="${OPTARG#*=}" ;; + keep_results*) keep_results_dir=true ;; + esac + ;; + h) usage $0 ;; + *) usage $0 "Invalid argument '$OPTARG'" ;; + esac +done + +trap 'rm -f $aio_file; rm -rf $testdir/results; error_exit' SIGINT SIGTERM ERR + +VM_PASSWORD="$ssh_pass" +mkdir -p $testdir/results +dd if=/dev/zero of=$aio_file bs=1M count=512 + +timing_enter vhost_run +vhost_run 0 +$rpc_py bdev_nvme_set_hotplug -e +$rpc_py bdev_malloc_create 256 4096 -b Malloc0 +$rpc_py bdev_aio_create $aio_file Aio0 512 +$rpc_py bdev_get_bdevs +$rpc_py vhost_create_scsi_controller naa.vhost.1 +$rpc_py vhost_scsi_controller_add_target naa.vhost.1 0 Nvme0n1 +$rpc_py vhost_scsi_controller_add_target naa.vhost.1 1 Malloc0 +# TODO: Currently there is bug for aio device. Disable this test +# $rpc_py vhost_scsi_controller_add_target naa.vhost.1 2 Aio0 +timing_exit vhost_run + +timing_enter start_vm +vm_setup --force=1 --disk-type=spdk_vhost_scsi --os=$WINDOWS_IMG --disks=vhost --memory=4096 +vm_run "1" +# Wait until VM goes up +vm_wait_for_boot "300" "$vm_num" +timing_exit start_vm + +vm_scp "$vm_num" $testdir/windows_scsi_compliance.ps1 127.0.0.1:/cygdrive/c/SCSI/ +vm_sshpass "$vm_num" "$ssh_pass" "cd /cygdrive/c/SCSI; powershell.exe -file windows_scsi_compliance.ps1" +vm_scp "$vm_num" 127.0.0.1:/cygdrive/c/SCSI/WIN_SCSI_* $testdir/results/ +dos2unix $testdir/results/WIN_SCSI_*.log + +notice "Kill vm 1" +vm_kill "$vm_num" +notice "Kill spdk" +vhost_kill 0 +notice "Remove $aio_file" +rm -f $aio_file + +python3 $testdir/windows_scsi_compliance.py +if ! $keep_results_dir; then + rm -rf $testdir/results +fi |