per Intel policy to include file commit date using git cmd below. The policy does not apply to non-Intel (C) notices. git log --follow -C90% --format=%ad --date default <file> | tail -1 and then pull just the year from the result. Intel copyrights were not added to files where Intel either had no contribution ot the contribution lacked substance (ie license header updates, formatting changes, etc) For intel copyrights added, --follow and -C95% were used. Signed-off-by: paul luse <paul.e.luse@intel.com> Change-Id: I2ef86976095b88a9bf5b1003e59f3943cd6bbe4c Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15209 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Community-CI: Mellanox Build Bot Reviewed-by: Ben Walker <benjamin.walker@intel.com> Reviewed-by: Krzysztof Karas <krzysztof.karas@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com>
439 lines
14 KiB
Bash
Executable File
439 lines
14 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
# Copyright (C) 2022 Intel Corporation
|
|
# All rights reserved.
|
|
#
|
|
testdir=$(readlink -f "$(dirname "$0")")
|
|
rootdir=$(readlink -f "$testdir/../..")
|
|
|
|
source "$rootdir/test/common/autotest_common.sh"
|
|
source "$testdir/common.sh"
|
|
|
|
sma_py="$rootdir/scripts/sma-client.py"
|
|
rpc_py="$rootdir/scripts/rpc.py"
|
|
|
|
t1sock='/var/tmp/spdk.sock1'
|
|
t2sock='/var/tmp/spdk.sock2'
|
|
invalid_port=8008
|
|
t1dscport=8009
|
|
t2dscport1=8010
|
|
t2dscport2=8011
|
|
t1nqn='nqn.2016-06.io.spdk:node1'
|
|
t2nqn='nqn.2016-06.io.spdk:node2'
|
|
hostnqn='nqn.2016-06.io.spdk:host0'
|
|
cleanup_period=1
|
|
|
|
function cleanup() {
|
|
killprocess $smapid
|
|
killprocess $tgtpid
|
|
killprocess $t1pid
|
|
killprocess $t2pid
|
|
}
|
|
|
|
function format_endpoints() {
|
|
local eps=("$@")
|
|
for ((i = 0; i < ${#eps[@]}; i++)); do
|
|
cat <<- EOF
|
|
{
|
|
"trtype": "tcp",
|
|
"traddr": "127.0.0.1",
|
|
"trsvcid": "${eps[i]}"
|
|
}
|
|
EOF
|
|
if ! ((i + 1 == ${#@})); then
|
|
echo ,
|
|
fi
|
|
done
|
|
}
|
|
|
|
function format_volume() {
|
|
local volume_id=$1
|
|
shift
|
|
|
|
cat <<- EOF
|
|
"volume": {
|
|
"volume_id": "$(uuid2base64 $volume_id)",
|
|
"nvmf": {
|
|
"hostnqn": "$hostnqn",
|
|
"discovery": {
|
|
"discovery_endpoints": [
|
|
$(format_endpoints "$@")
|
|
]
|
|
}
|
|
}
|
|
}
|
|
EOF
|
|
}
|
|
|
|
function create_device() {
|
|
local nqn=$1
|
|
local volume_id=$2
|
|
local volume=
|
|
|
|
shift
|
|
if [[ -n "$volume_id" ]]; then
|
|
volume="$(format_volume "$@"),"
|
|
fi
|
|
|
|
$sma_py <<- EOF
|
|
{
|
|
"method": "CreateDevice",
|
|
"params": {
|
|
$volume
|
|
"nvmf_tcp": {
|
|
"subnqn": "$nqn",
|
|
"adrfam": "ipv4",
|
|
"traddr": "127.0.0.1",
|
|
"trsvcid": "4419"
|
|
}
|
|
}
|
|
}
|
|
EOF
|
|
}
|
|
|
|
function delete_device() {
|
|
$sma_py <<- EOF
|
|
{
|
|
"method": "DeleteDevice",
|
|
"params": {
|
|
"handle": "$1"
|
|
}
|
|
}
|
|
EOF
|
|
}
|
|
|
|
function attach_volume() {
|
|
local device_id=$1
|
|
|
|
shift
|
|
$sma_py <<- EOF
|
|
{
|
|
"method": "AttachVolume",
|
|
"params": {
|
|
$(format_volume "$@"),
|
|
"device_handle": "$device_id"
|
|
}
|
|
}
|
|
EOF
|
|
}
|
|
|
|
function detach_volume() {
|
|
$sma_py <<- EOF
|
|
{
|
|
"method": "DetachVolume",
|
|
"params": {
|
|
"device_handle": "$1",
|
|
"volume_id": "$(uuid2base64 $2)"
|
|
}
|
|
}
|
|
EOF
|
|
}
|
|
|
|
trap "cleanup; exit 1" SIGINT SIGTERM EXIT
|
|
|
|
# Start two remote targets
|
|
$rootdir/build/bin/spdk_tgt -r $t1sock -m 0x1 &
|
|
t1pid=$!
|
|
$rootdir/build/bin/spdk_tgt -r $t2sock -m 0x2 &
|
|
t2pid=$!
|
|
|
|
# One target that the SMA will configure
|
|
$rootdir/build/bin/spdk_tgt -m 0x4 &
|
|
tgtpid=$!
|
|
|
|
# And finally the SMA itself
|
|
$rootdir/scripts/sma.py -c <(
|
|
cat <<- EOF
|
|
discovery_timeout: 5
|
|
volume_cleanup_period: $cleanup_period
|
|
devices:
|
|
- name: 'nvmf_tcp'
|
|
EOF
|
|
) &
|
|
smapid=$!
|
|
|
|
waitforlisten $t1pid
|
|
waitforlisten $t2pid
|
|
|
|
# Prepare the targets. The first one has a single subsystem with a single volume and a single
|
|
# discovery listener. The second one also has a single subsystem, but has two volumes attached to
|
|
# it and has two discovery listeners.
|
|
t1uuid=$(uuidgen)
|
|
t2uuid=$(uuidgen)
|
|
t2uuid2=$(uuidgen)
|
|
|
|
$rpc_py -s $t1sock <<- EOF
|
|
nvmf_create_transport -t tcp
|
|
bdev_null_create null0 128 4096 -u $t1uuid
|
|
nvmf_create_subsystem $t1nqn -s SPDK00000000000001 -d SPDK_Controller1
|
|
nvmf_subsystem_add_host $t1nqn $hostnqn
|
|
nvmf_subsystem_add_ns $t1nqn $t1uuid
|
|
nvmf_subsystem_add_listener $t1nqn -t tcp -a 127.0.0.1 -s 4420
|
|
nvmf_subsystem_add_listener discovery -t tcp -a 127.0.0.1 -s $t1dscport
|
|
EOF
|
|
|
|
$rpc_py -s $t2sock <<- EOF
|
|
nvmf_create_transport -t tcp
|
|
bdev_null_create null0 128 4096 -u $t2uuid
|
|
bdev_null_create null1 128 4096 -u $t2uuid2
|
|
nvmf_create_subsystem $t2nqn -s SPDK00000000000001 -d SPDK_Controller1
|
|
nvmf_subsystem_add_host $t2nqn $hostnqn
|
|
nvmf_subsystem_add_ns $t2nqn $t2uuid
|
|
nvmf_subsystem_add_ns $t2nqn $t2uuid2
|
|
nvmf_subsystem_add_listener $t2nqn -t tcp -a 127.0.0.1 -s 4421
|
|
nvmf_subsystem_add_listener discovery -t tcp -a 127.0.0.1 -s $t2dscport1
|
|
nvmf_subsystem_add_listener discovery -t tcp -a 127.0.0.1 -s $t2dscport2
|
|
EOF
|
|
|
|
# Wait until the SMA starts listening
|
|
sma_waitforlisten
|
|
|
|
localnqn='nqn.2016-06.io.spdk:local0'
|
|
|
|
# Create a device
|
|
device_id=$(create_device $localnqn | jq -r '.handle')
|
|
|
|
# Check that it's been created
|
|
$rpc_py nvmf_get_subsystems $localnqn
|
|
|
|
# Attach a volume specifying both targets
|
|
attach_volume $device_id $t1uuid $t1dscport $t2dscport1
|
|
|
|
# Check that a connection has been made to discovery services on both targets
|
|
[[ $($rpc_py bdev_nvme_get_discovery_info | jq -r '. | length') -eq 2 ]]
|
|
|
|
$rpc_py bdev_nvme_get_discovery_info | jq -r '.[].trid.trsvcid' | grep $t1dscport
|
|
$rpc_py bdev_nvme_get_discovery_info | jq -r '.[].trid.trsvcid' | grep $t2dscport1
|
|
|
|
# Check that the volume was attached to the device
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces | length') -eq 1 ]]
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces[0].uuid') == "$t1uuid" ]]
|
|
|
|
# Attach the other volume, this time specify only single target
|
|
attach_volume $device_id $t2uuid $t2dscport1
|
|
|
|
# Check that both volumes are attached to the device
|
|
[[ $($rpc_py bdev_nvme_get_discovery_info | jq -r '. | length') -eq 2 ]]
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces | length') -eq 2 ]]
|
|
$rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces[].uuid' | grep $t1uuid
|
|
$rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces[].uuid' | grep $t2uuid
|
|
|
|
# Detach the first volume
|
|
detach_volume $device_id $t1uuid
|
|
|
|
# Check that there's a connection to a single target now (because we've only specified a single
|
|
# target when connecting the other volume).
|
|
[[ $($rpc_py bdev_nvme_get_discovery_info | jq -r '. | length') -eq 1 ]]
|
|
$rpc_py bdev_nvme_get_discovery_info | jq -r '.[].trid.trsvcid' | grep $t2dscport1
|
|
# Check that the volume was actually removed
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces | length') -eq 1 ]]
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces[0].uuid') == "$t2uuid" ]]
|
|
|
|
# Detach the other volume
|
|
detach_volume $device_id $t2uuid
|
|
|
|
# And verify it's gone too
|
|
[[ $($rpc_py bdev_nvme_get_discovery_info | jq -r '. | length') -eq 0 ]]
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces | length') -eq 0 ]]
|
|
|
|
# Check that specifying an invalid volume UUID results in an error
|
|
NOT attach_volume $device_id $(uuidgen) $t1dscport
|
|
[[ $($rpc_py bdev_nvme_get_discovery_info | jq -r '. | length') -eq 0 ]]
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces | length') -eq 0 ]]
|
|
|
|
# Attach them again, this time both volumes specify both targets
|
|
volumes=($t1uuid $t2uuid)
|
|
for volume_id in "${volumes[@]}"; do
|
|
attach_volume $device_id $volume_id $t1dscport $t2dscport1
|
|
done
|
|
|
|
[[ $($rpc_py bdev_nvme_get_discovery_info | jq -r '. | length') -eq 2 ]]
|
|
$rpc_py bdev_nvme_get_discovery_info | jq -r '.[].trid.trsvcid' | grep $t1dscport
|
|
$rpc_py bdev_nvme_get_discovery_info | jq -r '.[].trid.trsvcid' | grep $t2dscport1
|
|
$rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces[].uuid' | grep $t1uuid
|
|
$rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces[].uuid' | grep $t2uuid
|
|
|
|
# Detach one and see that both targets are still connected
|
|
detach_volume $device_id $t1uuid
|
|
|
|
[[ $($rpc_py bdev_nvme_get_discovery_info | jq -r '. | length') -eq 2 ]]
|
|
$rpc_py bdev_nvme_get_discovery_info | jq -r '.[].trid.trsvcid' | grep $t1dscport
|
|
$rpc_py bdev_nvme_get_discovery_info | jq -r '.[].trid.trsvcid' | grep $t2dscport1
|
|
|
|
# Delete the device and verify that this also causes the volumes to be disconnected
|
|
delete_device $device_id
|
|
|
|
[[ $($rpc_py bdev_nvme_get_discovery_info | jq -r '. | length') -eq 0 ]]
|
|
NOT $rpc_py nvmf_get_subsystems $localnqn
|
|
|
|
# Create a device and attach a volume immediately
|
|
device_id=$(create_device $localnqn $t1uuid $t1dscport | jq -r '.handle')
|
|
|
|
# Verify that there's a connection to the target and the volume is attached to the device
|
|
[[ $($rpc_py bdev_nvme_get_discovery_info | jq -r '. | length') -eq 1 ]]
|
|
$rpc_py bdev_nvme_get_discovery_info | jq -r '.[].trid.trsvcid' | grep $t1dscport
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces | length') -eq 1 ]]
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces[0].uuid') == "$t1uuid" ]]
|
|
|
|
# Make sure it's also possible to detach it
|
|
detach_volume $device_id $t1uuid
|
|
|
|
[[ $($rpc_py bdev_nvme_get_discovery_info | jq -r '. | length') -eq 0 ]]
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces | length') -eq 0 ]]
|
|
|
|
# Check that discovery referrals work correctly
|
|
attach_volume $device_id $t2uuid $t2dscport1 $t2dscport2
|
|
|
|
# Check that only a single connection has been made
|
|
[[ $($rpc_py bdev_nvme_get_discovery_info | jq -r '. | length') -eq 1 ]]
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces | length') -eq 1 ]]
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces[0].uuid') == "$t2uuid" ]]
|
|
|
|
# Add the other volume from the same target/subsystem, but use a single discovery endpoint
|
|
attach_volume $device_id $t2uuid2 $t2dscport2
|
|
|
|
# Check that the volume was attached to the subsystem, but no extra connection has been made
|
|
[[ $($rpc_py bdev_nvme_get_discovery_info | jq -r '. | length') -eq 1 ]]
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces | length') -eq 2 ]]
|
|
$rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces[].uuid' | grep $t2uuid
|
|
$rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces[].uuid' | grep $t2uuid2
|
|
|
|
# Reset the device
|
|
delete_device $device_id
|
|
[[ $($rpc_py bdev_nvme_get_discovery_info | jq -r '. | length') -eq 0 ]]
|
|
|
|
device_id=$(create_device $localnqn | jq -r '.handle')
|
|
|
|
# Check subsystem NQN verification, start with a valid one
|
|
$sma_py <<- EOF
|
|
{
|
|
"method": "AttachVolume",
|
|
"params": {
|
|
"volume": {
|
|
"volume_id": "$(uuid2base64 $t1uuid)",
|
|
"nvmf": {
|
|
"hostnqn": "$hostnqn",
|
|
"subnqn": "$t1nqn",
|
|
"discovery": {
|
|
"discovery_endpoints": [
|
|
{
|
|
"trtype": "tcp",
|
|
"traddr": "127.0.0.1",
|
|
"trsvcid": "$t1dscport"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"device_handle": "$device_id"
|
|
}
|
|
}
|
|
EOF
|
|
|
|
[[ $($rpc_py bdev_nvme_get_discovery_info | jq -r '. | length') -eq 1 ]]
|
|
$rpc_py bdev_nvme_get_discovery_info | jq -r '.[].trid.trsvcid' | grep $t1dscport
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces | length') -eq 1 ]]
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces[0].uuid') == "$t1uuid" ]]
|
|
|
|
# Then check incorrect subnqn
|
|
NOT $sma_py <<- EOF
|
|
{
|
|
"method": "AttachVolume",
|
|
"params": {
|
|
"volume": {
|
|
"volume_id": "$(uuid2base64 $t2uuid)",
|
|
"nvmf": {
|
|
"hostnqn": "$hostnqn",
|
|
"subnqn": "${t2nqn}-invalid",
|
|
"discovery": {
|
|
"discovery_endpoints": [
|
|
{
|
|
"trtype": "tcp",
|
|
"traddr": "127.0.0.1",
|
|
"trsvcid": "$t2dscport1"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"device_handle": "$device_id"
|
|
}
|
|
}
|
|
EOF
|
|
|
|
# Verify the volume hasn't been attached
|
|
[[ $($rpc_py bdev_nvme_get_discovery_info | jq -r '. | length') -eq 1 ]]
|
|
$rpc_py bdev_nvme_get_discovery_info | jq -r '.[].trid.trsvcid' | grep $t1dscport
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces | length') -eq 1 ]]
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces[0].uuid') == "$t1uuid" ]]
|
|
|
|
# Check incorrect hostnqn
|
|
NOT $sma_py <<- EOF
|
|
{
|
|
"method": "AttachVolume",
|
|
"params": {
|
|
"volume": {
|
|
"volume_id": "$(uuid2base64 $t2uuid)",
|
|
"nvmf": {
|
|
"hostnqn": "${hostnqn}-invalid",
|
|
"discovery": {
|
|
"discovery_endpoints": [
|
|
{
|
|
"trtype": "tcp",
|
|
"traddr": "127.0.0.1",
|
|
"trsvcid": "$t2dscport1"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"device_handle": "$device_id"
|
|
}
|
|
}
|
|
EOF
|
|
|
|
# Verify that the volume wasn't attached
|
|
[[ $($rpc_py bdev_nvme_get_discovery_info | jq -r '. | length') -eq 1 ]]
|
|
$rpc_py bdev_nvme_get_discovery_info | jq -r '.[].trid.trsvcid' | grep $t1dscport
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces | length') -eq 1 ]]
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces[0].uuid') == "$t1uuid" ]]
|
|
|
|
# Check that the the attach will fail if there's nobody listening on the discovery endpoint
|
|
NOT attach_volume $device_id $(uuidgen) $invalid_port
|
|
[[ $($rpc_py bdev_nvme_get_discovery_info | jq -r '. | length') -eq 1 ]]
|
|
$rpc_py bdev_nvme_get_discovery_info | jq -r '.[].trid.trsvcid' | grep $t1dscport
|
|
|
|
# Make sure that the discovery service is stopped if a volume is disconnected outside of SMA (e.g.
|
|
# by removing it from the target)
|
|
$rpc_py -s $t1sock nvmf_subsystem_remove_ns $t1nqn 1
|
|
# Give SMA some time to be notified about the change
|
|
sleep $((cleanup_period + 1))
|
|
[[ $($rpc_py bdev_nvme_get_discovery_info | jq -r '. | length') -eq 0 ]]
|
|
$rpc_py -s $t1sock nvmf_subsystem_add_ns $t1nqn $t1uuid
|
|
|
|
# Do the same, but this time attach two volumes and check that the discovery service is only
|
|
# stopped once both volumes are disconnected
|
|
attach_volume $device_id $t2uuid $t2dscport1
|
|
attach_volume $device_id $t2uuid2 $t2dscport1
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces | length') -eq 2 ]]
|
|
[[ $($rpc_py bdev_nvme_get_discovery_info | jq -r '. | length') -eq 1 ]]
|
|
$rpc_py -s $t2sock nvmf_subsystem_remove_ns $t2nqn 2
|
|
# Give SMA some time to be notified about the change
|
|
sleep $((cleanup_period + 1))
|
|
# One of the volumes should be gone, but the discovery service should still be running
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces | length') -eq 1 ]]
|
|
[[ $($rpc_py bdev_nvme_get_discovery_info | jq -r '. | length') -eq 1 ]]
|
|
$rpc_py -s $t2sock nvmf_subsystem_remove_ns $t2nqn 1
|
|
# Give SMA some time to be notified about the change
|
|
sleep $((cleanup_period + 1))
|
|
# Now that both are gone, the discovery service should be stopped too
|
|
[[ $($rpc_py nvmf_get_subsystems $localnqn | jq -r '.[].namespaces | length') -eq 0 ]]
|
|
[[ $($rpc_py bdev_nvme_get_discovery_info | jq -r '. | length') -eq 0 ]]
|
|
$rpc_py -s $t2sock nvmf_subsystem_add_ns $t2nqn $t2uuid
|
|
$rpc_py -s $t2sock nvmf_subsystem_add_ns $t2nqn $t2uuid2
|
|
|
|
delete_device $device_id
|
|
|
|
cleanup
|
|
trap - SIGINT SIGTERM EXIT
|