1# SPDX-License-Identifier: BSD-2-Clause
2#
3# Copyright (c) 2024 Axcient
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14#
15# THIS DOCUMENTATION IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
26# Things that aren't tested due to lack of kernel support:
27# * Creating camsim ports
28# * Creating tpc ports
29# * Creating camtgt ports
30# * Creating umass ports
31
32# TODO
33# * Creating nvmf ports
34# * Creating ha ports
35# * Creating fc ports
36
37# The PGTAG can be any 16-bit number.  The only constraint is that each
38# PGTAG,TARGET pair must be globally unique.
39PGTAG=30257
40
41load_cfiscsi() {
42	if ! kldstat -q -m cfiscsi; then
43		kldload cfiscsi || atf_skip "could not load cfscsi kernel mod"
44	fi
45}
46
47skip_if_ctld() {
48	if service ctld onestatus > /dev/null; then
49		# If ctld is running on this server, let's not interfere.
50		atf_skip "Cannot run this test while ctld is running"
51	fi
52}
53
54cleanup() {
55	driver=$1
56
57	if [ -e port-create.txt ]; then
58		case "$driver" in
59		"ioctl")
60			PORTNUM=`awk '/port:/ {print $2}' port-create.txt`
61			ctladm port -r -d $driver -p $PORTNUM
62			;;
63		"iscsi")
64			TARGET=`awk '/target:/ {print $2}' port-create.txt`
65			ctladm port -r -d $driver -p "$PORTNUM" -O cfiscsi_portal_group_tag=$PGTAG -O cfiscsi_target=$TARGET
66			;;
67		esac
68	fi
69}
70
71atf_test_case create_ioctl cleanup
72create_ioctl_head()
73{
74	atf_set "descr" "ctladm can create a new ioctl port"
75	atf_set "require.user" "root"
76}
77create_ioctl_body()
78{
79	skip_if_ctld
80
81	atf_check -o save:port-create.txt ctladm port -c -d "ioctl"
82	atf_check egrep -q "Port created successfully" port-create.txt
83	atf_check egrep -q "frontend: *ioctl" port-create.txt
84	atf_check egrep -q "port: *[0-9]+" port-create.txt
85	portnum=`awk '/port:/ {print $2}' port-create.txt`
86	atf_check -o save:portlist.txt ctladm portlist -qf ioctl
87	atf_check egrep -q "$portnum *YES *ioctl *ioctl" portlist.txt
88}
89create_ioctl_cleanup()
90{
91	cleanup ioctl
92}
93
94atf_test_case remove_ioctl_without_required_args cleanup
95remove_ioctl_without_required_args_head()
96{
97	atf_set "descr" "ctladm will gracefully fail to remove an ioctl target if required arguments are missing"
98	atf_set "require.user" "root"
99}
100remove_ioctl_without_required_args_body()
101{
102	skip_if_ctld
103
104	atf_check -o save:port-create.txt ctladm port -c -d "ioctl"
105	atf_check egrep -q "Port created successfully" port-create.txt
106	atf_check -s exit:1 -e match:"Missing required argument: port_id" ctladm port -r -d "ioctl"
107}
108remove_ioctl_without_required_args_cleanup()
109{
110	cleanup ioctl
111}
112
113atf_test_case create_iscsi cleanup
114create_iscsi_head()
115{
116	atf_set "descr" "ctladm can create a new iscsi port"
117	atf_set "require.user" "root"
118}
119create_iscsi_body()
120{
121	skip_if_ctld
122	load_cfiscsi
123
124	TARGET=iqn.2018-10.myhost.create_iscsi
125	atf_check -o save:port-create.txt ctladm port -c -d "iscsi" -O cfiscsi_portal_group_tag=$PGTAG -O cfiscsi_target="$TARGET"
126	echo "target: $TARGET" >> port-create.txt
127	atf_check egrep -q "Port created successfully" port-create.txt
128	atf_check egrep -q "frontend: *iscsi" port-create.txt
129	atf_check egrep -q "port: *[0-9]+" port-create.txt
130	atf_check -o save:portlist.txt ctladm portlist -qf iscsi
131	# Unlike the ioctl driver, the iscsi driver creates ports in a disabled
132	# state, so the port's lunmap may be set before enabling it.
133	atf_check egrep -q "$portnum *NO *iscsi *iscsi.*$TARGET" portlist.txt
134}
135create_iscsi_cleanup()
136{
137	cleanup iscsi
138}
139
140atf_test_case create_iscsi_alias cleanup
141create_iscsi_alias_head()
142{
143	atf_set "descr" "ctladm can create a new iscsi port with a target alias"
144	atf_set "require.user" "root"
145}
146create_iscsi_alias_body()
147{
148	skip_if_ctld
149	load_cfiscsi
150
151	TARGET=iqn.2018-10.myhost.create_iscsi_alias
152	ALIAS="foobar"
153	atf_check -o save:port-create.txt ctladm port -c -d "iscsi" -O cfiscsi_portal_group_tag=$PGTAG -O cfiscsi_target="$TARGET" -O cfiscsi_target_alias="$ALIAS"
154	echo "target: $TARGET" >> port-create.txt
155	atf_check egrep -q "Port created successfully" port-create.txt
156	atf_check egrep -q "frontend: *iscsi" port-create.txt
157	atf_check egrep -q "port: *[0-9]+" port-create.txt
158	atf_check -o save:portlist.txt ctladm portlist -qvf iscsi
159	atf_check egrep -q "cfiscsi_target_alias=$ALIAS" portlist.txt
160}
161create_iscsi_alias_cleanup()
162{
163	cleanup iscsi
164}
165
166atf_test_case create_iscsi_without_required_args
167create_iscsi_without_required_args_head()
168{
169	atf_set "descr" "ctladm will gracefully fail to create an iSCSI target if required arguments are missing"
170	atf_set "require.user" "root"
171}
172create_iscsi_without_required_args_body()
173{
174	skip_if_ctld
175	load_cfiscsi
176
177	TARGET=iqn.2018-10.myhost.create_iscsi
178	atf_check -s exit:1 -e match:"Missing required argument: cfiscsi_target" ctladm port -c -d "iscsi" -O cfiscsi_portal_group_tag=$PGTAG
179	atf_check -s exit:1 -e match:"Missing required argument: cfiscsi_portal_group_tag" ctladm port -c -d "iscsi" -O cfiscsi_target=$TARGET
180}
181
182atf_test_case create_ioctl_options cleanup
183create_ioctl_options_head()
184{
185	atf_set "descr" "ctladm can set options when creating a new ioctl port"
186	atf_set "require.user" "root"
187}
188create_ioctl_options_body()
189{
190	skip_if_ctld
191
192	atf_check -o save:port-create.txt ctladm port -c -d "ioctl" -O pp=101 -O vp=102
193	atf_check egrep -q "Port created successfully" port-create.txt
194	atf_check egrep -q "frontend: *ioctl" port-create.txt
195	atf_check egrep -q "port: *[0-9]+" port-create.txt
196	portnum=`awk '/port:/ {print $2}' port-create.txt`
197	atf_check -o save:portlist.txt ctladm portlist -qf ioctl
198	if ! egrep -q '101[[:space:]]+102' portlist.txt; then
199		ctladm portlist
200		atf_fail "Did not create the port with the specified options"
201	fi
202}
203create_ioctl_options_cleanup()
204{
205	cleanup ioctl
206}
207
208
209atf_test_case disable_ioctl cleanup
210disable_ioctl_head()
211{
212	atf_set "descr" "ctladm can disable an ioctl port"
213	atf_set "require.user" "root"
214}
215disable_ioctl_body()
216{
217	skip_if_ctld
218
219	atf_check -o save:port-create.txt ctladm port -c -d "ioctl"
220	portnum=`awk '/port:/ {print $2}' port-create.txt`
221	atf_check -o save:portlist.txt ctladm portlist -qf ioctl
222	atf_check -o ignore ctladm port -o off -p $portnum
223	atf_check -o match:"^$portnum *NO" ctladm portlist -qf ioctl
224}
225disable_ioctl_cleanup()
226{
227	cleanup ioctl
228}
229
230atf_test_case enable_ioctl cleanup
231enable_ioctl_head()
232{
233	atf_set "descr" "ctladm can enable an ioctl port"
234	atf_set "require.user" "root"
235}
236enable_ioctl_body()
237{
238	skip_if_ctld
239
240	atf_check -o save:port-create.txt ctladm port -c -d "ioctl"
241	portnum=`awk '/port:/ {print $2}' port-create.txt`
242	atf_check -o save:portlist.txt ctladm portlist -qf ioctl
243	atf_check -o ignore ctladm port -o off -p $portnum
244	atf_check -o ignore ctladm port -o on -p $portnum
245	atf_check -o match:"^$portnum *YES" ctladm portlist -qf ioctl
246}
247enable_ioctl_cleanup()
248{
249	cleanup ioctl
250}
251
252atf_test_case remove_ioctl
253remove_ioctl_head()
254{
255	atf_set "descr" "ctladm can remove an ioctl port"
256	atf_set "require.user" "root"
257}
258remove_ioctl_body()
259{
260	skip_if_ctld
261
262	# Specify exact pp and vp to make the post-removal portlist check
263	# unambiguous
264	atf_check -o save:port-create.txt ctladm port -c -d "ioctl" -O pp=10001 -O vp=10002
265	portnum=`awk '/port:/ {print $2}' port-create.txt`
266	atf_check -o save:portlist.txt ctladm portlist -qf ioctl
267	atf_check -o inline:"Port destroyed successfully\n" ctladm port -r -d ioctl -p $portnum
268	# Check that the port was removed.  A new port may have been added with
269	# the same ID, so match against the pp and vp numbers, too.
270	if ctladm portlist -qf ioctl | egrep -q "^${portnum} .*10001 *10002"; then
271		ctladm portlist -qf ioctl
272		atf_fail "port was not removed"
273	fi
274}
275
276atf_test_case remove_iscsi
277remove_iscsi_head()
278{
279	atf_set "descr" "ctladm can remove an iscsi port"
280	atf_set "require.user" "root"
281}
282remove_iscsi_body()
283{
284	skip_if_ctld
285	load_cfiscsi
286
287	TARGET=iqn.2018-10.myhost.remove_iscsi
288	atf_check -o save:port-create.txt ctladm port -c -d "iscsi" -O cfiscsi_portal_group_tag=$PGTAG -O cfiscsi_target="$TARGET"
289	portnum=`awk '/port:/ {print $2}' port-create.txt`
290	atf_check -o save:portlist.txt ctladm portlist -qf iscsi
291	atf_check -o inline:"Port destroyed successfully\n" ctladm port -r -d iscsi -O cfiscsi_portal_group_tag=$PGTAG -O cfiscsi_target="$TARGET"
292	# Check that the port was removed.  A new port may have been added with
293	# the same ID, so match against the target and tag, too.
294	PGTAGHEX=0x7631	# PGTAG in hex
295	if ctladm portlist -qf iscsi | egrep -q "^${portnum} .*$PGTAG +[0-9]+ +$TARGET,t,$PGTAGHEX"; then
296		ctladm portlist -qf iscsi
297		atf_fail "port was not removed"
298	fi
299}
300
301atf_test_case remove_iscsi_without_required_args cleanup
302remove_iscsi_without_required_args_head()
303{
304	atf_set "descr" "ctladm will gracefully fail to remove an iSCSI target if required arguments are missing"
305	atf_set "require.user" "root"
306}
307remove_iscsi_without_required_args_body()
308{
309	skip_if_ctld
310	load_cfiscsi
311
312	TARGET=iqn.2018-10.myhost.remove_iscsi_without_required_args
313	atf_check -o save:port-create.txt ctladm port -c -d "iscsi" -O cfiscsi_portal_group_tag=$PGTAG -O cfiscsi_target="$TARGET"
314	echo "target: $TARGET" >> port-create.txt
315	atf_check -s exit:1 -e match:"Missing required argument: cfiscsi_portal_group_tag" ctladm port -r -d iscsi -O cfiscsi_target="$TARGET"
316	atf_check -s exit:1 -e match:"Missing required argument: cfiscsi_target" ctladm port -r -d iscsi -O cfiscsi_portal_group_tag=$PGTAG
317}
318remove_iscsi_without_required_args_cleanup()
319{
320	cleanup iscsi
321}
322
323atf_init_test_cases()
324{
325	atf_add_test_case create_ioctl
326	atf_add_test_case create_iscsi
327	atf_add_test_case create_iscsi_without_required_args
328	atf_add_test_case create_iscsi_alias
329	atf_add_test_case create_ioctl_options
330	atf_add_test_case disable_ioctl
331	atf_add_test_case enable_ioctl
332	atf_add_test_case remove_ioctl
333	atf_add_test_case remove_ioctl_without_required_args
334	atf_add_test_case remove_iscsi
335	atf_add_test_case remove_iscsi_without_required_args
336}
337