]> git.proxmox.com Git - ceph.git/blame - ceph/qa/workunits/rbd/image_read.sh
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / qa / workunits / rbd / image_read.sh
CommitLineData
11fdf7f2 1#!/usr/bin/env bash
7c673cae
FG
2
3# Copyright (C) 2013 Inktank Storage, Inc.
4#
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.
8#
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.
12
13# Alex Elder <elder@inktank.com>
14# April 10, 2013
15
16################################################################
17
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.
20#
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.
26#
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.
31#
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.)
37
38################################################################
39
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.
11fdf7f2 44set -e
7c673cae
FG
45
46DEFAULT_VERBOSE=true
47DEFAULT_TEST_CLONES=true
48DEFAULT_LOCAL_FILES=false
49DEFAULT_FORMAT=2
50DEFAULT_DOUBLE_ORDER=true
51DEFAULT_HALF_ORDER=false
52DEFAULT_PAGE_SIZE=4096
53DEFAULT_OBJECT_ORDER=22
54MIN_OBJECT_ORDER=12 # technically 9, but the rbd CLI enforces 12
55MAX_OBJECT_ORDER=32
56
11fdf7f2
TL
57RBD_FORCE_ALLOW_V1=1
58
7c673cae
FG
59PROGNAME=$(basename $0)
60
61ORIGINAL=original-$$
62SNAP1=snap1-$$
63CLONE1=clone1-$$
64SNAP2=snap2-$$
65CLONE2=clone2-$$
66
67function err() {
68 if [ $# -gt 0 ]; then
69 echo "${PROGNAME}: $@" >&2
70 fi
71 exit 2
72}
73
74function usage() {
75 if [ $# -gt 0 ]; then
76 echo "" >&2
77 echo "${PROGNAME}: $@" >&2
78 fi
79 echo "" >&2
80 echo "Usage: ${PROGNAME} [<options>]" >&2
81 echo "" >&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
87 echo " -1" >&2
88 echo " test using format 1 rbd images (default)" >&2
89 echo " -2" >&2
90 echo " test using format 2 rbd images" >&2
91 echo " -c" >&2
92 echo " also test rbd clone images (implies format 2)" >&2
93 echo " -d" >&2
94 echo " clone object order double its parent's (format 2)" >&2
95 echo " -h" >&2
96 echo " clone object order half of its parent's (format 2)" >&2
97 echo " -l" >&2
98 echo " use local files rather than rbd images" >&2
99 echo " -v" >&2
100 echo " disable reporting of what's going on" >&2
101 echo "" >&2
102 exit 1
103}
104
105function verbose() {
106 [ "${VERBOSE}" = true ] && echo "$@"
107 true # Don't let the verbose test spoil our return value
108}
109
110function quiet() {
111 "$@" 2> /dev/null
112}
113
114function boolean_toggle() {
115 [ $# -eq 1 ] || exit 99
116 test "$1" = "true" && echo false || echo true
117}
118
119function parseargs() {
120 local opts="o:p:12clv"
121 local lopts="order:,page_size:,local,clone,verbose"
122 local parsed
123 local clone_order_msg
124
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}}"
134
135 parsed=$(getopt -o "${opts}" -l "${lopts}" -n "${PROGNAME}" -- "$@") ||
136 usage
137 eval set -- "${parsed}"
138 while true; do
139 case "$1" in
140 -v|--verbose)
141 VERBOSE=$(boolean_toggle "${VERBOSE}");;
142 -c|--clone)
143 TEST_CLONES=$(boolean_toggle "${TEST_CLONES}");;
144 -d|--double)
145 DOUBLE_ORDER=$(boolean_toggle "${DOUBLE_ORDER}");;
146 -h|--half)
147 HALF_ORDER=$(boolean_toggle "${HALF_ORDER}");;
148 -l|--local)
149 LOCAL_FILES=$(boolean_toggle "${LOCAL_FILES}");;
150 -1|-2)
151 FORMAT="${1:1}";;
152 -p|--page_size)
153 PAGE_SIZE="$2"; shift;;
154 -o|--order)
155 OBJECT_ORDER="$2"; shift;;
156 --)
157 shift; break;;
158 *)
159 err "getopt internal error"
160 esac
161 shift
162 done
163 [ $# -gt 0 ] && usage "excess arguments ($*)"
164
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
169 # ignore them both.
170 if [ "${DOUBLE_ORDER}" = true ]; then
171 if [ "${HALF_ORDER}" = true ]; then
172 DOUBLE_ORDER=false
173 HALF_ORDER=false
174 else
175 ((MAX_OBJECT_ORDER -= 2))
176 fi
177 elif [ "${HALF_ORDER}" = true ]; then
178 ((MIN_OBJECT_ORDER += 2))
179 fi
180 fi
181
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}"
188
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"
198 else
199 CLONE1_ORDER="${OBJECT_ORDER}"
200 CLONE2_ORDER="${OBJECT_ORDER}"
201 clone_order_msg="the same as"
202 fi
203 fi
204
205 [ "${TEST_CLONES}" != true ] || FORMAT=2
206
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))
212
213 [ "${OBJECT_PAGES}" -lt 4 ] &&
214 usage "object size (${OBJECT_SIZE}) must be" \
215 "at least 4 * page size (${PAGE_SIZE})"
216
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"
229 fi
230
231 true # Don't let the clones test spoil our return value
232}
233
234function image_dev_path() {
235 [ $# -eq 1 ] || exit 99
236 local image_name="$1"
237
238 if [ "${LOCAL_FILES}" = true ]; then
239 echo "${TEMP}/${image_name}"
240 return
241 fi
242
243 echo "/dev/rbd/rbd/${image_name}"
244}
245
246function out_data_dir() {
247 [ $# -lt 2 ] || exit 99
248 local out_data="${TEMP}/data"
249 local image_name
250
251 if [ $# -eq 1 ]; then
252 image_name="$1"
253 echo "${out_data}/${image_name}"
254 else
255 echo "${out_data}"
256 fi
257}
258
259function setup() {
260 verbose "===== setting up ====="
261 TEMP=$(mktemp -d /tmp/rbd_image_read.XXXXX)
262 mkdir -p $(out_data_dir)
263
264 # create and fill the original image with some data
265 create_image "${ORIGINAL}"
266 map_image "${ORIGINAL}"
267 fill_original
268
269 # create a snapshot of the original
270 create_image_snap "${ORIGINAL}" "${SNAP1}"
271 map_image_snap "${ORIGINAL}" "${SNAP1}"
272
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}"
278
279 # create a snapshot of that clone
280 create_image_snap "${CLONE1}" "${SNAP2}"
281 map_image_snap "${CLONE1}" "${SNAP2}"
282
283 # create a clone of that clone's snapshot
284 create_snap_clone "${CLONE1}" "${SNAP2}" \
285 "${CLONE2}" "${CLONE2_ORDER}"
286 map_image "${CLONE2}"
287 fi
288}
289
290function teardown() {
291 verbose "===== cleaning up ====="
292 if [ "${TEST_CLONES}" = true ]; then
293 unmap_image "${CLONE2}" || true
294 destroy_snap_clone "${CLONE1}" "${SNAP2}" "${CLONE2}" || true
295
296 unmap_image_snap "${CLONE1}" "${SNAP2}" || true
297 destroy_image_snap "${CLONE1}" "${SNAP2}" || true
298
299 unmap_image "${CLONE1}" || true
300 destroy_snap_clone "${ORIGINAL}" "${SNAP1}" "${CLONE1}" || true
301 fi
302 unmap_image_snap "${ORIGINAL}" "${SNAP1}" || true
303 destroy_image_snap "${ORIGINAL}" "${SNAP1}" || true
304 unmap_image "${ORIGINAL}" || true
305 destroy_image "${ORIGINAL}" || true
306
307 rm -rf $(out_data_dir)
308 rmdir "${TEMP}"
309}
310
311function create_image() {
312 [ $# -eq 1 ] || exit 99
313 local image_name="$1"
314 local image_path
315 local bytes
316
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}" \
322 of="${image_path}"
323 return
324 fi
325
326 rbd create "${image_name}" --image-format "${FORMAT}" \
327 --size "${IMAGE_SIZE}" --order "${OBJECT_ORDER}" \
328 --image-shared
329}
330
331function destroy_image() {
332 [ $# -eq 1 ] || exit 99
333 local image_name="$1"
334 local image_path
335
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}"
340 return
341 fi
342
343 rbd rm "${image_name}"
344}
345
346function map_image() {
347 [ $# -eq 1 ] || exit 99
348 local image_name="$1" # can be image@snap too
349
350 if [ "${LOCAL_FILES}" = true ]; then
351 return
352 fi
353
354 sudo rbd map "${image_name}"
355}
356
357function unmap_image() {
358 [ $# -eq 1 ] || exit 99
359 local image_name="$1" # can be image@snap too
360 local image_path
361
362 if [ "${LOCAL_FILES}" = true ]; then
363 return
364 fi
365 image_path=$(image_dev_path "${image_name}")
366
367 if [ -e "${image_path}" ]; then
368 sudo rbd unmap "${image_path}"
369 fi
370}
371
372function map_image_snap() {
373 [ $# -eq 2 ] || exit 99
374 local image_name="$1"
375 local snap_name="$2"
376 local image_snap
377
378 if [ "${LOCAL_FILES}" = true ]; then
379 return
380 fi
381
382 image_snap="${image_name}@${snap_name}"
383 map_image "${image_snap}"
384}
385
386function unmap_image_snap() {
387 [ $# -eq 2 ] || exit 99
388 local image_name="$1"
389 local snap_name="$2"
390 local image_snap
391
392 if [ "${LOCAL_FILES}" = true ]; then
393 return
394 fi
395
396 image_snap="${image_name}@${snap_name}"
397 unmap_image "${image_snap}"
398}
399
400function create_image_snap() {
401 [ $# -eq 2 ] || exit 99
402 local image_name="$1"
403 local snap_name="$2"
404 local image_snap="${image_name}@${snap_name}"
405 local image_path
406 local snap_path
407
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}")
413
414 cp "${image_path}" "${snap_path}"
415 return
416 fi
417
418 rbd snap create "${image_snap}"
419}
420
421function destroy_image_snap() {
422 [ $# -eq 2 ] || exit 99
423 local image_name="$1"
424 local snap_name="$2"
425 local image_snap="${image_name}@${snap_name}"
426 local snap_path
427
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}"
433 return
434 fi
435
436 rbd snap rm "${image_snap}"
437}
438
439function create_snap_clone() {
440 [ $# -eq 4 ] || exit 99
441 local image_name="$1"
442 local snap_name="$2"
443 local clone_name="$3"
444 local clone_order="$4"
445 local image_snap="${image_name}@${snap_name}"
446 local snap_path
447 local clone_path
448
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}")
454
455 cp "${snap_path}" "${clone_path}"
456 return
457 fi
458
459 rbd snap protect "${image_snap}"
460 rbd clone --order "${clone_order}" --image-shared \
461 "${image_snap}" "${clone_name}"
462}
463
464function destroy_snap_clone() {
465 [ $# -eq 3 ] || exit 99
466 local image_name="$1"
467 local snap_name="$2"
468 local clone_name="$3"
469 local image_snap="${image_name}@${snap_name}"
470 local clone_path
471
472 verbose "destroying clone image \"${clone_name}\""
473 if [ "${LOCAL_FILES}" = true ]; then
474 clone_path=$(image_dev_path "${clone_name}")
475
476 rm -rf "${clone_path}"
477 return
478 fi
479
480 rbd rm "${clone_name}"
481 rbd snap unprotect "${image_snap}"
482}
483
484# function that produces "random" data with which to fill the image
485function source_data() {
486 while quiet dd if=/bin/bash skip=$(($$ % 199)) bs="${PAGE_SIZE}"; do
487 : # Just do the dd
488 done
489}
490
491function fill_original() {
492 local image_path=$(image_dev_path "${ORIGINAL}")
493
494 verbose "filling original image"
495 # Fill 16 objects worth of "random" data
496 source_data |
497 quiet dd bs="${PAGE_SIZE}" count=$((16 * OBJECT_PAGES)) \
498 of="${image_path}"
499}
500
501function do_read() {
502 [ $# -eq 3 -o $# -eq 4 ] || exit 99
503 local image_name="$1"
504 local offset="$2"
505 local length="$3"
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}")
510 local out_file
511
512 [ $# -eq 4 ] && offset=$((offset + 16 * OBJECT_PAGES))
513
514 verbose "reading \"${image_name}\" pages ${range}"
515
516 out_file="${out_data}/pages_${range}"
517
518 quiet dd bs="${PAGE_SIZE}" skip="${offset}" count="${length}" \
519 if="${image_path}" of="${out_file}"
520}
521
522function one_pass() {
523 [ $# -eq 1 -o $# -eq 2 ] || exit 99
524 local image_name="$1"
525 local extended
526 [ $# -eq 2 ] && extended="true"
527 local offset
528 local length
529
530 offset=0
531
532 # +-----------+-----------+---
533 # |X:X:X...X:X| : : ... : | :
534 # +-----------+-----------+---
535 length="${OBJECT_PAGES}"
536 do_read "${image_name}" "${offset}" "${length}" ${extended}
537 offset=$((offset + length))
538
539 # ---+-----------+---
540 # : |X: : ... : | :
541 # ---+-----------+---
542 length=1
543 do_read "${image_name}" "${offset}" "${length}" ${extended}
544 offset=$((offset + length))
545
546 # ---+-----------+---
547 # : | :X: ... : | :
548 # ---+-----------+---
549 length=1
550 do_read "${image_name}" "${offset}" "${length}" ${extended}
551 offset=$((offset + length))
552
553 # ---+-----------+---
554 # : | : :X...X: | :
555 # ---+-----------+---
556 length=$((OBJECT_PAGES - 3))
557 do_read "${image_name}" "${offset}" "${length}" ${extended}
558 offset=$((offset + length))
559
560 # ---+-----------+---
561 # : | : : ... :X| :
562 # ---+-----------+---
563 length=1
564 do_read "${image_name}" "${offset}" "${length}" ${extended}
565 offset=$((offset + length))
566
567 # ---+-----------+---
568 # : |X:X:X...X:X| :
569 # ---+-----------+---
570 length="${OBJECT_PAGES}"
571 do_read "${image_name}" "${offset}" "${length}" ${extended}
572 offset=$((offset + length))
573
574 offset=$((offset + 1)) # skip 1
575
576 # ---+-----------+---
577 # : | :X:X...X:X| :
578 # ---+-----------+---
579 length=$((OBJECT_PAGES - 1))
580 do_read "${image_name}" "${offset}" "${length}" ${extended}
581 offset=$((offset + length))
582
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))
589
590 # ---+-----------+-----------+---
591 # : | :X:X...X:X|X: : ... : | :
592 # ---+-----------+-----------+---
593 length="${OBJECT_PAGES}"
594 do_read "${image_name}" "${offset}" "${length}" ${extended}
595 offset=$((offset + length))
596
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))
603
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))
610
611 offset=$((offset + 1)) # skip 1
612
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))
619
620 # --+-----------+-----------+--------
621 # : | :X:X...X:X|X:X:X...X:X|X:X: :
622 # --+-----------+-----------+--------
623 length=2049
624 length=$((2 * OBJECT_PAGES + 1))
625 do_read "${image_name}" "${offset}" "${length}" ${extended}
626 # offset=$((offset + length))
627}
628
629function run_using() {
630 [ $# -eq 1 ] || exit 99
631 local image_name="$1"
632 local out_data=$(out_data_dir "${image_name}")
633
634 verbose "===== running using \"${image_name}\" ====="
635 mkdir -p "${out_data}"
636 one_pass "${image_name}"
637 one_pass "${image_name}" extended
638}
639
640function compare() {
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}")
645
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}"
650 done
651 [ "${image_name}" = "${ORIGINAL}" ] || rm -rf "${out_data}"
652}
653
654function doit() {
655 [ $# -eq 1 ] || exit 99
656 local image_name="$1"
657
658 run_using "${image_name}"
659 compare "${image_name}"
660}
661
662########## Start
663
664parseargs "$@"
665
666trap teardown EXIT HUP INT
667setup
668
669run_using "${ORIGINAL}"
670doit "${ORIGINAL}@${SNAP1}"
671if [ "${TEST_CLONES}" = true ]; then
672 doit "${CLONE1}"
673 doit "${CLONE1}@${SNAP2}"
674 doit "${CLONE2}"
675fi
676rm -rf $(out_data_dir "${ORIGINAL}")
677
678echo "Success!"
679
680exit 0