]>
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.
46 DEFAULT_TEST_CLONES
=true
47 DEFAULT_LOCAL_FILES
=false
49 DEFAULT_DOUBLE_ORDER
=true
50 DEFAULT_HALF_ORDER
=false
51 DEFAULT_PAGE_SIZE
=4096
52 DEFAULT_OBJECT_ORDER
=22
53 MIN_OBJECT_ORDER
=12 # technically 9, but the rbd CLI enforces 12
56 PROGNAME
=$
(basename $0)
66 echo "${PROGNAME}: $@" >&2
74 echo "${PROGNAME}: $@" >&2
77 echo "Usage: ${PROGNAME} [<options>]" >&2
79 echo "options are:" >&2
80 echo " -o object_order" >&2
81 echo " must be ${MIN_OBJECT_ORDER}..${MAX_OBJECT_ORDER}" >&2
82 echo " -p page_size (in bytes)" >&2
83 echo " note: there must be at least 4 pages per object" >&2
85 echo " test using format 1 rbd images (default)" >&2
87 echo " test using format 2 rbd images" >&2
89 echo " also test rbd clone images (implies format 2)" >&2
91 echo " clone object order double its parent's (format 2)" >&2
93 echo " clone object order half of its parent's (format 2)" >&2
95 echo " use local files rather than rbd images" >&2
97 echo " disable reporting of what's going on" >&2
103 [ "${VERBOSE}" = true
] && echo "$@"
104 true
# Don't let the verbose test spoil our return value
111 function boolean_toggle
() {
112 [ $# -eq 1 ] ||
exit 99
113 test "$1" = "true" && echo false ||
echo true
116 function parseargs
() {
117 local opts
="o:p:12clv"
118 local lopts
="order:,page_size:,local,clone,verbose"
120 local clone_order_msg
122 # use values from environment if available
123 VERBOSE
="${IMAGE_READ_VERBOSE:-${DEFAULT_VERBOSE}}"
124 TEST_CLONES
="${IMAGE_READ_TEST_CLONES:-${DEFAULT_TEST_CLONES}}"
125 LOCAL_FILES
="${IMAGE_READ_LOCAL_FILES:-${DEFAULT_LOCAL_FILES}}"
126 DOUBLE_ORDER
="${IMAGE_READ_DOUBLE_ORDER:-${DEFAULT_DOUBLE_ORDER}}"
127 HALF_ORDER
="${IMAGE_READ_HALF_ORDER:-${DEFAULT_HALF_ORDER}}"
128 FORMAT
="${IMAGE_READ_FORMAT:-${DEFAULT_FORMAT}}"
129 PAGE_SIZE
="${IMAGE_READ_PAGE_SIZE:-${DEFAULT_PAGE_SIZE}}"
130 OBJECT_ORDER
="${IMAGE_READ_OBJECT_ORDER:-${DEFAULT_OBJECT_ORDER}}"
132 parsed
=$
(getopt
-o "${opts}" -l "${lopts}" -n "${PROGNAME}" -- "$@") ||
134 eval set -- "${parsed}"
138 VERBOSE
=$
(boolean_toggle
"${VERBOSE}");;
140 TEST_CLONES
=$
(boolean_toggle
"${TEST_CLONES}");;
142 DOUBLE_ORDER
=$
(boolean_toggle
"${DOUBLE_ORDER}");;
144 HALF_ORDER
=$
(boolean_toggle
"${HALF_ORDER}");;
146 LOCAL_FILES
=$
(boolean_toggle
"${LOCAL_FILES}");;
150 PAGE_SIZE
="$2"; shift;;
152 OBJECT_ORDER
="$2"; shift;;
156 err
"getopt internal error"
160 [ $# -gt 0 ] && usage
"excess arguments ($*)"
162 if [ "${TEST_CLONES}" = true
]; then
163 # If we're using different object orders for clones,
164 # make sure the limits are updated accordingly. If
165 # both "half" and "double" are specified, just
167 if [ "${DOUBLE_ORDER}" = true
]; then
168 if [ "${HALF_ORDER}" = true
]; then
172 ((MAX_OBJECT_ORDER
-= 2))
174 elif [ "${HALF_ORDER}" = true
]; then
175 ((MIN_OBJECT_ORDER
+= 2))
179 [ "${OBJECT_ORDER}" -lt "${MIN_OBJECT_ORDER}" ] &&
180 usage
"object order (${OBJECT_ORDER}) must be" \
181 "at least ${MIN_OBJECT_ORDER}"
182 [ "${OBJECT_ORDER}" -gt "${MAX_OBJECT_ORDER}" ] &&
183 usage
"object order (${OBJECT_ORDER}) must be" \
184 "at most ${MAX_OBJECT_ORDER}"
186 if [ "${TEST_CLONES}" = true
]; then
187 if [ "${DOUBLE_ORDER}" = true
]; then
188 ((CLONE1_ORDER
= OBJECT_ORDER
+ 1))
189 ((CLONE2_ORDER
= OBJECT_ORDER
+ 2))
190 clone_order_msg
="double"
191 elif [ "${HALF_ORDER}" = true
]; then
192 ((CLONE1_ORDER
= OBJECT_ORDER
- 1))
193 ((CLONE2_ORDER
= OBJECT_ORDER
- 2))
194 clone_order_msg
="half of"
196 CLONE1_ORDER
="${OBJECT_ORDER}"
197 CLONE2_ORDER
="${OBJECT_ORDER}"
198 clone_order_msg
="the same as"
202 [ "${TEST_CLONES}" != true
] || FORMAT
=2
204 OBJECT_SIZE
=$
(echo "2 ^ ${OBJECT_ORDER}" |
bc)
205 OBJECT_PAGES
=$
(echo "${OBJECT_SIZE} / ${PAGE_SIZE}" |
bc)
206 IMAGE_SIZE
=$
((2 * 16 * OBJECT_SIZE
/ (1024 * 1024)))
207 [ "${IMAGE_SIZE}" -lt 1 ] && IMAGE_SIZE
=1
208 IMAGE_OBJECTS
=$
((IMAGE_SIZE
* (1024 * 1024) / OBJECT_SIZE
))
210 [ "${OBJECT_PAGES}" -lt 4 ] &&
211 usage
"object size (${OBJECT_SIZE}) must be" \
212 "at least 4 * page size (${PAGE_SIZE})"
214 echo "parameters for this run:"
215 echo " format ${FORMAT} images will be tested"
216 echo " object order is ${OBJECT_ORDER}, so" \
217 "objects are ${OBJECT_SIZE} bytes"
218 echo " page size is ${PAGE_SIZE} bytes, so" \
219 "there are are ${OBJECT_PAGES} pages in an object"
220 echo " derived image size is ${IMAGE_SIZE} MB, so" \
221 "there are ${IMAGE_OBJECTS} objects in an image"
222 if [ "${TEST_CLONES}" = true
]; then
223 echo " clone functionality will be tested"
224 echo " object size for a clone will be ${clone_order_msg}"
225 echo " the object size of its parent image"
228 true
# Don't let the clones test spoil our return value
231 function image_dev_path
() {
232 [ $# -eq 1 ] ||
exit 99
233 local image_name
="$1"
235 if [ "${LOCAL_FILES}" = true
]; then
236 echo "${TEMP}/${image_name}"
240 echo "/dev/rbd/rbd/${image_name}"
243 function out_data_dir
() {
244 [ $# -lt 2 ] ||
exit 99
245 local out_data
="${TEMP}/data"
248 if [ $# -eq 1 ]; then
250 echo "${out_data}/${image_name}"
257 verbose
"===== setting up ====="
258 TEMP
=$
(mktemp
-d /tmp
/rbd_image_read.XXXXX
)
259 mkdir
-p $
(out_data_dir
)
261 # create and fill the original image with some data
262 create_image
"${ORIGINAL}"
263 map_image
"${ORIGINAL}"
266 # create a snapshot of the original
267 create_image_snap
"${ORIGINAL}" "${SNAP1}"
268 map_image_snap
"${ORIGINAL}" "${SNAP1}"
270 if [ "${TEST_CLONES}" = true
]; then
271 # create a clone of the original snapshot
272 create_snap_clone
"${ORIGINAL}" "${SNAP1}" \
273 "${CLONE1}" "${CLONE1_ORDER}"
274 map_image
"${CLONE1}"
276 # create a snapshot of that clone
277 create_image_snap
"${CLONE1}" "${SNAP2}"
278 map_image_snap
"${CLONE1}" "${SNAP2}"
280 # create a clone of that clone's snapshot
281 create_snap_clone
"${CLONE1}" "${SNAP2}" \
282 "${CLONE2}" "${CLONE2_ORDER}"
283 map_image
"${CLONE2}"
287 function teardown
() {
288 verbose
"===== cleaning up ====="
289 if [ "${TEST_CLONES}" = true
]; then
290 unmap_image
"${CLONE2}" || true
291 destroy_snap_clone
"${CLONE1}" "${SNAP2}" "${CLONE2}" || true
293 unmap_image_snap
"${CLONE1}" "${SNAP2}" || true
294 destroy_image_snap
"${CLONE1}" "${SNAP2}" || true
296 unmap_image
"${CLONE1}" || true
297 destroy_snap_clone
"${ORIGINAL}" "${SNAP1}" "${CLONE1}" || true
299 unmap_image_snap
"${ORIGINAL}" "${SNAP1}" || true
300 destroy_image_snap
"${ORIGINAL}" "${SNAP1}" || true
301 unmap_image
"${ORIGINAL}" || true
302 destroy_image
"${ORIGINAL}" || true
304 rm -rf $
(out_data_dir
)
308 function create_image
() {
309 [ $# -eq 1 ] ||
exit 99
310 local image_name
="$1"
314 verbose
"creating image \"${image_name}\""
315 if [ "${LOCAL_FILES}" = true
]; then
316 image_path
=$
(image_dev_path
"${image_name}")
317 bytes
=$
(echo "${IMAGE_SIZE} * 1024 * 1024 - 1" |
bc)
318 quiet
dd if=/dev
/zero bs
=1 count
=1 seek
="${bytes}" \
323 rbd create
"${image_name}" --image-format "${FORMAT}" \
324 --size "${IMAGE_SIZE}" --order "${OBJECT_ORDER}" \
328 function destroy_image
() {
329 [ $# -eq 1 ] ||
exit 99
330 local image_name
="$1"
333 verbose
"destroying image \"${image_name}\""
334 if [ "${LOCAL_FILES}" = true
]; then
335 image_path
=$
(image_dev_path
"${image_name}")
336 rm -f "${image_path}"
340 rbd
rm "${image_name}"
343 function map_image
() {
344 [ $# -eq 1 ] ||
exit 99
345 local image_name
="$1" # can be image@snap too
347 if [ "${LOCAL_FILES}" = true
]; then
351 sudo rbd map
"${image_name}"
354 function unmap_image
() {
355 [ $# -eq 1 ] ||
exit 99
356 local image_name
="$1" # can be image@snap too
359 if [ "${LOCAL_FILES}" = true
]; then
362 image_path
=$
(image_dev_path
"${image_name}")
364 if [ -e "${image_path}" ]; then
365 sudo rbd unmap
"${image_path}"
369 function map_image_snap
() {
370 [ $# -eq 2 ] ||
exit 99
371 local image_name
="$1"
375 if [ "${LOCAL_FILES}" = true
]; then
379 image_snap
="${image_name}@${snap_name}"
380 map_image
"${image_snap}"
383 function unmap_image_snap
() {
384 [ $# -eq 2 ] ||
exit 99
385 local image_name
="$1"
389 if [ "${LOCAL_FILES}" = true
]; then
393 image_snap
="${image_name}@${snap_name}"
394 unmap_image
"${image_snap}"
397 function create_image_snap
() {
398 [ $# -eq 2 ] ||
exit 99
399 local image_name
="$1"
401 local image_snap
="${image_name}@${snap_name}"
405 verbose
"creating snapshot \"${snap_name}\"" \
406 "of image \"${image_name}\""
407 if [ "${LOCAL_FILES}" = true
]; then
408 image_path
=$
(image_dev_path
"${image_name}")
409 snap_path
=$
(image_dev_path
"${image_snap}")
411 cp "${image_path}" "${snap_path}"
415 rbd snap create
"${image_snap}"
418 function destroy_image_snap
() {
419 [ $# -eq 2 ] ||
exit 99
420 local image_name
="$1"
422 local image_snap
="${image_name}@${snap_name}"
425 verbose
"destroying snapshot \"${snap_name}\"" \
426 "of image \"${image_name}\""
427 if [ "${LOCAL_FILES}" = true
]; then
428 snap_path
=$
(image_dev_path
"${image_snap}")
429 rm -rf "${snap_path}"
433 rbd snap
rm "${image_snap}"
436 function create_snap_clone
() {
437 [ $# -eq 4 ] ||
exit 99
438 local image_name
="$1"
440 local clone_name
="$3"
441 local clone_order
="$4"
442 local image_snap
="${image_name}@${snap_name}"
446 verbose
"creating clone image \"${clone_name}\"" \
447 "of image snapshot \"${image_name}@${snap_name}\""
448 if [ "${LOCAL_FILES}" = true
]; then
449 snap_path
=$
(image_dev_path
"${image_name}@${snap_name}")
450 clone_path
=$
(image_dev_path
"${clone_name}")
452 cp "${snap_path}" "${clone_path}"
456 rbd snap protect
"${image_snap}"
457 rbd clone
--order "${clone_order}" --image-shared \
458 "${image_snap}" "${clone_name}"
461 function destroy_snap_clone
() {
462 [ $# -eq 3 ] ||
exit 99
463 local image_name
="$1"
465 local clone_name
="$3"
466 local image_snap
="${image_name}@${snap_name}"
469 verbose
"destroying clone image \"${clone_name}\""
470 if [ "${LOCAL_FILES}" = true
]; then
471 clone_path
=$
(image_dev_path
"${clone_name}")
473 rm -rf "${clone_path}"
477 rbd
rm "${clone_name}"
478 rbd snap unprotect
"${image_snap}"
481 # function that produces "random" data with which to fill the image
482 function source_data
() {
483 while quiet
dd if=/bin
/bash skip
=$
(($$
% 199)) bs
="${PAGE_SIZE}"; do
488 function fill_original
() {
489 local image_path
=$
(image_dev_path
"${ORIGINAL}")
491 verbose
"filling original image"
492 # Fill 16 objects worth of "random" data
494 quiet
dd bs
="${PAGE_SIZE}" count
=$
((16 * OBJECT_PAGES
)) \
499 [ $# -eq 3 -o $# -eq 4 ] ||
exit 99
500 local image_name
="$1"
503 [ "${length}" -gt 0 ] || err
"do_read: length must be non-zero"
504 local image_path
=$
(image_dev_path
"${image_name}")
505 local out_data
=$
(out_data_dir
"${image_name}")
506 local range
=$
(printf "%06u~%04u" "${offset}" "${length}")
509 [ $# -eq 4 ] && offset
=$
((offset
+ 16 * OBJECT_PAGES
))
511 verbose
"reading \"${image_name}\" pages ${range}"
513 out_file
="${out_data}/pages_${range}"
515 quiet
dd bs
="${PAGE_SIZE}" skip="${offset}" count="${length}" \
516 if="${image_path}" of
="${out_file}"
519 function one_pass
() {
520 [ $# -eq 1 -o $# -eq 2 ] ||
exit 99
521 local image_name
="$1"
523 [ $# -eq 2 ] && extended
="true"
529 # +-----------+-----------+---
530 # |X:X:X...X:X| : : ... : | :
531 # +-----------+-----------+---
532 length
="${OBJECT_PAGES}"
533 do_read
"${image_name}" "${offset}" "${length}" ${extended}
534 offset=$((offset + length))
536 # ---+-----------+---
538 # ---+-----------+---
540 do_read "${image_name}" "${offset}" "${length}" ${extended}
541 offset
=$
((offset
+ length
))
543 # ---+-----------+---
545 # ---+-----------+---
547 do_read
"${image_name}" "${offset}" "${length}" ${extended}
548 offset=$((offset + length))
550 # ---+-----------+---
552 # ---+-----------+---
553 length=$((OBJECT_PAGES - 3))
554 do_read "${image_name}" "${offset}" "${length}" ${extended}
555 offset
=$
((offset
+ length
))
557 # ---+-----------+---
559 # ---+-----------+---
561 do_read
"${image_name}" "${offset}" "${length}" ${extended}
562 offset=$((offset + length))
564 # ---+-----------+---
566 # ---+-----------+---
567 length="${OBJECT_PAGES}"
568 do_read "${image_name}" "${offset}" "${length}" ${extended}
569 offset
=$
((offset
+ length
))
571 offset
=$
((offset
+ 1)) # skip 1
573 # ---+-----------+---
575 # ---+-----------+---
576 length
=$
((OBJECT_PAGES
- 1))
577 do_read
"${image_name}" "${offset}" "${length}" ${extended}
578 offset=$((offset + length))
580 # ---+-----------+-----------+---
581 # : |X:X:X...X:X|X: : ... : | :
582 # ---+-----------+-----------+---
583 length=$((OBJECT_PAGES + 1))
584 do_read "${image_name}" "${offset}" "${length}" ${extended}
585 offset
=$
((offset
+ length
))
587 # ---+-----------+-----------+---
588 # : | :X:X...X:X|X: : ... : | :
589 # ---+-----------+-----------+---
590 length
="${OBJECT_PAGES}"
591 do_read
"${image_name}" "${offset}" "${length}" ${extended}
592 offset=$((offset + length))
594 # ---+-----------+-----------+---
595 # : | :X:X...X:X|X:X: ... : | :
596 # ---+-----------+-----------+---
597 length=$((OBJECT_PAGES + 1))
598 do_read "${image_name}" "${offset}" "${length}" ${extended}
599 offset
=$
((offset
+ length
))
601 # ---+-----------+-----------+---
602 # : | : :X...X:X|X:X:X...X:X| :
603 # ---+-----------+-----------+---
604 length
=$
((2 * OBJECT_PAGES
+ 2))
605 do_read
"${image_name}" "${offset}" "${length}" ${extended}
606 offset=$((offset + length))
608 offset=$((offset + 1)) # skip 1
610 # ---+-----------+-----------+-----
611 # : | :X:X...X:X|X:X:X...X:X|X: :
612 # ---+-----------+-----------+-----
613 length=$((2 * OBJECT_PAGES))
614 do_read "${image_name}" "${offset}" "${length}" ${extended}
615 offset
=$
((offset
+ length
))
617 # --+-----------+-----------+--------
618 # : | :X:X...X:X|X:X:X...X:X|X:X: :
619 # --+-----------+-----------+--------
621 length
=$
((2 * OBJECT_PAGES
+ 1))
622 do_read
"${image_name}" "${offset}" "${length}" ${extended}
623 # offset=$((offset + length))
626 function run_using() {
627 [ $# -eq 1 ] || exit 99
628 local image_name="$1"
629 local out_data=$(out_data_dir "${image_name}")
631 verbose "===== running using
\"${image_name}\" ====="
632 mkdir -p "${out_data}"
633 one_pass "${image_name}"
634 one_pass "${image_name}" extended
638 [ $# -eq 1 ] || exit 99
639 local image_name="$1"
640 local out_data=$(out_data_dir "${image_name}")
641 local original=$(out_data_dir "${ORIGINAL}")
643 verbose "===== comparing
\"${image_name}\" ====="
644 for i in $(ls "${original}"); do
645 verbose compare "\"${image_name}\" \"${i}\""
646 cmp "${original}/${i}" "${out_data}/${i}"
648 [ "${image_name}" = "${ORIGINAL}" ] || rm -rf "${out_data}"
652 [ $# -eq 1 ] || exit 99
653 local image_name="$1"
655 run_using "${image_name}"
656 compare "${image_name}"
663 trap teardown EXIT HUP INT
666 run_using "${ORIGINAL}"
667 doit "${ORIGINAL}@
${SNAP1}"
668 if [ "${TEST_CLONES}" = true ]; then
670 doit "${CLONE1}@
${SNAP2}"
673 rm -rf $(out_data_dir "${ORIGINAL}")