xref: /freebsd/usr.sbin/zonectl/tests/zonectl_test.sh (revision a259b98fa211ed87bfee58c575de4e2de94ee0fa)
1# SPDX-License-Identifier: BSD-2-Clause
2#
3# Copyright (c) 2026 voidanix <voidanix@FreeBSD.org>
4#
5
6MD_DEV_FILE="md.dev"
7
8alloc_zoned_md()
9{
10	atf_check truncate -s 1025m backing_file
11	md=$(mdconfig -a -t vnode -f backing_file) || \
12	    atf_fail "mdconfig -a failed"
13	echo ${md} > $MD_DEV_FILE
14}
15
16# Print the number of zones matching a report option.
17zone_count()
18{
19	zonectl -d /dev/${md}.zoned -c rz -o $1 -P summary | \
20	    awk '/zones,/ {print $1}'
21}
22
23zoned_md_cleanup()
24{
25	if [ -f "$MD_DEV_FILE" ]; then
26		read md < $MD_DEV_FILE
27		[ -c /dev/${md}.zoned ] && \
28		    gzoned destroy ${md}.zoned 2>/dev/null
29		mdconfig -d -u ${md} 2>/dev/null
30	fi
31	true
32}
33
34atf_test_case report_filter cleanup
35report_filter_head()
36{
37	atf_set "descr" "zonectl -o only reports zones matching the filter"
38	atf_set "require.user" "root"
39}
40report_filter_body()
41{
42	alloc_zoned_md
43	atf_check gzoned create -s 256m -c 0-1 ${md}
44
45	atf_check_equal "2" \
46	    "$(zonectl -d /dev/${md}.zoned -c rz -o nonwp -P summary | \
47	    awk '/zones,/ {print $1}')"
48	atf_check_equal "2" \
49	    "$(zonectl -d /dev/${md}.zoned -c rz -o empty -P summary | \
50	    awk '/zones,/ {print $1}')"
51	atf_check_equal "0" \
52	    "$(zonectl -d /dev/${md}.zoned -c rz -o full -P summary | \
53	    awk '/zones,/ {print $1}')"
54}
55report_filter_cleanup()
56{
57	zoned_md_cleanup
58}
59
60atf_test_case report_zones cleanup
61report_zones_head()
62{
63	atf_set "descr" "zonectl -c rz reports every zone"
64	atf_set "require.user" "root"
65}
66report_zones_body()
67{
68	alloc_zoned_md
69	atf_check gzoned create -s 256m ${md}
70	atf_check -o match:"^4 zones," \
71	    zonectl -d /dev/${md}.zoned -c rz -P summary
72	atf_check_equal "4" \
73	    "$(zonectl -d /dev/${md}.zoned -c rz -P script | \
74	    awk 'END {print NR}')"
75}
76report_zones_cleanup()
77{
78	zoned_md_cleanup
79}
80
81atf_test_case report_starting_lba cleanup
82report_starting_lba_head()
83{
84	atf_set "descr" "zonectl -c rz -l reports zones from a starting LBA"
85	atf_set "require.user" "root"
86}
87report_starting_lba_body()
88{
89	alloc_zoned_md
90	atf_check gzoned create -s 256m ${md}
91	# Only zones 2 and 3 lie at or past LBA 0x100000 (512m).
92	atf_check_equal "2" \
93	    "$(zonectl -d /dev/${md}.zoned -c rz -l 0x100000 -P summary | \
94	    awk '/zones,/ {print $1}')"
95	atf_check_equal "0x100000" \
96	    "$(zonectl -d /dev/${md}.zoned -c rz -l 0x100000 -P script | \
97	    awk -F',' 'NR == 1 {gsub(/ /, "", $1); print $1}')"
98}
99report_starting_lba_cleanup()
100{
101	zoned_md_cleanup
102}
103
104atf_test_case report_params cleanup
105report_params_head()
106{
107	atf_set "descr" "zonectl -c params reports the device parameters"
108	atf_set "require.user" "root"
109}
110report_params_body()
111{
112	alloc_zoned_md
113	atf_check gzoned create -s 256m -m 3 ${md}
114	atf_check -o match:"Zone Mode: Host Managed" \
115	    -o match:"URSWRZ.*: Yes" \
116	    -o match:"Open Sequential Write Required Zones: 3" \
117	    zonectl -d /dev/${md}.zoned -c params
118}
119report_params_cleanup()
120{
121	zoned_md_cleanup
122}
123
124atf_test_case open_zone cleanup
125open_zone_head()
126{
127	atf_set "descr" "zonectl -c open explicitly opens a zone"
128	atf_set "require.user" "root"
129}
130open_zone_body()
131{
132	alloc_zoned_md
133	atf_check gzoned create -s 256m ${md}
134	atf_check zonectl -d /dev/${md}.zoned -c open -l 0
135	atf_check_equal "1" "$(zone_count exp_open)"
136}
137open_zone_cleanup()
138{
139	zoned_md_cleanup
140}
141
142atf_test_case close_zone cleanup
143close_zone_head()
144{
145	atf_set "descr" "zonectl -c close closes an open zone"
146	atf_set "require.user" "root"
147}
148close_zone_body()
149{
150	alloc_zoned_md
151	atf_check gzoned create -s 256m ${md}
152	atf_check -e ignore dd if=/dev/zero of=/dev/${md}.zoned bs=1m count=1
153	atf_check_equal "1" "$(zone_count imp_open)"
154	atf_check zonectl -d /dev/${md}.zoned -c close -l 0
155	atf_check_equal "1" "$(zone_count closed)"
156	atf_check_equal "0" "$(zone_count imp_open)"
157}
158close_zone_cleanup()
159{
160	zoned_md_cleanup
161}
162
163atf_test_case finish_zone cleanup
164finish_zone_head()
165{
166	atf_set "descr" "zonectl -c finish transitions a zone to full"
167	atf_set "require.user" "root"
168}
169finish_zone_body()
170{
171	alloc_zoned_md
172	atf_check gzoned create -s 256m ${md}
173	atf_check -e ignore dd if=/dev/zero of=/dev/${md}.zoned bs=1m count=1
174	atf_check zonectl -d /dev/${md}.zoned -c finish -l 0
175	atf_check_equal "1" "$(zone_count full)"
176}
177finish_zone_cleanup()
178{
179	zoned_md_cleanup
180}
181
182atf_test_case zone_state_handling cleanup
183zone_state_handling_head()
184{
185	atf_set "descr" \
186	    "zone commands follow ZBC spec outside their transition states"
187	atf_set "require.user" "root"
188}
189zone_state_handling_body()
190{
191	alloc_zoned_md
192	atf_check gzoned create -s 256m -c 0 ${md}
193
194	# Explicitly targeting a conventional zone is the caller's error.
195	atf_check -s not-exit:0 -e ignore \
196	    zonectl -d /dev/${md}.zoned -c finish -l 0
197	# Closing an empty zone is a no-op.
198	atf_check zonectl -d /dev/${md}.zoned -c close -l 0x80000
199	atf_check_equal "3" "$(zone_count empty)"
200	# So is opening a full zone: it must stay full.
201	atf_check zonectl -d /dev/${md}.zoned -c finish -l 0x100000
202	atf_check zonectl -d /dev/${md}.zoned -c open -l 0x100000
203	atf_check_equal "1" "$(zone_count full)"
204	atf_check_equal "0" "$(zone_count exp_open)"
205}
206zone_state_handling_cleanup()
207{
208	zoned_md_cleanup
209}
210
211atf_init_test_cases()
212{
213	atf_add_test_case report_filter
214	atf_add_test_case report_zones
215	atf_add_test_case report_starting_lba
216	atf_add_test_case report_params
217	atf_add_test_case open_zone
218	atf_add_test_case close_zone
219	atf_add_test_case finish_zone
220	atf_add_test_case zone_state_handling
221}
222