]> git.proxmox.com Git - ceph.git/blame - ceph/qa/standalone/ceph-helpers.sh
bump version to 12.2.12-pve1
[ceph.git] / ceph / qa / standalone / ceph-helpers.sh
CommitLineData
7c673cae
FG
1#!/bin/bash
2#
3# Copyright (C) 2013,2014 Cloudwatt <libre.licensing@cloudwatt.com>
4# Copyright (C) 2014,2015 Red Hat <contact@redhat.com>
5# Copyright (C) 2014 Federico Gimenez <fgimenez@coit.es>
6#
7# Author: Loic Dachary <loic@dachary.org>
8# Author: Federico Gimenez <fgimenez@coit.es>
9#
10# This program is free software; you can redistribute it and/or modify
11# it under the terms of the GNU Library Public License as published by
12# the Free Software Foundation; either version 2, or (at your option)
13# any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU Library Public License for more details.
19#
20TIMEOUT=300
21PG_NUM=4
a8e16298 22TMPDIR=${TMPDIR:-${CEPH_BUILD_DIR}}
f64942e4
AA
23CEPH_BUILD_VIRTUALENV=${TMPDIR}
24TESTDIR=${TESTDIR:-${TMPDIR}}
7c673cae
FG
25
26if type xmlstarlet > /dev/null 2>&1; then
27 XMLSTARLET=xmlstarlet
28elif type xml > /dev/null 2>&1; then
29 XMLSTARLET=xml
30else
31 echo "Missing xmlstarlet binary!"
32 exit 1
33fi
31f18b77 34
7c673cae
FG
35if [ `uname` = FreeBSD ]; then
36 SED=gsed
f64942e4 37 AWK=gawk
31f18b77 38 DIFFCOLOPTS=""
b5b8bbf5 39 KERNCORE="kern.corefile"
7c673cae
FG
40else
41 SED=sed
f64942e4 42 AWK=awk
31f18b77 43 termwidth=$(stty -a | head -1 | sed -e 's/.*columns \([0-9]*\).*/\1/')
c07f9fc5
FG
44 if [ -n "$termwidth" -a "$termwidth" != "0" ]; then
45 termwidth="-W ${termwidth}"
31f18b77
FG
46 fi
47 DIFFCOLOPTS="-y $termwidth"
b5b8bbf5 48 KERNCORE="kernel.core_pattern"
31f18b77 49fi
7c673cae 50
c07f9fc5
FG
51EXTRA_OPTS=""
52if [ -n "$CEPH_LIB" ]; then
53 EXTRA_OPTS+=" --erasure-code-dir $CEPH_LIB"
54 EXTRA_OPTS+=" --plugin-dir $CEPH_LIB"
55 EXTRA_OPTS+=" --osd-class-dir $CEPH_LIB"
56fi
57
7c673cae
FG
58#! @file ceph-helpers.sh
59# @brief Toolbox to manage Ceph cluster dedicated to testing
60#
61# Example use case:
62#
63# ~~~~~~~~~~~~~~~~{.sh}
64# source ceph-helpers.sh
65#
66# function mytest() {
67# # cleanup leftovers and reset mydir
68# setup mydir
69# # create a cluster with one monitor and three osds
70# run_mon mydir a
71# run_osd mydir 0
72# run_osd mydir 2
73# run_osd mydir 3
74# # put and get an object
75# rados --pool rbd put GROUP /etc/group
76# rados --pool rbd get GROUP /tmp/GROUP
77# # stop the cluster and cleanup the directory
78# teardown mydir
79# }
80# ~~~~~~~~~~~~~~~~
81#
82# The focus is on simplicity and efficiency, in the context of
83# functional tests. The output is intentionally very verbose
84# and functions return as soon as an error is found. The caller
85# is also expected to abort on the first error so that debugging
86# can be done by looking at the end of the output.
87#
88# Each function is documented, implemented and tested independently.
89# When modifying a helper, the test and the documentation are
90# expected to be updated and it is easier of they are collocated. A
91# test for a given function can be run with
92#
93# ~~~~~~~~~~~~~~~~{.sh}
94# ceph-helpers.sh TESTS test_get_osds
95# ~~~~~~~~~~~~~~~~
96#
97# and all the tests (i.e. all functions matching test_*) are run
98# with:
99#
100# ~~~~~~~~~~~~~~~~{.sh}
101# ceph-helpers.sh TESTS
102# ~~~~~~~~~~~~~~~~
103#
104# A test function takes a single argument : the directory dedicated
105# to the tests. It is expected to not create any file outside of this
106# directory and remove it entirely when it completes successfully.
107#
108
109
c07f9fc5
FG
110function get_asok_dir() {
111 if [ -n "$CEPH_ASOK_DIR" ]; then
112 echo "$CEPH_ASOK_DIR"
113 else
114 echo ${TMPDIR:-/tmp}/ceph-asok.$$
115 fi
116}
117
118function get_asok_path() {
119 local name=$1
120 if [ -n "$name" ]; then
121 echo $(get_asok_dir)/ceph-$name.asok
122 else
123 echo $(get_asok_dir)/\$cluster-\$name.asok
124 fi
125}
7c673cae
FG
126##
127# Cleanup any leftovers found in **dir** via **teardown**
128# and reset **dir** as an empty environment.
129#
130# @param dir path name of the environment
131# @return 0 on success, 1 on error
132#
133function setup() {
134 local dir=$1
135 teardown $dir || return 1
136 mkdir -p $dir
c07f9fc5 137 mkdir -p $(get_asok_dir)
7c673cae
FG
138}
139
140function test_setup() {
141 local dir=$dir
142 setup $dir || return 1
143 test -d $dir || return 1
144 setup $dir || return 1
145 test -d $dir || return 1
146 teardown $dir
147}
148
149#######################################################################
150
151##
152# Kill all daemons for which a .pid file exists in **dir** and remove
153# **dir**. If the file system in which **dir** is btrfs, delete all
154# subvolumes that relate to it.
155#
156# @param dir path name of the environment
1adf2230 157# @param dumplogs pass "1" to dump logs otherwise it will only if cores found
7c673cae
FG
158# @return 0 on success, 1 on error
159#
160function teardown() {
161 local dir=$1
b5b8bbf5 162 local dumplogs=$2
7c673cae
FG
163 kill_daemons $dir KILL
164 if [ `uname` != FreeBSD ] \
165 && [ $(stat -f -c '%T' .) == "btrfs" ]; then
166 __teardown_btrfs $dir
167 fi
b5b8bbf5
FG
168 local cores="no"
169 local pattern="$(sysctl -n $KERNCORE)"
170 # See if we have apport core handling
171 if [ "${pattern:0:1}" = "|" ]; then
172 # TODO: Where can we get the dumps?
173 # Not sure where the dumps really are so this will look in the CWD
174 pattern=""
175 fi
176 # Local we start with core and teuthology ends with core
1adf2230 177 if ls $(dirname "$pattern") | grep -q '^core\|core$' ; then
b5b8bbf5
FG
178 cores="yes"
179 if [ -n "$LOCALRUN" ]; then
180 mkdir /tmp/cores.$$ 2> /dev/null || true
181 for i in $(ls $(dirname $(sysctl -n $KERNCORE)) | grep '^core\|core$'); do
182 mv $i /tmp/cores.$$
183 done
184 fi
185 fi
186 if [ "$cores" = "yes" -o "$dumplogs" = "1" ]; then
1adf2230
AA
187 if [ -n "$LOCALRUN" ]; then
188 display_logs $dir
189 else
190 # Move logs to where Teuthology will archive it
191 mkdir -p $TESTDIR/archive/log
192 mv $dir/*.log $TESTDIR/archive/log
193 fi
b5b8bbf5 194 fi
7c673cae 195 rm -fr $dir
c07f9fc5 196 rm -rf $(get_asok_dir)
b5b8bbf5
FG
197 if [ "$cores" = "yes" ]; then
198 echo "ERROR: Failure due to cores found"
199 if [ -n "$LOCALRUN" ]; then
200 echo "Find saved core files in /tmp/cores.$$"
201 fi
202 return 1
203 fi
204 return 0
7c673cae
FG
205}
206
207function __teardown_btrfs() {
208 local btrfs_base_dir=$1
f64942e4
AA
209 local btrfs_root=$(df -P . | tail -1 | $AWK '{print $NF}')
210 local btrfs_dirs=$(cd $btrfs_base_dir; sudo btrfs subvolume list -t . | $AWK '/^[0-9]/ {print $4}' | grep "$btrfs_base_dir/$btrfs_dir")
7c673cae
FG
211 for subvolume in $btrfs_dirs; do
212 sudo btrfs subvolume delete $btrfs_root/$subvolume
213 done
214}
215
216function test_teardown() {
217 local dir=$dir
218 setup $dir || return 1
219 teardown $dir || return 1
220 ! test -d $dir || return 1
221}
222
223#######################################################################
224
225##
226# Sends a signal to a single daemon.
227# This is a helper function for kill_daemons
228#
229# After the daemon is sent **signal**, its actual termination
230# will be verified by sending it signal 0. If the daemon is
231# still alive, kill_daemon will pause for a few seconds and
232# try again. This will repeat for a fixed number of times
233# before kill_daemon returns on failure. The list of
234# sleep intervals can be specified as **delays** and defaults
235# to:
236#
237# 0.1 0.2 1 1 1 2 3 5 5 5 10 10 20 60 60 60 120
238#
239# This sequence is designed to run first a very short sleep time (0.1)
240# if the machine is fast enough and the daemon terminates in a fraction of a
241# second. The increasing sleep numbers should give plenty of time for
242# the daemon to die even on the slowest running machine. If a daemon
243# takes more than a few minutes to stop (the sum of all sleep times),
244# there probably is no point in waiting more and a number of things
245# are likely to go wrong anyway: better give up and return on error.
246#
247# @param pid the process id to send a signal
248# @param send_signal the signal to send
249# @param delays sequence of sleep times before failure
250#
251function kill_daemon() {
7c673cae
FG
252 local pid=$(cat $1)
253 local send_signal=$2
254 local delays=${3:-0.1 0.2 1 1 1 2 3 5 5 5 10 10 20 60 60 60 120}
255 local exit_code=1
256 for try in $delays ; do
257 if kill -$send_signal $pid 2> /dev/null ; then
258 exit_code=1
259 else
260 exit_code=0
261 break
262 fi
263 send_signal=0
264 sleep $try
265 done;
266 return $exit_code
267}
268
269function test_kill_daemon() {
270 local dir=$1
271 setup $dir || return 1
272 run_mon $dir a --osd_pool_default_size=1 || return 1
273 run_mgr $dir x || return 1
274 run_osd $dir 0 || return 1
275
276 name_prefix=osd
277 for pidfile in $(find $dir 2>/dev/null | grep $name_prefix'[^/]*\.pid') ; do
278 #
279 # sending signal 0 won't kill the daemon
280 # waiting just for one second instead of the default schedule
281 # allows us to quickly verify what happens when kill fails
282 # to stop the daemon (i.e. it must return false)
283 #
284 ! kill_daemon $pidfile 0 1 || return 1
285 #
286 # killing just the osd and verify the mon still is responsive
287 #
288 kill_daemon $pidfile TERM || return 1
289 done
290
291 ceph osd dump | grep "osd.0 down" || return 1
292
293 name_prefix=mgr
294 for pidfile in $(find $dir 2>/dev/null | grep $name_prefix'[^/]*\.pid') ; do
295 #
296 # kill the mgr
297 #
298 kill_daemon $pidfile TERM || return 1
299 done
300
301 name_prefix=mon
302 for pidfile in $(find $dir 2>/dev/null | grep $name_prefix'[^/]*\.pid') ; do
303 #
304 # kill the mon and verify it cannot be reached
305 #
306 kill_daemon $pidfile TERM || return 1
224ce89b 307 ! timeout 5 ceph status || return 1
7c673cae
FG
308 done
309
310 teardown $dir || return 1
311}
312
313##
314# Kill all daemons for which a .pid file exists in **dir**. Each
315# daemon is sent a **signal** and kill_daemons waits for it to exit
316# during a few minutes. By default all daemons are killed. If a
317# **name_prefix** is provided, only the daemons for which a pid
318# file is found matching the prefix are killed. See run_osd and
319# run_mon for more information about the name conventions for
320# the pid files.
321#
322# Send TERM to all daemons : kill_daemons $dir
323# Send KILL to all daemons : kill_daemons $dir KILL
324# Send KILL to all osds : kill_daemons $dir KILL osd
325# Send KILL to osd 1 : kill_daemons $dir KILL osd.1
326#
327# If a daemon is sent the TERM signal and does not terminate
328# within a few minutes, it will still be running even after
c07f9fc5 329# kill_daemons returns.
7c673cae
FG
330#
331# If all daemons are kill successfully the function returns 0
c07f9fc5 332# if at least one daemon remains, this is treated as an
7c673cae
FG
333# error and the function return 1.
334#
335# @param dir path name of the environment
336# @param signal name of the first signal (defaults to TERM)
337# @param name_prefix only kill match daemons (defaults to all)
338# @param delays sequence of sleep times before failure
339# @return 0 on success, 1 on error
340#
341function kill_daemons() {
342 local trace=$(shopt -q -o xtrace && echo true || echo false)
343 $trace && shopt -u -o xtrace
344 local dir=$1
345 local signal=${2:-TERM}
346 local name_prefix=$3 # optional, osd, mon, osd.1
347 local delays=$4 #optional timing
348 local status=0
349 local pids=""
350
351 for pidfile in $(find $dir 2>/dev/null | grep $name_prefix'[^/]*\.pid') ; do
352 run_in_background pids kill_daemon $pidfile $signal $delays
353 done
354
355 wait_background pids
356 status=$?
357
358 $trace && shopt -s -o xtrace
359 return $status
360}
361
362function test_kill_daemons() {
363 local dir=$1
364 setup $dir || return 1
365 run_mon $dir a --osd_pool_default_size=1 || return 1
366 run_mgr $dir x || return 1
367 run_osd $dir 0 || return 1
368 #
369 # sending signal 0 won't kill the daemon
370 # waiting just for one second instead of the default schedule
c07f9fc5 371 # allows us to quickly verify what happens when kill fails
7c673cae
FG
372 # to stop the daemon (i.e. it must return false)
373 #
374 ! kill_daemons $dir 0 osd 1 || return 1
375 #
376 # killing just the osd and verify the mon still is responsive
377 #
378 kill_daemons $dir TERM osd || return 1
379 ceph osd dump | grep "osd.0 down" || return 1
380 #
381 # kill the mgr
382 #
383 kill_daemons $dir TERM mgr || return 1
384 #
385 # kill the mon and verify it cannot be reached
386 #
387 kill_daemons $dir TERM || return 1
224ce89b 388 ! timeout 5 ceph status || return 1
7c673cae
FG
389 teardown $dir || return 1
390}
391
a8e16298
TL
392#
393# return a random TCP port which is not used yet
394#
395# please note, there could be racing if we use this function for
396# a free port, and then try to bind on this port.
397#
398function get_unused_port() {
399 local ip=127.0.0.1
400 python3 -c "import socket; s=socket.socket(); s.bind(('$ip', 0)); print(s.getsockname()[1]); s.close()"
401}
402
7c673cae
FG
403#######################################################################
404
405##
406# Run a monitor by the name mon.**id** with data in **dir**/**id**.
407# The logs can be found in **dir**/mon.**id**.log and the pid file
408# is **dir**/mon.**id**.pid and the admin socket is
409# **dir**/**id**/ceph-mon.**id**.asok.
410#
411# The remaining arguments are passed verbatim to ceph-mon --mkfs
412# and the ceph-mon daemon.
413#
414# Two mandatory arguments must be provided: --fsid and --mon-host
415# Instead of adding them to every call to run_mon, they can be
416# set in the CEPH_ARGS environment variable to be read implicitly
417# by every ceph command.
418#
419# The CEPH_CONF variable is expected to be set to /dev/null to
420# only rely on arguments for configuration.
421#
422# Examples:
423#
424# CEPH_ARGS="--fsid=$(uuidgen) "
425# CEPH_ARGS+="--mon-host=127.0.0.1:7018 "
426# run_mon $dir a # spawn a mon and bind port 7018
427# run_mon $dir a --debug-filestore=20 # spawn with filestore debugging
428#
429# If mon_initial_members is not set, the default rbd pool is deleted
430# and replaced with a replicated pool with less placement groups to
431# speed up initialization. If mon_initial_members is set, no attempt
432# is made to recreate the rbd pool because it would hang forever,
433# waiting for other mons to join.
434#
435# A **dir**/ceph.conf file is created but not meant to be used by any
436# function. It is convenient for debugging a failure with:
437#
438# ceph --conf **dir**/ceph.conf -s
439#
440# @param dir path name of the environment
441# @param id mon identifier
442# @param ... can be any option valid for ceph-mon
443# @return 0 on success, 1 on error
444#
c07f9fc5 445function run_mon() {
7c673cae
FG
446 local dir=$1
447 shift
448 local id=$1
449 shift
450 local data=$dir/$id
451
452 ceph-mon \
453 --id $id \
454 --mkfs \
455 --mon-data=$data \
456 --run-dir=$dir \
457 "$@" || return 1
458
459 ceph-mon \
460 --id $id \
461 --mon-osd-full-ratio=.99 \
462 --mon-data-avail-crit=1 \
b5b8bbf5 463 --mon-data-avail-warn=5 \
7c673cae
FG
464 --paxos-propose-interval=0.1 \
465 --osd-crush-chooseleaf-type=0 \
c07f9fc5 466 $EXTRA_OPTS \
7c673cae
FG
467 --debug-mon 20 \
468 --debug-ms 20 \
469 --debug-paxos 20 \
470 --chdir= \
471 --mon-data=$data \
472 --log-file=$dir/\$name.log \
c07f9fc5 473 --admin-socket=$(get_asok_path) \
7c673cae
FG
474 --mon-cluster-log-file=$dir/log \
475 --run-dir=$dir \
476 --pid-file=$dir/\$name.pid \
477 --mon-allow-pool-delete \
c07f9fc5 478 --mon-osd-backfillfull-ratio .99 \
7c673cae
FG
479 "$@" || return 1
480
481 cat > $dir/ceph.conf <<EOF
482[global]
483fsid = $(get_config mon $id fsid)
484mon host = $(get_config mon $id mon_host)
485EOF
224ce89b
WB
486}
487
7c673cae
FG
488function test_run_mon() {
489 local dir=$1
490
491 setup $dir || return 1
492
493 run_mon $dir a --mon-initial-members=a || return 1
c07f9fc5 494 create_rbd_pool || return 1
7c673cae 495 # rbd has not been deleted / created, hence it has pool id 0
224ce89b 496 ceph osd dump | grep "pool 1 'rbd'" || return 1
7c673cae
FG
497 kill_daemons $dir || return 1
498
499 run_mon $dir a || return 1
c07f9fc5 500 create_rbd_pool || return 1
7c673cae 501 # rbd has been deleted / created, hence it does not have pool id 0
224ce89b 502 ! ceph osd dump | grep "pool 1 'rbd'" || return 1
c07f9fc5 503 local size=$(CEPH_ARGS='' ceph --format=json daemon $(get_asok_path mon.a) \
7c673cae
FG
504 config get osd_pool_default_size)
505 test "$size" = '{"osd_pool_default_size":"3"}' || return 1
506
507 ! CEPH_ARGS='' ceph status || return 1
508 CEPH_ARGS='' ceph --conf $dir/ceph.conf status || return 1
509
510 kill_daemons $dir || return 1
511
512 run_mon $dir a --osd_pool_default_size=1 || return 1
c07f9fc5 513 local size=$(CEPH_ARGS='' ceph --format=json daemon $(get_asok_path mon.a) \
7c673cae
FG
514 config get osd_pool_default_size)
515 test "$size" = '{"osd_pool_default_size":"1"}' || return 1
516 kill_daemons $dir || return 1
517
518 CEPH_ARGS="$CEPH_ARGS --osd_pool_default_size=2" \
519 run_mon $dir a || return 1
c07f9fc5 520 local size=$(CEPH_ARGS='' ceph --format=json daemon $(get_asok_path mon.a) \
7c673cae
FG
521 config get osd_pool_default_size)
522 test "$size" = '{"osd_pool_default_size":"2"}' || return 1
523 kill_daemons $dir || return 1
524
525 teardown $dir || return 1
526}
527
c07f9fc5
FG
528function create_rbd_pool() {
529 ceph osd pool delete rbd rbd --yes-i-really-really-mean-it || return 1
b5b8bbf5 530 create_pool rbd $PG_NUM || return 1
c07f9fc5
FG
531 rbd pool init rbd
532}
533
b5b8bbf5
FG
534function create_pool() {
535 ceph osd pool create "$@"
536 sleep 1
537}
538
28e407b8
AA
539function delete_pool() {
540 local poolname=$1
541 ceph osd pool delete $poolname $poolname --yes-i-really-really-mean-it
542}
543
7c673cae
FG
544#######################################################################
545
546function run_mgr() {
547 local dir=$1
548 shift
549 local id=$1
550 shift
551 local data=$dir/$id
552
553 ceph-mgr \
554 --id $id \
c07f9fc5 555 $EXTRA_OPTS \
7c673cae
FG
556 --debug-mgr 20 \
557 --debug-objecter 20 \
558 --debug-ms 20 \
559 --debug-paxos 20 \
560 --chdir= \
561 --mgr-data=$data \
562 --log-file=$dir/\$name.log \
c07f9fc5 563 --admin-socket=$(get_asok_path) \
7c673cae
FG
564 --run-dir=$dir \
565 --pid-file=$dir/\$name.pid \
566 "$@" || return 1
567}
568
569#######################################################################
570
571##
572# Create (prepare) and run (activate) an osd by the name osd.**id**
573# with data in **dir**/**id**. The logs can be found in
574# **dir**/osd.**id**.log, the pid file is **dir**/osd.**id**.pid and
575# the admin socket is **dir**/**id**/ceph-osd.**id**.asok.
576#
577# The remaining arguments are passed verbatim to ceph-osd.
578#
579# Two mandatory arguments must be provided: --fsid and --mon-host
580# Instead of adding them to every call to run_osd, they can be
581# set in the CEPH_ARGS environment variable to be read implicitly
582# by every ceph command.
583#
584# The CEPH_CONF variable is expected to be set to /dev/null to
585# only rely on arguments for configuration.
586#
587# The run_osd function creates the OSD data directory with ceph-disk
588# prepare on the **dir**/**id** directory and relies on the
589# activate_osd function to run the daemon.
590#
591# Examples:
592#
593# CEPH_ARGS="--fsid=$(uuidgen) "
594# CEPH_ARGS+="--mon-host=127.0.0.1:7018 "
595# run_osd $dir 0 # prepare and activate an osd using the monitor listening on 7018
596#
597# @param dir path name of the environment
598# @param id osd identifier
599# @param ... can be any option valid for ceph-osd
600# @return 0 on success, 1 on error
601#
602function run_osd() {
603 local dir=$1
604 shift
605 local id=$1
606 shift
607 local osd_data=$dir/$id
608
609 local ceph_disk_args
610 ceph_disk_args+=" --statedir=$dir"
611 ceph_disk_args+=" --sysconfdir=$dir"
612 ceph_disk_args+=" --prepend-to-path="
613
614 mkdir -p $osd_data
615 ceph-disk $ceph_disk_args \
31f18b77 616 prepare --filestore $osd_data || return 1
7c673cae
FG
617
618 activate_osd $dir $id "$@"
619}
620
621function run_osd_bluestore() {
622 local dir=$1
623 shift
624 local id=$1
625 shift
626 local osd_data=$dir/$id
627
628 local ceph_disk_args
629 ceph_disk_args+=" --statedir=$dir"
630 ceph_disk_args+=" --sysconfdir=$dir"
631 ceph_disk_args+=" --prepend-to-path="
632
633 mkdir -p $osd_data
634 ceph-disk $ceph_disk_args \
635 prepare --bluestore $osd_data || return 1
636
31f18b77 637 activate_osd $dir $id "$@"
7c673cae
FG
638}
639
640function test_run_osd() {
641 local dir=$1
642
643 setup $dir || return 1
644
645 run_mon $dir a || return 1
646 run_mgr $dir x || return 1
647
648 run_osd $dir 0 || return 1
c07f9fc5 649 local backfills=$(CEPH_ARGS='' ceph --format=json daemon $(get_asok_path osd.0) \
7c673cae
FG
650 config get osd_max_backfills)
651 echo "$backfills" | grep --quiet 'osd_max_backfills' || return 1
652
653 run_osd $dir 1 --osd-max-backfills 20 || return 1
c07f9fc5 654 local backfills=$(CEPH_ARGS='' ceph --format=json daemon $(get_asok_path osd.1) \
7c673cae
FG
655 config get osd_max_backfills)
656 test "$backfills" = '{"osd_max_backfills":"20"}' || return 1
657
658 CEPH_ARGS="$CEPH_ARGS --osd-max-backfills 30" run_osd $dir 2 || return 1
c07f9fc5 659 local backfills=$(CEPH_ARGS='' ceph --format=json daemon $(get_asok_path osd.2) \
7c673cae
FG
660 config get osd_max_backfills)
661 test "$backfills" = '{"osd_max_backfills":"30"}' || return 1
662
663 teardown $dir || return 1
664}
665
666#######################################################################
667
668##
669# Shutdown and remove all traces of the osd by the name osd.**id**.
670#
671# The OSD is shutdown with the TERM signal. It is then removed from
672# the auth list, crush map, osd map etc and the files associated with
673# it are also removed.
674#
675# @param dir path name of the environment
676# @param id osd identifier
677# @return 0 on success, 1 on error
678#
679function destroy_osd() {
680 local dir=$1
681 local id=$2
682
7c673cae 683 ceph osd out osd.$id || return 1
c07f9fc5
FG
684 kill_daemons $dir TERM osd.$id || return 1
685 ceph osd purge osd.$id --yes-i-really-mean-it || return 1
7c673cae
FG
686 teardown $dir/$id || return 1
687 rm -fr $dir/$id
688}
689
690function test_destroy_osd() {
691 local dir=$1
692
693 setup $dir || return 1
694 run_mon $dir a || return 1
695 run_mgr $dir x || return 1
696 run_osd $dir 0 || return 1
697 destroy_osd $dir 0 || return 1
698 ! ceph osd dump | grep "osd.$id " || return 1
699 teardown $dir || return 1
700}
701
702#######################################################################
703
704##
705# Run (activate) an osd by the name osd.**id** with data in
706# **dir**/**id**. The logs can be found in **dir**/osd.**id**.log,
707# the pid file is **dir**/osd.**id**.pid and the admin socket is
708# **dir**/**id**/ceph-osd.**id**.asok.
709#
710# The remaining arguments are passed verbatim to ceph-osd.
711#
712# Two mandatory arguments must be provided: --fsid and --mon-host
713# Instead of adding them to every call to activate_osd, they can be
714# set in the CEPH_ARGS environment variable to be read implicitly
715# by every ceph command.
716#
717# The CEPH_CONF variable is expected to be set to /dev/null to
718# only rely on arguments for configuration.
719#
720# The activate_osd function expects a valid OSD data directory
721# in **dir**/**id**, either just created via run_osd or re-using
722# one left by a previous run of ceph-osd. The ceph-osd daemon is
723# run indirectly via ceph-disk activate.
724#
725# The activate_osd function blocks until the monitor reports the osd
726# up. If it fails to do so within $TIMEOUT seconds, activate_osd
727# fails.
728#
729# Examples:
730#
731# CEPH_ARGS="--fsid=$(uuidgen) "
732# CEPH_ARGS+="--mon-host=127.0.0.1:7018 "
733# activate_osd $dir 0 # activate an osd using the monitor listening on 7018
734#
735# @param dir path name of the environment
736# @param id osd identifier
737# @param ... can be any option valid for ceph-osd
738# @return 0 on success, 1 on error
739#
740function activate_osd() {
741 local dir=$1
742 shift
743 local id=$1
744 shift
745 local osd_data=$dir/$id
746
747 local ceph_disk_args
748 ceph_disk_args+=" --statedir=$dir"
749 ceph_disk_args+=" --sysconfdir=$dir"
750 ceph_disk_args+=" --prepend-to-path="
751
752 local ceph_args="$CEPH_ARGS"
7c673cae
FG
753 ceph_args+=" --osd-failsafe-full-ratio=.99"
754 ceph_args+=" --osd-journal-size=100"
755 ceph_args+=" --osd-scrub-load-threshold=2000"
756 ceph_args+=" --osd-data=$osd_data"
757 ceph_args+=" --chdir="
c07f9fc5 758 ceph_args+=$EXTRA_OPTS
7c673cae 759 ceph_args+=" --run-dir=$dir"
c07f9fc5 760 ceph_args+=" --admin-socket=$(get_asok_path)"
7c673cae
FG
761 ceph_args+=" --debug-osd=20"
762 ceph_args+=" --log-file=$dir/\$name.log"
763 ceph_args+=" --pid-file=$dir/\$name.pid"
764 ceph_args+=" --osd-max-object-name-len 460"
765 ceph_args+=" --osd-max-object-namespace-len 64"
224ce89b 766 ceph_args+=" --enable-experimental-unrecoverable-data-corrupting-features *"
7c673cae
FG
767 ceph_args+=" "
768 ceph_args+="$@"
769 mkdir -p $osd_data
770 CEPH_ARGS="$ceph_args " ceph-disk $ceph_disk_args \
771 activate \
772 --mark-init=none \
773 $osd_data || return 1
774
775 [ "$id" = "$(cat $osd_data/whoami)" ] || return 1
776
777 wait_for_osd up $id || return 1
778}
779
780function test_activate_osd() {
781 local dir=$1
782
783 setup $dir || return 1
784
785 run_mon $dir a || return 1
786 run_mgr $dir x || return 1
787
788 run_osd $dir 0 || return 1
c07f9fc5 789 local backfills=$(CEPH_ARGS='' ceph --format=json daemon $(get_asok_path osd.0) \
7c673cae
FG
790 config get osd_max_backfills)
791 echo "$backfills" | grep --quiet 'osd_max_backfills' || return 1
792
793 kill_daemons $dir TERM osd || return 1
794
795 activate_osd $dir 0 --osd-max-backfills 20 || return 1
c07f9fc5 796 local backfills=$(CEPH_ARGS='' ceph --format=json daemon $(get_asok_path osd.0) \
7c673cae
FG
797 config get osd_max_backfills)
798 test "$backfills" = '{"osd_max_backfills":"20"}' || return 1
799
800 teardown $dir || return 1
801}
802
803#######################################################################
804
805##
806# Wait until the OSD **id** is either up or down, as specified by
807# **state**. It fails after $TIMEOUT seconds.
808#
809# @param state either up or down
810# @param id osd identifier
811# @return 0 on success, 1 on error
812#
813function wait_for_osd() {
814 local state=$1
815 local id=$2
816
817 status=1
818 for ((i=0; i < $TIMEOUT; i++)); do
819 echo $i
820 if ! ceph osd dump | grep "osd.$id $state"; then
821 sleep 1
822 else
823 status=0
824 break
825 fi
826 done
827 return $status
828}
829
830function test_wait_for_osd() {
831 local dir=$1
832 setup $dir || return 1
833 run_mon $dir a --osd_pool_default_size=1 || return 1
834 run_mgr $dir x || return 1
835 run_osd $dir 0 || return 1
836 wait_for_osd up 0 || return 1
837 kill_daemons $dir TERM osd || return 1
838 wait_for_osd down 0 || return 1
839 ( TIMEOUT=1 ; ! wait_for_osd up 0 ) || return 1
840 teardown $dir || return 1
841}
842
843#######################################################################
844
845##
846# Display the list of OSD ids supporting the **objectname** stored in
847# **poolname**, as reported by ceph osd map.
848#
849# @param poolname an existing pool
850# @param objectname an objectname (may or may not exist)
851# @param STDOUT white space separated list of OSD ids
852# @return 0 on success, 1 on error
853#
854function get_osds() {
855 local poolname=$1
856 local objectname=$2
857
31f18b77
FG
858 local osds=$(ceph --format json osd map $poolname $objectname 2>/dev/null | \
859 jq '.acting | .[]')
7c673cae
FG
860 # get rid of the trailing space
861 echo $osds
862}
863
864function test_get_osds() {
865 local dir=$1
866
867 setup $dir || return 1
868 run_mon $dir a --osd_pool_default_size=2 || return 1
869 run_mgr $dir x || return 1
870 run_osd $dir 0 || return 1
871 run_osd $dir 1 || return 1
c07f9fc5 872 create_rbd_pool || return 1
7c673cae 873 wait_for_clean || return 1
c07f9fc5 874 create_rbd_pool || return 1
7c673cae
FG
875 get_osds rbd GROUP | grep --quiet '^[0-1] [0-1]$' || return 1
876 teardown $dir || return 1
877}
878
879#######################################################################
880
881##
882# Wait for the monitor to form quorum (optionally, of size N)
883#
884# @param timeout duration (lower-bound) to wait for quorum to be formed
885# @param quorumsize size of quorum to wait for
886# @return 0 on success, 1 on error
887#
888function wait_for_quorum() {
889 local timeout=$1
890 local quorumsize=$2
891
892 if [[ -z "$timeout" ]]; then
893 timeout=300
894 fi
895
896 if [[ -z "$quorumsize" ]]; then
897 timeout $timeout ceph mon_status --format=json >&/dev/null || return 1
898 return 0
899 fi
900
901 no_quorum=1
c07f9fc5 902 wait_until=$((`date +%s` + $timeout))
7c673cae
FG
903 while [[ $(date +%s) -lt $wait_until ]]; do
904 jqfilter='.quorum | length == '$quorumsize
905 jqinput="$(timeout $timeout ceph mon_status --format=json 2>/dev/null)"
906 res=$(echo $jqinput | jq "$jqfilter")
907 if [[ "$res" == "true" ]]; then
908 no_quorum=0
909 break
910 fi
911 done
912 return $no_quorum
913}
914
915#######################################################################
916
917##
918# Return the PG of supporting the **objectname** stored in
919# **poolname**, as reported by ceph osd map.
920#
921# @param poolname an existing pool
922# @param objectname an objectname (may or may not exist)
923# @param STDOUT a PG
924# @return 0 on success, 1 on error
925#
926function get_pg() {
927 local poolname=$1
928 local objectname=$2
929
31f18b77 930 ceph --format json osd map $poolname $objectname 2>/dev/null | jq -r '.pgid'
7c673cae
FG
931}
932
933function test_get_pg() {
934 local dir=$1
935
936 setup $dir || return 1
937 run_mon $dir a --osd_pool_default_size=1 || return 1
938 run_mgr $dir x || return 1
939 run_osd $dir 0 || return 1
c07f9fc5 940 create_rbd_pool || return 1
7c673cae
FG
941 wait_for_clean || return 1
942 get_pg rbd GROUP | grep --quiet '^[0-9]\.[0-9a-f][0-9a-f]*$' || return 1
943 teardown $dir || return 1
944}
945
946#######################################################################
947
948##
949# Return the value of the **config**, obtained via the config get command
950# of the admin socket of **daemon**.**id**.
951#
952# @param daemon mon or osd
953# @param id mon or osd ID
954# @param config the configuration variable name as found in config_opts.h
955# @param STDOUT the config value
956# @return 0 on success, 1 on error
957#
958function get_config() {
959 local daemon=$1
960 local id=$2
961 local config=$3
962
963 CEPH_ARGS='' \
c07f9fc5 964 ceph --format json daemon $(get_asok_path $daemon.$id) \
7c673cae 965 config get $config 2> /dev/null | \
31f18b77 966 jq -r ".$config"
7c673cae
FG
967}
968
969function test_get_config() {
970 local dir=$1
971
972 # override the default config using command line arg and check it
973 setup $dir || return 1
974 run_mon $dir a --osd_pool_default_size=1 || return 1
975 test $(get_config mon a osd_pool_default_size) = 1 || return 1
976 run_mgr $dir x || return 1
977 run_osd $dir 0 --osd_max_scrubs=3 || return 1
978 test $(get_config osd 0 osd_max_scrubs) = 3 || return 1
979 teardown $dir || return 1
980}
981
982#######################################################################
983
984##
985# Set the **config** to specified **value**, via the config set command
986# of the admin socket of **daemon**.**id**
987#
988# @param daemon mon or osd
989# @param id mon or osd ID
990# @param config the configuration variable name as found in config_opts.h
991# @param value the config value
992# @return 0 on success, 1 on error
993#
994function set_config() {
995 local daemon=$1
996 local id=$2
997 local config=$3
998 local value=$4
999
c07f9fc5 1000 test $(env CEPH_ARGS='' ceph --format json daemon $(get_asok_path $daemon.$id) \
31f18b77
FG
1001 config set $config $value 2> /dev/null | \
1002 jq 'has("success")') == true
7c673cae
FG
1003}
1004
1005function test_set_config() {
1006 local dir=$1
1007
1008 setup $dir || return 1
1009 run_mon $dir a --osd_pool_default_size=1 || return 1
1010 test $(get_config mon a ms_crc_header) = true || return 1
1011 set_config mon a ms_crc_header false || return 1
1012 test $(get_config mon a ms_crc_header) = false || return 1
1013 set_config mon a ms_crc_header true || return 1
1014 test $(get_config mon a ms_crc_header) = true || return 1
1015 teardown $dir || return 1
1016}
1017
1018#######################################################################
1019
1020##
1021# Return the OSD id of the primary OSD supporting the **objectname**
1022# stored in **poolname**, as reported by ceph osd map.
1023#
1024# @param poolname an existing pool
1025# @param objectname an objectname (may or may not exist)
1026# @param STDOUT the primary OSD id
1027# @return 0 on success, 1 on error
1028#
1029function get_primary() {
1030 local poolname=$1
1031 local objectname=$2
1032
31f18b77
FG
1033 ceph --format json osd map $poolname $objectname 2>/dev/null | \
1034 jq '.acting_primary'
7c673cae
FG
1035}
1036
1037function test_get_primary() {
1038 local dir=$1
1039
1040 setup $dir || return 1
1041 run_mon $dir a --osd_pool_default_size=1 || return 1
1042 local osd=0
1043 run_mgr $dir x || return 1
1044 run_osd $dir $osd || return 1
c07f9fc5 1045 create_rbd_pool || return 1
7c673cae
FG
1046 wait_for_clean || return 1
1047 test $(get_primary rbd GROUP) = $osd || return 1
1048 teardown $dir || return 1
1049}
1050
1051#######################################################################
1052
1053##
1054# Return the id of any OSD supporting the **objectname** stored in
1055# **poolname**, as reported by ceph osd map, except the primary.
1056#
1057# @param poolname an existing pool
1058# @param objectname an objectname (may or may not exist)
1059# @param STDOUT the OSD id
1060# @return 0 on success, 1 on error
1061#
1062function get_not_primary() {
1063 local poolname=$1
1064 local objectname=$2
1065
1066 local primary=$(get_primary $poolname $objectname)
31f18b77
FG
1067 ceph --format json osd map $poolname $objectname 2>/dev/null | \
1068 jq ".acting | map(select (. != $primary)) | .[0]"
7c673cae
FG
1069}
1070
1071function test_get_not_primary() {
1072 local dir=$1
1073
1074 setup $dir || return 1
1075 run_mon $dir a --osd_pool_default_size=2 || return 1
1076 run_mgr $dir x || return 1
1077 run_osd $dir 0 || return 1
1078 run_osd $dir 1 || return 1
c07f9fc5 1079 create_rbd_pool || return 1
7c673cae
FG
1080 wait_for_clean || return 1
1081 local primary=$(get_primary rbd GROUP)
1082 local not_primary=$(get_not_primary rbd GROUP)
1083 test $not_primary != $primary || return 1
1084 test $not_primary = 0 -o $not_primary = 1 || return 1
1085 teardown $dir || return 1
1086}
1087
1088#######################################################################
1089
1090##
1091# Run ceph-objectstore-tool against the OSD **id** using the data path
1092# **dir**. The OSD is killed with TERM prior to running
1093# ceph-objectstore-tool because access to the data path is
1094# exclusive. The OSD is restarted after the command completes. The
1095# objectstore_tool returns after all PG are active+clean again.
1096#
1097# @param dir the data path of the OSD
1098# @param id the OSD id
1099# @param ... arguments to ceph-objectstore-tool
1100# @param STDIN the input of ceph-objectstore-tool
1101# @param STDOUT the output of ceph-objectstore-tool
1102# @return 0 on success, 1 on error
1103#
1104# The value of $ceph_osd_args will be passed to restarted osds
1105#
1106function objectstore_tool() {
1107 local dir=$1
1108 shift
1109 local id=$1
1110 shift
1111 local osd_data=$dir/$id
1112
1113 local osd_type=$(cat $osd_data/type)
1114
1115 kill_daemons $dir TERM osd.$id >&2 < /dev/null || return 1
1116
1117 local journal_args
1118 if [ "$objectstore_type" == "filestore" ]; then
1119 journal_args=" --journal-path $osd_data/journal"
1120 fi
1121 ceph-objectstore-tool \
7c673cae
FG
1122 --data-path $osd_data \
1123 $journal_args \
1124 "$@" || return 1
1125 activate_osd $dir $id $ceph_osd_args >&2 || return 1
1126 wait_for_clean >&2
1127}
1128
1129function test_objectstore_tool() {
1130 local dir=$1
1131
1132 setup $dir || return 1
1133 run_mon $dir a --osd_pool_default_size=1 || return 1
1134 local osd=0
1135 run_mgr $dir x || return 1
1136 run_osd $dir $osd || return 1
c07f9fc5 1137 create_rbd_pool || return 1
7c673cae
FG
1138 wait_for_clean || return 1
1139 rados --pool rbd put GROUP /etc/group || return 1
1140 objectstore_tool $dir $osd GROUP get-bytes | \
1141 diff - /etc/group
1142 ! objectstore_tool $dir $osd NOTEXISTS get-bytes || return 1
1143 teardown $dir || return 1
1144}
1145
1146#######################################################################
1147
1148##
1149# Predicate checking if there is an ongoing recovery in the
1150# cluster. If any of the recovering_{keys,bytes,objects}_per_sec
1151# counters are reported by ceph status, it means recovery is in
1152# progress.
1153#
1154# @return 0 if recovery in progress, 1 otherwise
1155#
1156function get_is_making_recovery_progress() {
31f18b77
FG
1157 local recovery_progress
1158 recovery_progress+=".recovering_keys_per_sec + "
1159 recovery_progress+=".recovering_bytes_per_sec + "
1160 recovery_progress+=".recovering_objects_per_sec"
1161 local progress=$(ceph --format json status 2>/dev/null | \
1162 jq -r ".pgmap | $recovery_progress")
1163 test "$progress" != null
7c673cae
FG
1164}
1165
1166function test_get_is_making_recovery_progress() {
1167 local dir=$1
1168
1169 setup $dir || return 1
1170 run_mon $dir a || return 1
1171 run_mgr $dir x || return 1
1172 ! get_is_making_recovery_progress || return 1
1173 teardown $dir || return 1
1174}
1175
1176#######################################################################
1177
1178##
1179# Return the number of active PGs in the cluster. A PG is active if
1180# ceph pg dump pgs reports it both **active** and **clean** and that
1181# not **stale**.
1182#
1183# @param STDOUT the number of active PGs
1184# @return 0 on success, 1 on error
1185#
1186function get_num_active_clean() {
31f18b77
FG
1187 local expression
1188 expression+="select(contains(\"active\") and contains(\"clean\")) | "
1189 expression+="select(contains(\"stale\") | not)"
1190 ceph --format json pg dump pgs 2>/dev/null | \
1191 jq "[.[] | .state | $expression] | length"
7c673cae
FG
1192}
1193
1194function test_get_num_active_clean() {
1195 local dir=$1
1196
1197 setup $dir || return 1
1198 run_mon $dir a --osd_pool_default_size=1 || return 1
1199 run_mgr $dir x || return 1
1200 run_osd $dir 0 || return 1
c07f9fc5 1201 create_rbd_pool || return 1
7c673cae
FG
1202 wait_for_clean || return 1
1203 local num_active_clean=$(get_num_active_clean)
1204 test "$num_active_clean" = $PG_NUM || return 1
1205 teardown $dir || return 1
1206}
1207
1208#######################################################################
1209
1210##
1211# Return the number of PGs in the cluster, according to
1212# ceph pg dump pgs.
1213#
1214# @param STDOUT the number of PGs
1215# @return 0 on success, 1 on error
1216#
1217function get_num_pgs() {
31f18b77 1218 ceph --format json status 2>/dev/null | jq '.pgmap.num_pgs'
7c673cae
FG
1219}
1220
1221function test_get_num_pgs() {
1222 local dir=$1
1223
1224 setup $dir || return 1
1225 run_mon $dir a --osd_pool_default_size=1 || return 1
1226 run_mgr $dir x || return 1
1227 run_osd $dir 0 || return 1
c07f9fc5 1228 create_rbd_pool || return 1
7c673cae
FG
1229 wait_for_clean || return 1
1230 local num_pgs=$(get_num_pgs)
1231 test "$num_pgs" -gt 0 || return 1
1232 teardown $dir || return 1
1233}
1234
1235#######################################################################
1236
c07f9fc5
FG
1237##
1238# Return the OSD ids in use by at least one PG in the cluster (either
1239# in the up or the acting set), according to ceph pg dump pgs. Every
1240# OSD id shows as many times as they are used in up and acting sets.
1241# If an OSD id is in both the up and acting set of a given PG, it will
1242# show twice.
1243#
1244# @param STDOUT a sorted list of OSD ids
1245# @return 0 on success, 1 on error
1246#
1247function get_osd_id_used_by_pgs() {
1248 ceph --format json pg dump pgs 2>/dev/null | jq '.[] | .up[], .acting[]' | sort
1249}
1250
1251function test_get_osd_id_used_by_pgs() {
1252 local dir=$1
1253
1254 setup $dir || return 1
1255 run_mon $dir a --osd_pool_default_size=1 || return 1
1256 run_mgr $dir x || return 1
1257 run_osd $dir 0 || return 1
1258 create_rbd_pool || return 1
1259 wait_for_clean || return 1
1260 local osd_ids=$(get_osd_id_used_by_pgs | uniq)
1261 test "$osd_ids" = "0" || return 1
1262 teardown $dir || return 1
1263}
1264
1265#######################################################################
1266
1267##
1268# Wait until the OSD **id** shows **count** times in the
1269# PGs (see get_osd_id_used_by_pgs for more information about
1270# how OSD ids are counted).
1271#
1272# @param id the OSD id
1273# @param count the number of time it must show in the PGs
1274# @return 0 on success, 1 on error
1275#
1276function wait_osd_id_used_by_pgs() {
1277 local id=$1
1278 local count=$2
1279
1280 status=1
1281 for ((i=0; i < $TIMEOUT / 5; i++)); do
1282 echo $i
1283 if ! test $(get_osd_id_used_by_pgs | grep -c $id) = $count ; then
1284 sleep 5
1285 else
1286 status=0
1287 break
1288 fi
1289 done
1290 return $status
1291}
1292
1293function test_wait_osd_id_used_by_pgs() {
1294 local dir=$1
1295
1296 setup $dir || return 1
1297 run_mon $dir a --osd_pool_default_size=1 || return 1
1298 run_mgr $dir x || return 1
1299 run_osd $dir 0 || return 1
1300 create_rbd_pool || return 1
1301 wait_for_clean || return 1
1302 wait_osd_id_used_by_pgs 0 8 || return 1
1303 ! TIMEOUT=1 wait_osd_id_used_by_pgs 123 5 || return 1
1304 teardown $dir || return 1
1305}
1306
1307#######################################################################
1308
7c673cae
FG
1309##
1310# Return the date and time of the last completed scrub for **pgid**,
1311# as reported by ceph pg dump pgs. Note that a repair also sets this
1312# date.
1313#
1314# @param pgid the id of the PG
1315# @param STDOUT the date and time of the last scrub
1316# @return 0 on success, 1 on error
1317#
1318function get_last_scrub_stamp() {
1319 local pgid=$1
1320 local sname=${2:-last_scrub_stamp}
31f18b77
FG
1321 ceph --format json pg dump pgs 2>/dev/null | \
1322 jq -r ".[] | select(.pgid==\"$pgid\") | .$sname"
7c673cae
FG
1323}
1324
1325function test_get_last_scrub_stamp() {
1326 local dir=$1
1327
1328 setup $dir || return 1
1329 run_mon $dir a --osd_pool_default_size=1 || return 1
1330 run_mgr $dir x || return 1
1331 run_osd $dir 0 || return 1
c07f9fc5 1332 create_rbd_pool || return 1
7c673cae 1333 wait_for_clean || return 1
b5b8bbf5 1334 stamp=$(get_last_scrub_stamp 1.0)
7c673cae
FG
1335 test -n "$stamp" || return 1
1336 teardown $dir || return 1
1337}
1338
1339#######################################################################
1340
1341##
1342# Predicate checking if the cluster is clean, i.e. all of its PGs are
1343# in a clean state (see get_num_active_clean for a definition).
1344#
1345# @return 0 if the cluster is clean, 1 otherwise
1346#
1347function is_clean() {
1348 num_pgs=$(get_num_pgs)
1349 test $num_pgs != 0 || return 1
1350 test $(get_num_active_clean) = $num_pgs || return 1
1351}
1352
1353function test_is_clean() {
1354 local dir=$1
1355
1356 setup $dir || return 1
1357 run_mon $dir a --osd_pool_default_size=1 || return 1
1358 run_mgr $dir x || return 1
1359 run_osd $dir 0 || return 1
c07f9fc5 1360 create_rbd_pool || return 1
7c673cae
FG
1361 wait_for_clean || return 1
1362 is_clean || return 1
1363 teardown $dir || return 1
1364}
1365
1366#######################################################################
1367
f64942e4 1368calc() { $AWK "BEGIN{print $*}"; }
94b18763 1369
7c673cae
FG
1370##
1371# Return a list of numbers that are increasingly larger and whose
1372# total is **timeout** seconds. It can be used to have short sleep
1373# delay while waiting for an event on a fast machine. But if running
1374# very slowly the larger delays avoid stressing the machine even
1375# further or spamming the logs.
1376#
1377# @param timeout sum of all delays, in seconds
1378# @return a list of sleep delays
1379#
1380function get_timeout_delays() {
1381 local trace=$(shopt -q -o xtrace && echo true || echo false)
1382 $trace && shopt -u -o xtrace
1383 local timeout=$1
1384 local first_step=${2:-1}
1385
1386 local i
1387 local total="0"
1388 i=$first_step
94b18763
FG
1389 while test "$(calc $total + $i \<= $timeout)" = "1"; do
1390 echo -n "$(calc $i) "
1391 total=$(calc $total + $i)
1392 i=$(calc $i \* 2)
7c673cae 1393 done
94b18763
FG
1394 if test "$(calc $total \< $timeout)" = "1"; then
1395 echo -n "$(calc $timeout - $total) "
7c673cae
FG
1396 fi
1397 $trace && shopt -s -o xtrace
1398}
1399
1400function test_get_timeout_delays() {
1401 test "$(get_timeout_delays 1)" = "1 " || return 1
94b18763
FG
1402 test "$(get_timeout_delays 5)" = "1 2 2 " || return 1
1403 test "$(get_timeout_delays 6)" = "1 2 3 " || return 1
7c673cae 1404 test "$(get_timeout_delays 7)" = "1 2 4 " || return 1
94b18763
FG
1405 test "$(get_timeout_delays 8)" = "1 2 4 1 " || return 1
1406 test "$(get_timeout_delays 1 .1)" = "0.1 0.2 0.4 0.3 " || return 1
1407 test "$(get_timeout_delays 1.5 .1)" = "0.1 0.2 0.4 0.8 " || return 1
1408 test "$(get_timeout_delays 5 .1)" = "0.1 0.2 0.4 0.8 1.6 1.9 " || return 1
1409 test "$(get_timeout_delays 6 .1)" = "0.1 0.2 0.4 0.8 1.6 2.9 " || return 1
1410 test "$(get_timeout_delays 6.3 .1)" = "0.1 0.2 0.4 0.8 1.6 3.2 " || return 1
1411 test "$(get_timeout_delays 20 .1)" = "0.1 0.2 0.4 0.8 1.6 3.2 6.4 7.3 " || return 1
7c673cae
FG
1412}
1413
1414#######################################################################
1415
1416##
1417# Wait until the cluster becomes clean or if it does not make progress
1418# for $TIMEOUT seconds.
1419# Progress is measured either via the **get_is_making_recovery_progress**
1420# predicate or if the number of clean PGs changes (as returned by get_num_active_clean)
1421#
1422# @return 0 if the cluster is clean, 1 otherwise
1423#
1424function wait_for_clean() {
a8e16298 1425 local cmd=$1
7c673cae
FG
1426 local num_active_clean=-1
1427 local cur_active_clean
1428 local -a delays=($(get_timeout_delays $TIMEOUT .1))
1429 local -i loop=0
31f18b77 1430
3a9019d9 1431 flush_pg_stats || return 1
31f18b77
FG
1432 while test $(get_num_pgs) == 0 ; do
1433 sleep 1
1434 done
7c673cae
FG
1435
1436 while true ; do
1437 # Comparing get_num_active_clean & get_num_pgs is used to determine
1438 # if the cluster is clean. That's almost an inline of is_clean() to
1439 # get more performance by avoiding multiple calls of get_num_active_clean.
1440 cur_active_clean=$(get_num_active_clean)
1441 test $cur_active_clean = $(get_num_pgs) && break
1442 if test $cur_active_clean != $num_active_clean ; then
1443 loop=0
1444 num_active_clean=$cur_active_clean
1445 elif get_is_making_recovery_progress ; then
1446 loop=0
1447 elif (( $loop >= ${#delays[*]} )) ; then
1448 ceph report
1449 return 1
1450 fi
a8e16298
TL
1451 # eval is a no-op if cmd is empty
1452 eval $cmd
7c673cae
FG
1453 sleep ${delays[$loop]}
1454 loop+=1
1455 done
1456 return 0
1457}
1458
1459function test_wait_for_clean() {
1460 local dir=$1
1461
1462 setup $dir || return 1
1463 run_mon $dir a --osd_pool_default_size=1 || return 1
1464 run_mgr $dir x || return 1
c07f9fc5 1465 create_rbd_pool || return 1
7c673cae
FG
1466 ! TIMEOUT=1 wait_for_clean || return 1
1467 run_osd $dir 0 || return 1
1468 wait_for_clean || return 1
1469 teardown $dir || return 1
1470}
1471
1472#######################################################################
1473
1474##
1adf2230
AA
1475# Wait until the cluster has health condition passed as arg
1476# again for $TIMEOUT seconds.
7c673cae 1477#
1adf2230
AA
1478# @param string to grep for in health detail
1479# @return 0 if the cluster health matches request, 1 otherwise
7c673cae
FG
1480#
1481function wait_for_health() {
1482 local grepstr=$1
1483 local -a delays=($(get_timeout_delays $TIMEOUT .1))
1484 local -i loop=0
1485
1486 while ! ceph health detail | grep "$grepstr" ; do
1487 if (( $loop >= ${#delays[*]} )) ; then
1488 ceph health detail
1489 return 1
1490 fi
1491 sleep ${delays[$loop]}
1492 loop+=1
1493 done
1494}
1495
1adf2230
AA
1496##
1497# Wait until the cluster becomes HEALTH_OK again or if it does not make progress
1498# for $TIMEOUT seconds.
1499#
1500# @return 0 if the cluster is HEALTHY, 1 otherwise
1501#
7c673cae
FG
1502function wait_for_health_ok() {
1503 wait_for_health "HEALTH_OK" || return 1
1504}
1505
1506function test_wait_for_health_ok() {
1507 local dir=$1
1508
1509 setup $dir || return 1
1510 run_mon $dir a --osd_pool_default_size=1 --osd_failsafe_full_ratio=.99 --mon_pg_warn_min_per_osd=0 || return 1
31f18b77 1511 run_mgr $dir x --mon_pg_warn_min_per_osd=0 || return 1
7c673cae 1512 run_osd $dir 0 || return 1
224ce89b
WB
1513 kill_daemons $dir TERM osd || return 1
1514 ! TIMEOUT=1 wait_for_health_ok || return 1
1515 activate_osd $dir 0 || return 1
7c673cae
FG
1516 wait_for_health_ok || return 1
1517 teardown $dir || return 1
1518}
1519
1520
1521#######################################################################
1522
1523##
1524# Run repair on **pgid** and wait until it completes. The repair
1525# function will fail if repair does not complete within $TIMEOUT
1526# seconds.
1527#
1528# @param pgid the id of the PG
1529# @return 0 on success, 1 on error
1530#
1531function repair() {
1532 local pgid=$1
1533 local last_scrub=$(get_last_scrub_stamp $pgid)
1534 ceph pg repair $pgid
1535 wait_for_scrub $pgid "$last_scrub"
1536}
1537
1538function test_repair() {
1539 local dir=$1
1540
1541 setup $dir || return 1
1542 run_mon $dir a --osd_pool_default_size=1 || return 1
1543 run_mgr $dir x || return 1
1544 run_osd $dir 0 || return 1
c07f9fc5 1545 create_rbd_pool || return 1
7c673cae 1546 wait_for_clean || return 1
b5b8bbf5 1547 repair 1.0 || return 1
7c673cae 1548 kill_daemons $dir KILL osd || return 1
b5b8bbf5 1549 ! TIMEOUT=1 repair 1.0 || return 1
7c673cae
FG
1550 teardown $dir || return 1
1551}
1552#######################################################################
1553
1554##
1555# Run scrub on **pgid** and wait until it completes. The pg_scrub
1556# function will fail if repair does not complete within $TIMEOUT
1557# seconds. The pg_scrub is complete whenever the
1558# **get_last_scrub_stamp** function reports a timestamp different from
1559# the one stored before starting the scrub.
1560#
1561# @param pgid the id of the PG
1562# @return 0 on success, 1 on error
1563#
1564function pg_scrub() {
1565 local pgid=$1
1566 local last_scrub=$(get_last_scrub_stamp $pgid)
1567 ceph pg scrub $pgid
1568 wait_for_scrub $pgid "$last_scrub"
1569}
1570
1571function pg_deep_scrub() {
1572 local pgid=$1
1573 local last_scrub=$(get_last_scrub_stamp $pgid last_deep_scrub_stamp)
1574 ceph pg deep-scrub $pgid
1575 wait_for_scrub $pgid "$last_scrub" last_deep_scrub_stamp
1576}
1577
1578function test_pg_scrub() {
1579 local dir=$1
1580
1581 setup $dir || return 1
1582 run_mon $dir a --osd_pool_default_size=1 || return 1
1583 run_mgr $dir x || return 1
1584 run_osd $dir 0 || return 1
c07f9fc5 1585 create_rbd_pool || return 1
7c673cae 1586 wait_for_clean || return 1
b5b8bbf5 1587 pg_scrub 1.0 || return 1
7c673cae 1588 kill_daemons $dir KILL osd || return 1
b5b8bbf5 1589 ! TIMEOUT=1 pg_scrub 1.0 || return 1
7c673cae
FG
1590 teardown $dir || return 1
1591}
1592
1593#######################################################################
1594
1595##
1596# Run the *command* and expect it to fail (i.e. return a non zero status).
1597# The output (stderr and stdout) is stored in a temporary file in *dir*
1598# and is expected to contain the string *expected*.
1599#
1600# Return 0 if the command failed and the string was found. Otherwise
1601# return 1 and cat the full output of the command on stderr for debug.
1602#
1603# @param dir temporary directory to store the output
1604# @param expected string to look for in the output
1605# @param command ... the command and its arguments
1606# @return 0 on success, 1 on error
1607#
1608
1609function expect_failure() {
1610 local dir=$1
1611 shift
1612 local expected="$1"
1613 shift
1614 local success
1615
1616 if "$@" > $dir/out 2>&1 ; then
1617 success=true
1618 else
1619 success=false
1620 fi
1621
1622 if $success || ! grep --quiet "$expected" $dir/out ; then
1623 cat $dir/out >&2
1624 return 1
1625 else
1626 return 0
1627 fi
1628}
1629
1630function test_expect_failure() {
1631 local dir=$1
1632
1633 setup $dir || return 1
1634 expect_failure $dir FAIL bash -c 'echo FAIL ; exit 1' || return 1
1635 # the command did not fail
1636 ! expect_failure $dir FAIL bash -c 'echo FAIL ; exit 0' > $dir/out || return 1
1637 grep --quiet FAIL $dir/out || return 1
1638 # the command failed but the output does not contain the expected string
1639 ! expect_failure $dir FAIL bash -c 'echo UNEXPECTED ; exit 1' > $dir/out || return 1
1640 ! grep --quiet FAIL $dir/out || return 1
1641 teardown $dir || return 1
1642}
1643
1644#######################################################################
1645
1646##
1647# Given the *last_scrub*, wait for scrub to happen on **pgid**. It
1648# will fail if scrub does not complete within $TIMEOUT seconds. The
1649# repair is complete whenever the **get_last_scrub_stamp** function
1650# reports a timestamp different from the one given in argument.
1651#
1652# @param pgid the id of the PG
1653# @param last_scrub timestamp of the last scrub for *pgid*
1654# @return 0 on success, 1 on error
1655#
1656function wait_for_scrub() {
1657 local pgid=$1
1658 local last_scrub="$2"
1659 local sname=${3:-last_scrub_stamp}
1660
1661 for ((i=0; i < $TIMEOUT; i++)); do
b5b8bbf5 1662 if test "$(get_last_scrub_stamp $pgid $sname)" '>' "$last_scrub" ; then
7c673cae
FG
1663 return 0
1664 fi
1665 sleep 1
1666 done
1667 return 1
1668}
1669
1670function test_wait_for_scrub() {
1671 local dir=$1
1672
1673 setup $dir || return 1
1674 run_mon $dir a --osd_pool_default_size=1 || return 1
1675 run_mgr $dir x || return 1
1676 run_osd $dir 0 || return 1
c07f9fc5 1677 create_rbd_pool || return 1
7c673cae 1678 wait_for_clean || return 1
b5b8bbf5 1679 local pgid=1.0
7c673cae
FG
1680 ceph pg repair $pgid
1681 local last_scrub=$(get_last_scrub_stamp $pgid)
1682 wait_for_scrub $pgid "$last_scrub" || return 1
1683 kill_daemons $dir KILL osd || return 1
1684 last_scrub=$(get_last_scrub_stamp $pgid)
1685 ! TIMEOUT=1 wait_for_scrub $pgid "$last_scrub" || return 1
1686 teardown $dir || return 1
1687}
1688
1689#######################################################################
1690
1691##
1692# Return 0 if the erasure code *plugin* is available, 1 otherwise.
1693#
1694# @param plugin erasure code plugin
1695# @return 0 on success, 1 on error
1696#
1697
1698function erasure_code_plugin_exists() {
1699 local plugin=$1
1700 local status
1701 local grepstr
1702 local s
1703 case `uname` in
1704 FreeBSD) grepstr="Cannot open.*$plugin" ;;
1705 *) grepstr="$plugin.*No such file" ;;
1706 esac
1707
1708 s=$(ceph osd erasure-code-profile set TESTPROFILE plugin=$plugin 2>&1)
1709 local status=$?
1710 if [ $status -eq 0 ]; then
1711 ceph osd erasure-code-profile rm TESTPROFILE
1712 elif ! echo $s | grep --quiet "$grepstr" ; then
1713 status=1
1714 # display why the string was rejected.
1715 echo $s
1716 fi
1717 return $status
1718}
1719
1720function test_erasure_code_plugin_exists() {
1721 local dir=$1
1722
1723 setup $dir || return 1
1724 run_mon $dir a || return 1
1725 run_mgr $dir x || return 1
1726 erasure_code_plugin_exists jerasure || return 1
1727 ! erasure_code_plugin_exists FAKE || return 1
1728 teardown $dir || return 1
1729}
1730
1731#######################################################################
1732
1733##
1734# Display all log files from **dir** on stdout.
1735#
1736# @param dir directory in which all data is stored
1737#
1738
1739function display_logs() {
1740 local dir=$1
1741
1742 find $dir -maxdepth 1 -name '*.log' | \
1743 while read file ; do
1744 echo "======================= $file"
1745 cat $file
1746 done
1747}
1748
1749function test_display_logs() {
1750 local dir=$1
1751
1752 setup $dir || return 1
1753 run_mon $dir a || return 1
1754 kill_daemons $dir || return 1
1755 display_logs $dir > $dir/log.out
1756 grep --quiet mon.a.log $dir/log.out || return 1
1757 teardown $dir || return 1
1758}
1759
1760#######################################################################
1761##
1762# Spawn a command in background and save the pid in the variable name
1763# passed in argument. To make the output reading easier, the output is
1764# prepend with the process id.
1765#
1766# Example:
1767# pids1=""
1768# run_in_background pids1 bash -c 'sleep 1; exit 1'
1769#
1770# @param pid_variable the variable name (not value) where the pids will be stored
1771# @param ... the command to execute
1772# @return only the pid_variable output should be considered and used with **wait_background**
1773#
1774function run_in_background() {
1775 local pid_variable=$1
94b18763 1776 shift
7c673cae 1777 # Execute the command and prepend the output with its pid
f64942e4 1778 # We enforce to return the exit status of the command and not the sed one.
94b18763 1779 ("$@" |& sed 's/^/'$$': /'; return "${PIPESTATUS[0]}") >&2 &
7c673cae
FG
1780 eval "$pid_variable+=\" $!\""
1781}
1782
94b18763
FG
1783function save_stdout {
1784 local out="$1"
1785 shift
1786 "$@" > "$out"
1787}
1788
7c673cae
FG
1789function test_run_in_background() {
1790 local pids
1791 run_in_background pids sleep 1
1792 run_in_background pids sleep 1
1793 test $(echo $pids | wc -w) = 2 || return 1
1794 wait $pids || return 1
1795}
1796
1797#######################################################################
1798##
1799# Wait for pids running in background to complete.
1800# This function is usually used after a **run_in_background** call
1801# Example:
1802# pids1=""
1803# run_in_background pids1 bash -c 'sleep 1; exit 1'
1804# wait_background pids1
1805#
1806# @param pids The variable name that contains the active PIDS. Set as empty at then end of the function.
1807# @return returns 1 if at least one process exits in error unless returns 0
1808#
1809function wait_background() {
1810 # We extract the PIDS from the variable name
1811 pids=${!1}
1812
1813 return_code=0
1814 for pid in $pids; do
1815 if ! wait $pid; then
1816 # If one process failed then return 1
1817 return_code=1
1818 fi
1819 done
1820
1821 # We empty the variable reporting that all process ended
1822 eval "$1=''"
1823
1824 return $return_code
1825}
1826
1827
1828function test_wait_background() {
1829 local pids=""
1830 run_in_background pids bash -c "sleep 1; exit 1"
1831 run_in_background pids bash -c "sleep 2; exit 0"
1832 wait_background pids
1833 if [ $? -ne 1 ]; then return 1; fi
1834
1835 run_in_background pids bash -c "sleep 1; exit 0"
1836 run_in_background pids bash -c "sleep 2; exit 0"
1837 wait_background pids
1838 if [ $? -ne 0 ]; then return 1; fi
1839
1840 if [ ! -z "$pids" ]; then return 1; fi
1841}
1842
31f18b77
FG
1843function flush_pg_stats()
1844{
1845 local timeout=${1:-$TIMEOUT}
1846
1847 ids=`ceph osd ls`
1848 seqs=''
1849 for osd in $ids; do
1850 seq=`ceph tell osd.$osd flush_pg_stats`
1851 seqs="$seqs $osd-$seq"
1852 done
1853
1854 for s in $seqs; do
1855 osd=`echo $s | cut -d - -f 1`
1856 seq=`echo $s | cut -d - -f 2`
1857 echo "waiting osd.$osd seq $seq"
1858 while test $(ceph osd last-stat-seq $osd) -lt $seq; do
1859 sleep 1
1860 if [ $((timeout--)) -eq 0 ]; then
1861 return 1
1862 fi
1863 done
1864 done
1865}
1866
1867function test_flush_pg_stats()
1868{
1869 local dir=$1
1870
1871 setup $dir || return 1
1872 run_mon $dir a --osd_pool_default_size=1 || return 1
1873 run_mgr $dir x || return 1
1874 run_osd $dir 0 || return 1
c07f9fc5 1875 create_rbd_pool || return 1
31f18b77 1876 rados -p rbd put obj /etc/group
3a9019d9 1877 flush_pg_stats || return 1
31f18b77
FG
1878 local jq_filter='.pools | .[] | select(.name == "rbd") | .stats'
1879 raw_bytes_used=`ceph df detail --format=json | jq "$jq_filter.raw_bytes_used"`
1880 bytes_used=`ceph df detail --format=json | jq "$jq_filter.bytes_used"`
1881 test $raw_bytes_used > 0 || return 1
1882 test $raw_bytes_used == $bytes_used || return 1
b5b8bbf5 1883 teardown $dir
31f18b77
FG
1884}
1885
7c673cae
FG
1886#######################################################################
1887
1888##
1889# Call the **run** function (which must be defined by the caller) with
1890# the **dir** argument followed by the caller argument list.
1891#
1892# If the **run** function returns on error, all logs found in **dir**
1893# are displayed for diagnostic purposes.
1894#
1895# **teardown** function is called when the **run** function returns
1896# (on success or on error), to cleanup leftovers. The CEPH_CONF is set
1897# to /dev/null and CEPH_ARGS is unset so that the tests are protected from
1898# external interferences.
1899#
1900# It is the responsibility of the **run** function to call the
1901# **setup** function to prepare the test environment (create a temporary
1902# directory etc.).
1903#
1904# The shell is required (via PS4) to display the function and line
1905# number whenever a statement is executed to help debugging.
1906#
1907# @param dir directory in which all data is stored
1908# @param ... arguments passed transparently to **run**
1909# @return 0 on success, 1 on error
1910#
1911function main() {
1912 local dir=td/$1
1913 shift
1914
1915 shopt -s -o xtrace
1916 PS4='${BASH_SOURCE[0]}:$LINENO: ${FUNCNAME[0]}: '
1917
1918 export PATH=${CEPH_BUILD_VIRTUALENV}/ceph-disk-virtualenv/bin:${CEPH_BUILD_VIRTUALENV}/ceph-detect-init-virtualenv/bin:.:$PATH # make sure program from sources are preferred
1919 #export PATH=$CEPH_ROOT/src/ceph-disk/virtualenv/bin:$CEPH_ROOT/src/ceph-detect-init/virtualenv/bin:.:$PATH # make sure program from sources are preferred
1920
1921 export CEPH_CONF=/dev/null
1922 unset CEPH_ARGS
1923
1924 local code
1925 if run $dir "$@" ; then
1926 code=0
1927 else
7c673cae
FG
1928 code=1
1929 fi
b5b8bbf5 1930 teardown $dir $code || return 1
7c673cae
FG
1931 return $code
1932}
1933
1934#######################################################################
1935
1936function run_tests() {
1937 shopt -s -o xtrace
1938 PS4='${BASH_SOURCE[0]}:$LINENO: ${FUNCNAME[0]}: '
1939
1940 export PATH=${CEPH_BUILD_VIRTUALENV}/ceph-disk-virtualenv/bin:${CEPH_BUILD_VIRTUALENV}/ceph-detect-init-virtualenv/bin:.:$PATH # make sure program from sources are preferred
1941 #export PATH=$CEPH_ROOT/src/ceph-disk/virtualenv/bin:$CEPH_ROOT/src/ceph-detect-init/virtualenv/bin:.:$PATH # make sure program from sources are preferred
1942
1943 export CEPH_MON="127.0.0.1:7109" # git grep '\<7109\>' : there must be only one
1944 export CEPH_ARGS
b5b8bbf5 1945 CEPH_ARGS+=" --fsid=$(uuidgen) --auth-supported=none "
7c673cae
FG
1946 CEPH_ARGS+="--mon-host=$CEPH_MON "
1947 export CEPH_CONF=/dev/null
1948
1949 local funcs=${@:-$(set | sed -n -e 's/^\(test_[0-9a-z_]*\) .*/\1/p')}
1950 local dir=td/ceph-helpers
1951
1952 for func in $funcs ; do
b5b8bbf5
FG
1953 if ! $func $dir; then
1954 teardown $dir 1
1955 return 1
1956 fi
7c673cae
FG
1957 done
1958}
1959
1960if test "$1" = TESTS ; then
1961 shift
1962 run_tests "$@"
b5b8bbf5 1963 exit $?
7c673cae
FG
1964fi
1965
224ce89b
WB
1966# NOTE:
1967# jq only support --exit-status|-e from version 1.4 forwards, which makes
1968# returning on error waaaay prettier and straightforward.
1969# However, the current automated upstream build is running with v1.3,
1970# which has no idea what -e is. Hence the convoluted error checking we
1971# need. Sad.
1972# The next time someone changes this code, please check if v1.4 is now
1973# a thing, and, if so, please change these to use -e. Thanks.
1974
1975# jq '.all.supported | select([.[] == "foo"] | any)'
1976function jq_success() {
1977 input="$1"
1978 filter="$2"
1979 expects="\"$3\""
1980
1981 in_escaped=$(printf %s "$input" | sed "s/'/'\\\\''/g")
1982 filter_escaped=$(printf %s "$filter" | sed "s/'/'\\\\''/g")
1983
1984 ret=$(echo "$in_escaped" | jq "$filter_escaped")
1985 if [[ "$ret" == "true" ]]; then
1986 return 0
1987 elif [[ -n "$expects" ]]; then
1988 if [[ "$ret" == "$expects" ]]; then
1989 return 0
1990 fi
1991 fi
1992 return 1
1993 input=$1
1994 filter=$2
1995 expects="$3"
1996
1997 ret="$(echo $input | jq \"$filter\")"
1998 if [[ "$ret" == "true" ]]; then
1999 return 0
2000 elif [[ -n "$expects" && "$ret" == "$expects" ]]; then
2001 return 0
2002 fi
2003 return 1
2004}
2005
b5b8bbf5
FG
2006function inject_eio() {
2007 local pooltype=$1
2008 shift
2009 local which=$1
2010 shift
2011 local poolname=$1
2012 shift
2013 local objname=$1
2014 shift
2015 local dir=$1
2016 shift
2017 local shard_id=$1
2018 shift
2019
2020 local -a initial_osds=($(get_osds $poolname $objname))
2021 local osd_id=${initial_osds[$shard_id]}
2022 if [ "$pooltype" != "ec" ]; then
2023 shard_id=""
2024 fi
2025 set_config osd $osd_id filestore_debug_inject_read_err true || return 1
2026 local loop=0
2027 while ( CEPH_ARGS='' ceph --admin-daemon $(get_asok_path osd.$osd_id) \
2028 inject${which}err $poolname $objname $shard_id | grep -q Invalid ); do
2029 loop=$(expr $loop + 1)
2030 if [ $loop = "10" ]; then
2031 return 1
2032 fi
2033 sleep 1
2034 done
2035}
2036
1adf2230
AA
2037function multidiff() {
2038 if ! diff $@ ; then
2039 if [ "$DIFFCOLOPTS" = "" ]; then
2040 return 1
2041 fi
2042 diff $DIFFCOLOPTS $@
2043 fi
2044}
2045
7c673cae 2046# Local Variables:
c07f9fc5 2047# compile-command: "cd ../../src ; make -j4 && ../qa/standalone/ceph-helpers.sh TESTS # test_get_config"
7c673cae 2048# End: