]>
git.proxmox.com Git - ceph.git/blob - ceph/qa/workunits/rbd/image_read.sh
3 # Copyright (C) 2013 Inktank Storage, Inc.
5 # This is free software; see the source for copying conditions.
6 # There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR
7 # A PARTICULAR PURPOSE.
9 # This is free software; you can redistribute it and/or modify it
10 # under the terms of the GNU General Public License as
11 # published by the Free Software Foundation version 2.
13 # Alex Elder <elder@inktank.com>
16 ################################################################
18 # The purpose of this test is to validate that data read from a
19 # mapped rbd image is what it's expected to be.
21 # By default it creates an image and fills it with some data. It
22 # then reads back the data at a series of offsets known to cover
23 # various situations (such as reading the beginning, end, or the
24 # entirety of an object, or doing a read that spans multiple
25 # objects), and stashes the results in a set of local files.
27 # It also creates and maps a snapshot of the original image after
28 # it's been filled, and reads back the same ranges of data from the
29 # snapshot. It then compares the data read back with what was read
30 # back from the original image, verifying they match.
32 # Clone functionality is tested as well, in which case a clone is
33 # made of the snapshot, and the same ranges of data are again read
34 # and compared with the original. In addition, a snapshot of that
35 # clone is created, and a clone of *that* snapshot is put through
36 # the same set of tests. (Clone testing can be optionally skipped.)
38 ################################################################
40 # Default parameter values. Environment variables, if set, will
41 # supercede these defaults. Such variables have names that begin
42 # with "IMAGE_READ_", for e.g. use IMAGE_READ_PAGE_SIZE=65536
43 # to use 65536 as the page size.
47 DEFAULT_TEST_CLONES
=true
48 DEFAULT_LOCAL_FILES
=false
50 DEFAULT_DOUBLE_ORDER
=true
51 DEFAULT_HALF_ORDER
=false
52 DEFAULT_PAGE_SIZE
=4096
53 DEFAULT_OBJECT_ORDER
=22
54 MIN_OBJECT_ORDER
=12 # technically 9, but the rbd CLI enforces 12
59 PROGNAME
=$
(basename $0)
69 echo "${PROGNAME}: $@" >&2
77 echo "${PROGNAME}: $@" >&2
80 echo "Usage: ${PROGNAME} [<options>]" >&2
82 echo "options are:" >&2
83 echo " -o object_order" >&2
84 echo " must be ${MIN_OBJECT_ORDER}..${MAX_OBJECT_ORDER}" >&2
85 echo " -p page_size (in bytes)" >&2
86 echo " note: there must be at least 4 pages per object" >&2
88 echo " test using format 1 rbd images (default)" >&2
90 echo " test using format 2 rbd images" >&2
92 echo " also test rbd clone images (implies format 2)" >&2
94 echo " clone object order double its parent's (format 2)" >&2
96 echo " clone object order half of its parent's (format 2)" >&2
98 echo " use local files rather than rbd images" >&2
100 echo " disable reporting of what's going on" >&2
106 [ "${VERBOSE}" = true
] && echo "$@"
107 true
# Don't let the verbose test spoil our return value
114 function boolean_toggle
() {
115 [ $# -eq 1 ] ||
exit 99
116 test "$1" = "true" && echo false ||
echo true
119 function parseargs
() {
120 local opts
="o:p:12clv"
121 local lopts
="order:,page_size:,local,clone,verbose"
123 local clone_order_msg
125 # use values from environment if available
126 VERBOSE
="${IMAGE_READ_VERBOSE:-${DEFAULT_VERBOSE}}"
127 TEST_CLONES
="${IMAGE_READ_TEST_CLONES:-${DEFAULT_TEST_CLONES}}"
128 LOCAL_FILES
="${IMAGE_READ_LOCAL_FILES:-${DEFAULT_LOCAL_FILES}}"
129 DOUBLE_ORDER
="${IMAGE_READ_DOUBLE_ORDER:-${DEFAULT_DOUBLE_ORDER}}"
130 HALF_ORDER
="${IMAGE_READ_HALF_ORDER:-${DEFAULT_HALF_ORDER}}"
131 FORMAT
="${IMAGE_READ_FORMAT:-${DEFAULT_FORMAT}}"
132 PAGE_SIZE
="${IMAGE_READ_PAGE_SIZE:-${DEFAULT_PAGE_SIZE}}"
133 OBJECT_ORDER
="${IMAGE_READ_OBJECT_ORDER:-${DEFAULT_OBJECT_ORDER}}"
135 parsed
=$
(getopt
-o "${opts}" -l "${lopts}" -n "${PROGNAME}" -- "$@") ||
137 eval set -- "${parsed}"
141 VERBOSE
=$
(boolean_toggle
"${VERBOSE}");;
143 TEST_CLONES
=$
(boolean_toggle
"${TEST_CLONES}");;
145 DOUBLE_ORDER
=$
(boolean_toggle
"${DOUBLE_ORDER}");;
147 HALF_ORDER
=$
(boolean_toggle
"${HALF_ORDER}");;
149 LOCAL_FILES
=$
(boolean_toggle
"${LOCAL_FILES}");;
153 PAGE_SIZE
="$2"; shift;;
155 OBJECT_ORDER
="$2"; shift;;
159 err
"getopt internal error"
163 [ $# -gt 0 ] && usage
"excess arguments ($*)"
165 if [ "${TEST_CLONES}" = true
]; then
166 # If we're using different object orders for clones,
167 # make sure the limits are updated accordingly. If
168 # both "half" and "double" are specified, just
170 if [ "${DOUBLE_ORDER}" = true
]; then
171 if [ "${HALF_ORDER}" = true
]; then
175 ((MAX_OBJECT_ORDER
-= 2))
177 elif [ "${HALF_ORDER}" = true
]; then
178 ((MIN_OBJECT_ORDER
+= 2))
182 [ "${OBJECT_ORDER}" -lt "${MIN_OBJECT_ORDER}" ] &&
183 usage
"object order (${OBJECT_ORDER}) must be" \
184 "at least ${MIN_OBJECT_ORDER}"
185 [ "${OBJECT_ORDER}" -gt "${MAX_OBJECT_ORDER}" ] &&
186 usage
"object order (${OBJECT_ORDER}) must be" \
187 "at most ${MAX_OBJECT_ORDER}"
189 if [ "${TEST_CLONES}" = true
]; then
190 if [ "${DOUBLE_ORDER}" = true
]; then
191 ((CLONE1_ORDER
= OBJECT_ORDER
+ 1))
192 ((CLONE2_ORDER
= OBJECT_ORDER
+ 2))
193 clone_order_msg
="double"
194 elif [ "${HALF_ORDER}" = true
]; then
195 ((CLONE1_ORDER
= OBJECT_ORDER
- 1))
196 ((CLONE2_ORDER
= OBJECT_ORDER
- 2))
197 clone_order_msg
="half of"
199 CLONE1_ORDER
="${OBJECT_ORDER}"
200 CLONE2_ORDER
="${OBJECT_ORDER}"
201 clone_order_msg
="the same as"
205 [ "${TEST_CLONES}" != true
] || FORMAT
=2
207 OBJECT_SIZE
=$
(echo "2 ^ ${OBJECT_ORDER}" |
bc)
208 OBJECT_PAGES
=$
(echo "${OBJECT_SIZE} / ${PAGE_SIZE}" |
bc)
209 IMAGE_SIZE
=$
((2 * 16 * OBJECT_SIZE
/ (1024 * 1024)))
210 [ "${IMAGE_SIZE}" -lt 1 ] && IMAGE_SIZE
=1
211 IMAGE_OBJECTS
=$
((IMAGE_SIZE
* (1024 * 1024) / OBJECT_SIZE
))
213 [ "${OBJECT_PAGES}" -lt 4 ] &&
214 usage
"object size (${OBJECT_SIZE}) must be" \
215 "at least 4 * page size (${PAGE_SIZE})"
217 echo "parameters for this run:"
218 echo " format ${FORMAT} images will be tested"
219 echo " object order is ${OBJECT_ORDER}, so" \
220 "objects are ${OBJECT_SIZE} bytes"
221 echo " page size is ${PAGE_SIZE} bytes, so" \
222 "there are are ${OBJECT_PAGES} pages in an object"
223 echo " derived image size is ${IMAGE_SIZE} MB, so" \
224 "there are ${IMAGE_OBJECTS} objects in an image"
225 if [ "${TEST_CLONES}" = true
]; then
226 echo " clone functionality will be tested"
227 echo " object size for a clone will be ${clone_order_msg}"
228 echo " the object size of its parent image"
231 true
# Don't let the clones test spoil our return value
234 function image_dev_path
() {
235 [ $# -eq 1 ] ||
exit 99
236 local image_name
="$1"
238 if [ "${LOCAL_FILES}" = true
]; then
239 echo "${TEMP}/${image_name}"
243 echo "/dev/rbd/rbd/${image_name}"
246 function out_data_dir
() {
247 [ $# -lt 2 ] ||
exit 99
248 local out_data
="${TEMP}/data"
251 if [ $# -eq 1 ]; then
253 echo "${out_data}/${image_name}"
260 verbose
"===== setting up ====="
261 TEMP
=$
(mktemp
-d /tmp
/rbd_image_read.XXXXX
)
262 mkdir
-p $
(out_data_dir
)
264 # create and fill the original image with some data
265 create_image
"${ORIGINAL}"
266 map_image
"${ORIGINAL}"
269 # create a snapshot of the original
270 create_image_snap
"${ORIGINAL}" "${SNAP1}"
271 map_image_snap
"${ORIGINAL}" "${SNAP1}"
273 if [ "${TEST_CLONES}" = true
]; then
274 # create a clone of the original snapshot
275 create_snap_clone
"${ORIGINAL}" "${SNAP1}" \
276 "${CLONE1}" "${CLONE1_ORDER}"
277 map_image
"${CLONE1}"
279 # create a snapshot of that clone
280 create_image_snap
"${CLONE1}" "${SNAP2}"
281 map_image_snap
"${CLONE1}" "${SNAP2}"
283 # create a clone of that clone's snapshot
284 create_snap_clone
"${CLONE1}" "${SNAP2}" \
285 "${CLONE2}" "${CLONE2_ORDER}"
286 map_image
"${CLONE2}"
290 function teardown
() {
291 verbose
"===== cleaning up ====="
292 if [ "${TEST_CLONES}" = true
]; then
293 unmap_image
"${CLONE2}" || true
294 destroy_snap_clone
"${CLONE1}" "${SNAP2}" "${CLONE2}" || true
296 unmap_image_snap
"${CLONE1}" "${SNAP2}" || true
297 destroy_image_snap
"${CLONE1}" "${SNAP2}" || true
299 unmap_image
"${CLONE1}" || true
300 destroy_snap_clone
"${ORIGINAL}" "${SNAP1}" "${CLONE1}" || true
302 unmap_image_snap
"${ORIGINAL}" "${SNAP1}" || true
303 destroy_image_snap
"${ORIGINAL}" "${SNAP1}" || true
304 unmap_image
"${ORIGINAL}" || true
305 destroy_image
"${ORIGINAL}" || true
307 rm -rf $
(out_data_dir
)
311 function create_image
() {
312 [ $# -eq 1 ] ||
exit 99
313 local image_name
="$1"
317 verbose
"creating image \"${image_name}\""
318 if [ "${LOCAL_FILES}" = true
]; then
319 image_path
=$
(image_dev_path
"${image_name}")
320 bytes
=$
(echo "${IMAGE_SIZE} * 1024 * 1024 - 1" |
bc)
321 quiet
dd if=/dev
/zero bs
=1 count
=1 seek
="${bytes}" \
326 rbd create
"${image_name}" --image-format "${FORMAT}" \
327 --size "${IMAGE_SIZE}" --order "${OBJECT_ORDER}" \
331 function destroy_image
() {
332 [ $# -eq 1 ] ||
exit 99
333 local image_name
="$1"
336 verbose
"destroying image \"${image_name}\""
337 if [ "${LOCAL_FILES}" = true
]; then
338 image_path
=$
(image_dev_path
"${image_name}")
339 rm -f "${image_path}"
343 rbd
rm "${image_name}"
346 function map_image
() {
347 [ $# -eq 1 ] ||
exit 99
348 local image_name
="$1" # can be image@snap too
350 if [ "${LOCAL_FILES}" = true
]; then
354 sudo rbd map
"${image_name}"
357 function unmap_image
() {
358 [ $# -eq 1 ] ||
exit 99
359 local image_name
="$1" # can be image@snap too
362 if [ "${LOCAL_FILES}" = true
]; then
365 image_path
=$
(image_dev_path
"${image_name}")
367 if [ -e "${image_path}" ]; then
368 sudo rbd unmap
"${image_path}"
372 function map_image_snap
() {
373 [ $# -eq 2 ] ||
exit 99
374 local image_name
="$1"
378 if [ "${LOCAL_FILES}" = true
]; then
382 image_snap
="${image_name}@${snap_name}"
383 map_image
"${image_snap}"
386 function unmap_image_snap
() {
387 [ $# -eq 2 ] ||
exit 99
388 local image_name
="$1"
392 if [ "${LOCAL_FILES}" = true
]; then
396 image_snap
="${image_name}@${snap_name}"
397 unmap_image
"${image_snap}"
400 function create_image_snap
() {
401 [ $# -eq 2 ] ||
exit 99
402 local image_name
="$1"
404 local image_snap
="${image_name}@${snap_name}"
408 verbose
"creating snapshot \"${snap_name}\"" \
409 "of image \"${image_name}\""
410 if [ "${LOCAL_FILES}" = true
]; then
411 image_path
=$
(image_dev_path
"${image_name}")
412 snap_path
=$
(image_dev_path
"${image_snap}")
414 cp "${image_path}" "${snap_path}"
418 rbd snap create
"${image_snap}"
421 function destroy_image_snap
() {
422 [ $# -eq 2 ] ||
exit 99
423 local image_name
="$1"
425 local image_snap
="${image_name}@${snap_name}"
428 verbose
"destroying snapshot \"${snap_name}\"" \
429 "of image \"${image_name}\""
430 if [ "${LOCAL_FILES}" = true
]; then
431 snap_path
=$
(image_dev_path
"${image_snap}")
432 rm -rf "${snap_path}"
436 rbd snap
rm "${image_snap}"
439 function create_snap_clone
() {
440 [ $# -eq 4 ] ||
exit 99
441 local image_name
="$1"
443 local clone_name
="$3"
444 local clone_order
="$4"
445 local image_snap
="${image_name}@${snap_name}"
449 verbose
"creating clone image \"${clone_name}\"" \
450 "of image snapshot \"${image_name}@${snap_name}\""
451 if [ "${LOCAL_FILES}" = true
]; then
452 snap_path
=$
(image_dev_path
"${image_name}@${snap_name}")
453 clone_path
=$
(image_dev_path
"${clone_name}")
455 cp "${snap_path}" "${clone_path}"
459 rbd snap protect
"${image_snap}"
460 rbd clone
--order "${clone_order}" --image-shared \
461 "${image_snap}" "${clone_name}"
464 function destroy_snap_clone
() {
465 [ $# -eq 3 ] ||
exit 99
466 local image_name
="$1"
468 local clone_name
="$3"
469 local image_snap
="${image_name}@${snap_name}"
472 verbose
"destroying clone image \"${clone_name}\""
473 if [ "${LOCAL_FILES}" = true
]; then
474 clone_path
=$
(image_dev_path
"${clone_name}")
476 rm -rf "${clone_path}"
480 rbd
rm "${clone_name}"
481 rbd snap unprotect
"${image_snap}"
484 # function that produces "random" data with which to fill the image
485 function source_data
() {
486 while quiet
dd if=/bin
/bash skip
=$
(($$
% 199)) bs
="${PAGE_SIZE}"; do
491 function fill_original
() {
492 local image_path
=$
(image_dev_path
"${ORIGINAL}")
494 verbose
"filling original image"
495 # Fill 16 objects worth of "random" data
497 quiet
dd bs
="${PAGE_SIZE}" count
=$
((16 * OBJECT_PAGES
)) \
502 [ $# -eq 3 -o $# -eq 4 ] ||
exit 99
503 local image_name
="$1"
506 [ "${length}" -gt 0 ] || err
"do_read: length must be non-zero"
507 local image_path
=$
(image_dev_path
"${image_name}")
508 local out_data
=$
(out_data_dir
"${image_name}")
509 local range
=$
(printf "%06u~%04u" "${offset}" "${length}")
512 [ $# -eq 4 ] && offset
=$
((offset
+ 16 * OBJECT_PAGES
))
514 verbose
"reading \"${image_name}\" pages ${range}"
516 out_file
="${out_data}/pages_${range}"
518 quiet
dd bs
="${PAGE_SIZE}" skip="${offset}" count="${length}" \
519 if="${image_path}" of
="${out_file}"
522 function one_pass
() {
523 [ $# -eq 1 -o $# -eq 2 ] ||
exit 99
524 local image_name
="$1"
526 [ $# -eq 2 ] && extended
="true"
532 # +-----------+-----------+---
533 # |X:X:X...X:X| : : ... : | :
534 # +-----------+-----------+---
535 length
="${OBJECT_PAGES}"
536 do_read
"${image_name}" "${offset}" "${length}" ${extended}
537 offset=$((offset + length))
539 # ---+-----------+---
541 # ---+-----------+---
543 do_read "${image_name}" "${offset}" "${length}" ${extended}
544 offset
=$
((offset
+ length
))
546 # ---+-----------+---
548 # ---+-----------+---
550 do_read
"${image_name}" "${offset}" "${length}" ${extended}
551 offset=$((offset + length))
553 # ---+-----------+---
555 # ---+-----------+---
556 length=$((OBJECT_PAGES - 3))
557 do_read "${image_name}" "${offset}" "${length}" ${extended}
558 offset
=$
((offset
+ length
))
560 # ---+-----------+---
562 # ---+-----------+---
564 do_read
"${image_name}" "${offset}" "${length}" ${extended}
565 offset=$((offset + length))
567 # ---+-----------+---
569 # ---+-----------+---
570 length="${OBJECT_PAGES}"
571 do_read "${image_name}" "${offset}" "${length}" ${extended}
572 offset
=$
((offset
+ length
))
574 offset
=$
((offset
+ 1)) # skip 1
576 # ---+-----------+---
578 # ---+-----------+---
579 length
=$
((OBJECT_PAGES
- 1))
580 do_read
"${image_name}" "${offset}" "${length}" ${extended}
581 offset=$((offset + length))
583 # ---+-----------+-----------+---
584 # : |X:X:X...X:X|X: : ... : | :
585 # ---+-----------+-----------+---
586 length=$((OBJECT_PAGES + 1))
587 do_read "${image_name}" "${offset}" "${length}" ${extended}
588 offset
=$
((offset
+ length
))
590 # ---+-----------+-----------+---
591 # : | :X:X...X:X|X: : ... : | :
592 # ---+-----------+-----------+---
593 length
="${OBJECT_PAGES}"
594 do_read
"${image_name}" "${offset}" "${length}" ${extended}
595 offset=$((offset + length))
597 # ---+-----------+-----------+---
598 # : | :X:X...X:X|X:X: ... : | :
599 # ---+-----------+-----------+---
600 length=$((OBJECT_PAGES + 1))
601 do_read "${image_name}" "${offset}" "${length}" ${extended}
602 offset
=$
((offset
+ length
))
604 # ---+-----------+-----------+---
605 # : | : :X...X:X|X:X:X...X:X| :
606 # ---+-----------+-----------+---
607 length
=$
((2 * OBJECT_PAGES
+ 2))
608 do_read
"${image_name}" "${offset}" "${length}" ${extended}
609 offset=$((offset + length))
611 offset=$((offset + 1)) # skip 1
613 # ---+-----------+-----------+-----
614 # : | :X:X...X:X|X:X:X...X:X|X: :
615 # ---+-----------+-----------+-----
616 length=$((2 * OBJECT_PAGES))
617 do_read "${image_name}" "${offset}" "${length}" ${extended}
618 offset
=$
((offset
+ length
))
620 # --+-----------+-----------+--------
621 # : | :X:X...X:X|X:X:X...X:X|X:X: :
622 # --+-----------+-----------+--------
624 length
=$
((2 * OBJECT_PAGES
+ 1))
625 do_read
"${image_name}" "${offset}" "${length}" ${extended}
626 # offset=$((offset + length))
629 function run_using() {
630 [ $# -eq 1 ] || exit 99
631 local image_name="$1"
632 local out_data=$(out_data_dir "${image_name}")
634 verbose "===== running using
\"${image_name}\" ====="
635 mkdir -p "${out_data}"
636 one_pass "${image_name}"
637 one_pass "${image_name}" extended
641 [ $# -eq 1 ] || exit 99
642 local image_name="$1"
643 local out_data=$(out_data_dir "${image_name}")
644 local original=$(out_data_dir "${ORIGINAL}")
646 verbose "===== comparing
\"${image_name}\" ====="
647 for i in $(ls "${original}"); do
648 verbose compare "\"${image_name}\" \"${i}\""
649 cmp "${original}/${i}" "${out_data}/${i}"
651 [ "${image_name}" = "${ORIGINAL}" ] || rm -rf "${out_data}"
655 [ $# -eq 1 ] || exit 99
656 local image_name="$1"
658 run_using "${image_name}"
659 compare "${image_name}"
666 trap teardown EXIT HUP INT
669 run_using "${ORIGINAL}"
670 doit "${ORIGINAL}@
${SNAP1}"
671 if [ "${TEST_CLONES}" = true ]; then
673 doit "${CLONE1}@
${SNAP2}"
676 rm -rf $(out_data_dir "${ORIGINAL}")