]> git.proxmox.com Git - ceph.git/blame - ceph/qa/standalone/ceph-helpers.sh
bump version to 12.2.11-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
f64942e4
AA
22TMPDIR=${TMPDIR:-/tmp}
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
392#######################################################################
393
394##
395# Run a monitor by the name mon.**id** with data in **dir**/**id**.
396# The logs can be found in **dir**/mon.**id**.log and the pid file
397# is **dir**/mon.**id**.pid and the admin socket is
398# **dir**/**id**/ceph-mon.**id**.asok.
399#
400# The remaining arguments are passed verbatim to ceph-mon --mkfs
401# and the ceph-mon daemon.
402#
403# Two mandatory arguments must be provided: --fsid and --mon-host
404# Instead of adding them to every call to run_mon, they can be
405# set in the CEPH_ARGS environment variable to be read implicitly
406# by every ceph command.
407#
408# The CEPH_CONF variable is expected to be set to /dev/null to
409# only rely on arguments for configuration.
410#
411# Examples:
412#
413# CEPH_ARGS="--fsid=$(uuidgen) "
414# CEPH_ARGS+="--mon-host=127.0.0.1:7018 "
415# run_mon $dir a # spawn a mon and bind port 7018
416# run_mon $dir a --debug-filestore=20 # spawn with filestore debugging
417#
418# If mon_initial_members is not set, the default rbd pool is deleted
419# and replaced with a replicated pool with less placement groups to
420# speed up initialization. If mon_initial_members is set, no attempt
421# is made to recreate the rbd pool because it would hang forever,
422# waiting for other mons to join.
423#
424# A **dir**/ceph.conf file is created but not meant to be used by any
425# function. It is convenient for debugging a failure with:
426#
427# ceph --conf **dir**/ceph.conf -s
428#
429# @param dir path name of the environment
430# @param id mon identifier
431# @param ... can be any option valid for ceph-mon
432# @return 0 on success, 1 on error
433#
c07f9fc5 434function run_mon() {
7c673cae
FG
435 local dir=$1
436 shift
437 local id=$1
438 shift
439 local data=$dir/$id
440
441 ceph-mon \
442 --id $id \
443 --mkfs \
444 --mon-data=$data \
445 --run-dir=$dir \
446 "$@" || return 1
447
448 ceph-mon \
449 --id $id \
450 --mon-osd-full-ratio=.99 \
451 --mon-data-avail-crit=1 \
b5b8bbf5 452 --mon-data-avail-warn=5 \
7c673cae
FG
453 --paxos-propose-interval=0.1 \
454 --osd-crush-chooseleaf-type=0 \
c07f9fc5 455 $EXTRA_OPTS \
7c673cae
FG
456 --debug-mon 20 \
457 --debug-ms 20 \
458 --debug-paxos 20 \
459 --chdir= \
460 --mon-data=$data \
461 --log-file=$dir/\$name.log \
c07f9fc5 462 --admin-socket=$(get_asok_path) \
7c673cae
FG
463 --mon-cluster-log-file=$dir/log \
464 --run-dir=$dir \
465 --pid-file=$dir/\$name.pid \
466 --mon-allow-pool-delete \
c07f9fc5 467 --mon-osd-backfillfull-ratio .99 \
7c673cae
FG
468 "$@" || return 1
469
470 cat > $dir/ceph.conf <<EOF
471[global]
472fsid = $(get_config mon $id fsid)
473mon host = $(get_config mon $id mon_host)
474EOF
224ce89b
WB
475}
476
7c673cae
FG
477function test_run_mon() {
478 local dir=$1
479
480 setup $dir || return 1
481
482 run_mon $dir a --mon-initial-members=a || return 1
c07f9fc5 483 create_rbd_pool || return 1
7c673cae 484 # rbd has not been deleted / created, hence it has pool id 0
224ce89b 485 ceph osd dump | grep "pool 1 'rbd'" || return 1
7c673cae
FG
486 kill_daemons $dir || return 1
487
488 run_mon $dir a || return 1
c07f9fc5 489 create_rbd_pool || return 1
7c673cae 490 # rbd has been deleted / created, hence it does not have pool id 0
224ce89b 491 ! ceph osd dump | grep "pool 1 'rbd'" || return 1
c07f9fc5 492 local size=$(CEPH_ARGS='' ceph --format=json daemon $(get_asok_path mon.a) \
7c673cae
FG
493 config get osd_pool_default_size)
494 test "$size" = '{"osd_pool_default_size":"3"}' || return 1
495
496 ! CEPH_ARGS='' ceph status || return 1
497 CEPH_ARGS='' ceph --conf $dir/ceph.conf status || return 1
498
499 kill_daemons $dir || return 1
500
501 run_mon $dir a --osd_pool_default_size=1 || return 1
c07f9fc5 502 local size=$(CEPH_ARGS='' ceph --format=json daemon $(get_asok_path mon.a) \
7c673cae
FG
503 config get osd_pool_default_size)
504 test "$size" = '{"osd_pool_default_size":"1"}' || return 1
505 kill_daemons $dir || return 1
506
507 CEPH_ARGS="$CEPH_ARGS --osd_pool_default_size=2" \
508 run_mon $dir a || return 1
c07f9fc5 509 local size=$(CEPH_ARGS='' ceph --format=json daemon $(get_asok_path mon.a) \
7c673cae
FG
510 config get osd_pool_default_size)
511 test "$size" = '{"osd_pool_default_size":"2"}' || return 1
512 kill_daemons $dir || return 1
513
514 teardown $dir || return 1
515}
516
c07f9fc5
FG
517function create_rbd_pool() {
518 ceph osd pool delete rbd rbd --yes-i-really-really-mean-it || return 1
b5b8bbf5 519 create_pool rbd $PG_NUM || return 1
c07f9fc5
FG
520 rbd pool init rbd
521}
522
b5b8bbf5
FG
523function create_pool() {
524 ceph osd pool create "$@"
525 sleep 1
526}
527
28e407b8
AA
528function delete_pool() {
529 local poolname=$1
530 ceph osd pool delete $poolname $poolname --yes-i-really-really-mean-it
531}
532
7c673cae
FG
533#######################################################################
534
535function run_mgr() {
536 local dir=$1
537 shift
538 local id=$1
539 shift
540 local data=$dir/$id
541
542 ceph-mgr \
543 --id $id \
c07f9fc5 544 $EXTRA_OPTS \
7c673cae
FG
545 --debug-mgr 20 \
546 --debug-objecter 20 \
547 --debug-ms 20 \
548 --debug-paxos 20 \
549 --chdir= \
550 --mgr-data=$data \
551 --log-file=$dir/\$name.log \
c07f9fc5 552 --admin-socket=$(get_asok_path) \
7c673cae
FG
553 --run-dir=$dir \
554 --pid-file=$dir/\$name.pid \
555 "$@" || return 1
556}
557
558#######################################################################
559
560##
561# Create (prepare) and run (activate) an osd by the name osd.**id**
562# with data in **dir**/**id**. The logs can be found in
563# **dir**/osd.**id**.log, the pid file is **dir**/osd.**id**.pid and
564# the admin socket is **dir**/**id**/ceph-osd.**id**.asok.
565#
566# The remaining arguments are passed verbatim to ceph-osd.
567#
568# Two mandatory arguments must be provided: --fsid and --mon-host
569# Instead of adding them to every call to run_osd, they can be
570# set in the CEPH_ARGS environment variable to be read implicitly
571# by every ceph command.
572#
573# The CEPH_CONF variable is expected to be set to /dev/null to
574# only rely on arguments for configuration.
575#
576# The run_osd function creates the OSD data directory with ceph-disk
577# prepare on the **dir**/**id** directory and relies on the
578# activate_osd function to run the daemon.
579#
580# Examples:
581#
582# CEPH_ARGS="--fsid=$(uuidgen) "
583# CEPH_ARGS+="--mon-host=127.0.0.1:7018 "
584# run_osd $dir 0 # prepare and activate an osd using the monitor listening on 7018
585#
586# @param dir path name of the environment
587# @param id osd identifier
588# @param ... can be any option valid for ceph-osd
589# @return 0 on success, 1 on error
590#
591function run_osd() {
592 local dir=$1
593 shift
594 local id=$1
595 shift
596 local osd_data=$dir/$id
597
598 local ceph_disk_args
599 ceph_disk_args+=" --statedir=$dir"
600 ceph_disk_args+=" --sysconfdir=$dir"
601 ceph_disk_args+=" --prepend-to-path="
602
603 mkdir -p $osd_data
604 ceph-disk $ceph_disk_args \
31f18b77 605 prepare --filestore $osd_data || return 1
7c673cae
FG
606
607 activate_osd $dir $id "$@"
608}
609
610function run_osd_bluestore() {
611 local dir=$1
612 shift
613 local id=$1
614 shift
615 local osd_data=$dir/$id
616
617 local ceph_disk_args
618 ceph_disk_args+=" --statedir=$dir"
619 ceph_disk_args+=" --sysconfdir=$dir"
620 ceph_disk_args+=" --prepend-to-path="
621
622 mkdir -p $osd_data
623 ceph-disk $ceph_disk_args \
624 prepare --bluestore $osd_data || return 1
625
31f18b77 626 activate_osd $dir $id "$@"
7c673cae
FG
627}
628
629function test_run_osd() {
630 local dir=$1
631
632 setup $dir || return 1
633
634 run_mon $dir a || return 1
635 run_mgr $dir x || return 1
636
637 run_osd $dir 0 || return 1
c07f9fc5 638 local backfills=$(CEPH_ARGS='' ceph --format=json daemon $(get_asok_path osd.0) \
7c673cae
FG
639 config get osd_max_backfills)
640 echo "$backfills" | grep --quiet 'osd_max_backfills' || return 1
641
642 run_osd $dir 1 --osd-max-backfills 20 || return 1
c07f9fc5 643 local backfills=$(CEPH_ARGS='' ceph --format=json daemon $(get_asok_path osd.1) \
7c673cae
FG
644 config get osd_max_backfills)
645 test "$backfills" = '{"osd_max_backfills":"20"}' || return 1
646
647 CEPH_ARGS="$CEPH_ARGS --osd-max-backfills 30" run_osd $dir 2 || return 1
c07f9fc5 648 local backfills=$(CEPH_ARGS='' ceph --format=json daemon $(get_asok_path osd.2) \
7c673cae
FG
649 config get osd_max_backfills)
650 test "$backfills" = '{"osd_max_backfills":"30"}' || return 1
651
652 teardown $dir || return 1
653}
654
655#######################################################################
656
657##
658# Shutdown and remove all traces of the osd by the name osd.**id**.
659#
660# The OSD is shutdown with the TERM signal. It is then removed from
661# the auth list, crush map, osd map etc and the files associated with
662# it are also removed.
663#
664# @param dir path name of the environment
665# @param id osd identifier
666# @return 0 on success, 1 on error
667#
668function destroy_osd() {
669 local dir=$1
670 local id=$2
671
7c673cae 672 ceph osd out osd.$id || return 1
c07f9fc5
FG
673 kill_daemons $dir TERM osd.$id || return 1
674 ceph osd purge osd.$id --yes-i-really-mean-it || return 1
7c673cae
FG
675 teardown $dir/$id || return 1
676 rm -fr $dir/$id
677}
678
679function test_destroy_osd() {
680 local dir=$1
681
682 setup $dir || return 1
683 run_mon $dir a || return 1
684 run_mgr $dir x || return 1
685 run_osd $dir 0 || return 1
686 destroy_osd $dir 0 || return 1
687 ! ceph osd dump | grep "osd.$id " || return 1
688 teardown $dir || return 1
689}
690
691#######################################################################
692
693##
694# Run (activate) an osd by the name osd.**id** with data in
695# **dir**/**id**. The logs can be found in **dir**/osd.**id**.log,
696# the pid file is **dir**/osd.**id**.pid and the admin socket is
697# **dir**/**id**/ceph-osd.**id**.asok.
698#
699# The remaining arguments are passed verbatim to ceph-osd.
700#
701# Two mandatory arguments must be provided: --fsid and --mon-host
702# Instead of adding them to every call to activate_osd, they can be
703# set in the CEPH_ARGS environment variable to be read implicitly
704# by every ceph command.
705#
706# The CEPH_CONF variable is expected to be set to /dev/null to
707# only rely on arguments for configuration.
708#
709# The activate_osd function expects a valid OSD data directory
710# in **dir**/**id**, either just created via run_osd or re-using
711# one left by a previous run of ceph-osd. The ceph-osd daemon is
712# run indirectly via ceph-disk activate.
713#
714# The activate_osd function blocks until the monitor reports the osd
715# up. If it fails to do so within $TIMEOUT seconds, activate_osd
716# fails.
717#
718# Examples:
719#
720# CEPH_ARGS="--fsid=$(uuidgen) "
721# CEPH_ARGS+="--mon-host=127.0.0.1:7018 "
722# activate_osd $dir 0 # activate an osd using the monitor listening on 7018
723#
724# @param dir path name of the environment
725# @param id osd identifier
726# @param ... can be any option valid for ceph-osd
727# @return 0 on success, 1 on error
728#
729function activate_osd() {
730 local dir=$1
731 shift
732 local id=$1
733 shift
734 local osd_data=$dir/$id
735
736 local ceph_disk_args
737 ceph_disk_args+=" --statedir=$dir"
738 ceph_disk_args+=" --sysconfdir=$dir"
739 ceph_disk_args+=" --prepend-to-path="
740
741 local ceph_args="$CEPH_ARGS"
7c673cae
FG
742 ceph_args+=" --osd-failsafe-full-ratio=.99"
743 ceph_args+=" --osd-journal-size=100"
744 ceph_args+=" --osd-scrub-load-threshold=2000"
745 ceph_args+=" --osd-data=$osd_data"
746 ceph_args+=" --chdir="
c07f9fc5 747 ceph_args+=$EXTRA_OPTS
7c673cae 748 ceph_args+=" --run-dir=$dir"
c07f9fc5 749 ceph_args+=" --admin-socket=$(get_asok_path)"
7c673cae
FG
750 ceph_args+=" --debug-osd=20"
751 ceph_args+=" --log-file=$dir/\$name.log"
752 ceph_args+=" --pid-file=$dir/\$name.pid"
753 ceph_args+=" --osd-max-object-name-len 460"
754 ceph_args+=" --osd-max-object-namespace-len 64"
224ce89b 755 ceph_args+=" --enable-experimental-unrecoverable-data-corrupting-features *"
7c673cae
FG
756 ceph_args+=" "
757 ceph_args+="$@"
758 mkdir -p $osd_data
759 CEPH_ARGS="$ceph_args " ceph-disk $ceph_disk_args \
760 activate \
761 --mark-init=none \
762 $osd_data || return 1
763
764 [ "$id" = "$(cat $osd_data/whoami)" ] || return 1
765
766 wait_for_osd up $id || return 1
767}
768
769function test_activate_osd() {
770 local dir=$1
771
772 setup $dir || return 1
773
774 run_mon $dir a || return 1
775 run_mgr $dir x || return 1
776
777 run_osd $dir 0 || return 1
c07f9fc5 778 local backfills=$(CEPH_ARGS='' ceph --format=json daemon $(get_asok_path osd.0) \
7c673cae
FG
779 config get osd_max_backfills)
780 echo "$backfills" | grep --quiet 'osd_max_backfills' || return 1
781
782 kill_daemons $dir TERM osd || return 1
783
784 activate_osd $dir 0 --osd-max-backfills 20 || return 1
c07f9fc5 785 local backfills=$(CEPH_ARGS='' ceph --format=json daemon $(get_asok_path osd.0) \
7c673cae
FG
786 config get osd_max_backfills)
787 test "$backfills" = '{"osd_max_backfills":"20"}' || return 1
788
789 teardown $dir || return 1
790}
791
792#######################################################################
793
794##
795# Wait until the OSD **id** is either up or down, as specified by
796# **state**. It fails after $TIMEOUT seconds.
797#
798# @param state either up or down
799# @param id osd identifier
800# @return 0 on success, 1 on error
801#
802function wait_for_osd() {
803 local state=$1
804 local id=$2
805
806 status=1
807 for ((i=0; i < $TIMEOUT; i++)); do
808 echo $i
809 if ! ceph osd dump | grep "osd.$id $state"; then
810 sleep 1
811 else
812 status=0
813 break
814 fi
815 done
816 return $status
817}
818
819function test_wait_for_osd() {
820 local dir=$1
821 setup $dir || return 1
822 run_mon $dir a --osd_pool_default_size=1 || return 1
823 run_mgr $dir x || return 1
824 run_osd $dir 0 || return 1
825 wait_for_osd up 0 || return 1
826 kill_daemons $dir TERM osd || return 1
827 wait_for_osd down 0 || return 1
828 ( TIMEOUT=1 ; ! wait_for_osd up 0 ) || return 1
829 teardown $dir || return 1
830}
831
832#######################################################################
833
834##
835# Display the list of OSD ids supporting the **objectname** stored in
836# **poolname**, as reported by ceph osd map.
837#
838# @param poolname an existing pool
839# @param objectname an objectname (may or may not exist)
840# @param STDOUT white space separated list of OSD ids
841# @return 0 on success, 1 on error
842#
843function get_osds() {
844 local poolname=$1
845 local objectname=$2
846
31f18b77
FG
847 local osds=$(ceph --format json osd map $poolname $objectname 2>/dev/null | \
848 jq '.acting | .[]')
7c673cae
FG
849 # get rid of the trailing space
850 echo $osds
851}
852
853function test_get_osds() {
854 local dir=$1
855
856 setup $dir || return 1
857 run_mon $dir a --osd_pool_default_size=2 || return 1
858 run_mgr $dir x || return 1
859 run_osd $dir 0 || return 1
860 run_osd $dir 1 || return 1
c07f9fc5 861 create_rbd_pool || return 1
7c673cae 862 wait_for_clean || return 1
c07f9fc5 863 create_rbd_pool || return 1
7c673cae
FG
864 get_osds rbd GROUP | grep --quiet '^[0-1] [0-1]$' || return 1
865 teardown $dir || return 1
866}
867
868#######################################################################
869
870##
871# Wait for the monitor to form quorum (optionally, of size N)
872#
873# @param timeout duration (lower-bound) to wait for quorum to be formed
874# @param quorumsize size of quorum to wait for
875# @return 0 on success, 1 on error
876#
877function wait_for_quorum() {
878 local timeout=$1
879 local quorumsize=$2
880
881 if [[ -z "$timeout" ]]; then
882 timeout=300
883 fi
884
885 if [[ -z "$quorumsize" ]]; then
886 timeout $timeout ceph mon_status --format=json >&/dev/null || return 1
887 return 0
888 fi
889
890 no_quorum=1
c07f9fc5 891 wait_until=$((`date +%s` + $timeout))
7c673cae
FG
892 while [[ $(date +%s) -lt $wait_until ]]; do
893 jqfilter='.quorum | length == '$quorumsize
894 jqinput="$(timeout $timeout ceph mon_status --format=json 2>/dev/null)"
895 res=$(echo $jqinput | jq "$jqfilter")
896 if [[ "$res" == "true" ]]; then
897 no_quorum=0
898 break
899 fi
900 done
901 return $no_quorum
902}
903
904#######################################################################
905
906##
907# Return the PG of supporting the **objectname** stored in
908# **poolname**, as reported by ceph osd map.
909#
910# @param poolname an existing pool
911# @param objectname an objectname (may or may not exist)
912# @param STDOUT a PG
913# @return 0 on success, 1 on error
914#
915function get_pg() {
916 local poolname=$1
917 local objectname=$2
918
31f18b77 919 ceph --format json osd map $poolname $objectname 2>/dev/null | jq -r '.pgid'
7c673cae
FG
920}
921
922function test_get_pg() {
923 local dir=$1
924
925 setup $dir || return 1
926 run_mon $dir a --osd_pool_default_size=1 || return 1
927 run_mgr $dir x || return 1
928 run_osd $dir 0 || return 1
c07f9fc5 929 create_rbd_pool || return 1
7c673cae
FG
930 wait_for_clean || return 1
931 get_pg rbd GROUP | grep --quiet '^[0-9]\.[0-9a-f][0-9a-f]*$' || return 1
932 teardown $dir || return 1
933}
934
935#######################################################################
936
937##
938# Return the value of the **config**, obtained via the config get command
939# of the admin socket of **daemon**.**id**.
940#
941# @param daemon mon or osd
942# @param id mon or osd ID
943# @param config the configuration variable name as found in config_opts.h
944# @param STDOUT the config value
945# @return 0 on success, 1 on error
946#
947function get_config() {
948 local daemon=$1
949 local id=$2
950 local config=$3
951
952 CEPH_ARGS='' \
c07f9fc5 953 ceph --format json daemon $(get_asok_path $daemon.$id) \
7c673cae 954 config get $config 2> /dev/null | \
31f18b77 955 jq -r ".$config"
7c673cae
FG
956}
957
958function test_get_config() {
959 local dir=$1
960
961 # override the default config using command line arg and check it
962 setup $dir || return 1
963 run_mon $dir a --osd_pool_default_size=1 || return 1
964 test $(get_config mon a osd_pool_default_size) = 1 || return 1
965 run_mgr $dir x || return 1
966 run_osd $dir 0 --osd_max_scrubs=3 || return 1
967 test $(get_config osd 0 osd_max_scrubs) = 3 || return 1
968 teardown $dir || return 1
969}
970
971#######################################################################
972
973##
974# Set the **config** to specified **value**, via the config set command
975# of the admin socket of **daemon**.**id**
976#
977# @param daemon mon or osd
978# @param id mon or osd ID
979# @param config the configuration variable name as found in config_opts.h
980# @param value the config value
981# @return 0 on success, 1 on error
982#
983function set_config() {
984 local daemon=$1
985 local id=$2
986 local config=$3
987 local value=$4
988
c07f9fc5 989 test $(env CEPH_ARGS='' ceph --format json daemon $(get_asok_path $daemon.$id) \
31f18b77
FG
990 config set $config $value 2> /dev/null | \
991 jq 'has("success")') == true
7c673cae
FG
992}
993
994function test_set_config() {
995 local dir=$1
996
997 setup $dir || return 1
998 run_mon $dir a --osd_pool_default_size=1 || return 1
999 test $(get_config mon a ms_crc_header) = true || return 1
1000 set_config mon a ms_crc_header false || return 1
1001 test $(get_config mon a ms_crc_header) = false || return 1
1002 set_config mon a ms_crc_header true || return 1
1003 test $(get_config mon a ms_crc_header) = true || return 1
1004 teardown $dir || return 1
1005}
1006
1007#######################################################################
1008
1009##
1010# Return the OSD id of the primary OSD supporting the **objectname**
1011# stored in **poolname**, as reported by ceph osd map.
1012#
1013# @param poolname an existing pool
1014# @param objectname an objectname (may or may not exist)
1015# @param STDOUT the primary OSD id
1016# @return 0 on success, 1 on error
1017#
1018function get_primary() {
1019 local poolname=$1
1020 local objectname=$2
1021
31f18b77
FG
1022 ceph --format json osd map $poolname $objectname 2>/dev/null | \
1023 jq '.acting_primary'
7c673cae
FG
1024}
1025
1026function test_get_primary() {
1027 local dir=$1
1028
1029 setup $dir || return 1
1030 run_mon $dir a --osd_pool_default_size=1 || return 1
1031 local osd=0
1032 run_mgr $dir x || return 1
1033 run_osd $dir $osd || return 1
c07f9fc5 1034 create_rbd_pool || return 1
7c673cae
FG
1035 wait_for_clean || return 1
1036 test $(get_primary rbd GROUP) = $osd || return 1
1037 teardown $dir || return 1
1038}
1039
1040#######################################################################
1041
1042##
1043# Return the id of any OSD supporting the **objectname** stored in
1044# **poolname**, as reported by ceph osd map, except the primary.
1045#
1046# @param poolname an existing pool
1047# @param objectname an objectname (may or may not exist)
1048# @param STDOUT the OSD id
1049# @return 0 on success, 1 on error
1050#
1051function get_not_primary() {
1052 local poolname=$1
1053 local objectname=$2
1054
1055 local primary=$(get_primary $poolname $objectname)
31f18b77
FG
1056 ceph --format json osd map $poolname $objectname 2>/dev/null | \
1057 jq ".acting | map(select (. != $primary)) | .[0]"
7c673cae
FG
1058}
1059
1060function test_get_not_primary() {
1061 local dir=$1
1062
1063 setup $dir || return 1
1064 run_mon $dir a --osd_pool_default_size=2 || return 1
1065 run_mgr $dir x || return 1
1066 run_osd $dir 0 || return 1
1067 run_osd $dir 1 || return 1
c07f9fc5 1068 create_rbd_pool || return 1
7c673cae
FG
1069 wait_for_clean || return 1
1070 local primary=$(get_primary rbd GROUP)
1071 local not_primary=$(get_not_primary rbd GROUP)
1072 test $not_primary != $primary || return 1
1073 test $not_primary = 0 -o $not_primary = 1 || return 1
1074 teardown $dir || return 1
1075}
1076
1077#######################################################################
1078
1079##
1080# Run ceph-objectstore-tool against the OSD **id** using the data path
1081# **dir**. The OSD is killed with TERM prior to running
1082# ceph-objectstore-tool because access to the data path is
1083# exclusive. The OSD is restarted after the command completes. The
1084# objectstore_tool returns after all PG are active+clean again.
1085#
1086# @param dir the data path of the OSD
1087# @param id the OSD id
1088# @param ... arguments to ceph-objectstore-tool
1089# @param STDIN the input of ceph-objectstore-tool
1090# @param STDOUT the output of ceph-objectstore-tool
1091# @return 0 on success, 1 on error
1092#
1093# The value of $ceph_osd_args will be passed to restarted osds
1094#
1095function objectstore_tool() {
1096 local dir=$1
1097 shift
1098 local id=$1
1099 shift
1100 local osd_data=$dir/$id
1101
1102 local osd_type=$(cat $osd_data/type)
1103
1104 kill_daemons $dir TERM osd.$id >&2 < /dev/null || return 1
1105
1106 local journal_args
1107 if [ "$objectstore_type" == "filestore" ]; then
1108 journal_args=" --journal-path $osd_data/journal"
1109 fi
1110 ceph-objectstore-tool \
7c673cae
FG
1111 --data-path $osd_data \
1112 $journal_args \
1113 "$@" || return 1
1114 activate_osd $dir $id $ceph_osd_args >&2 || return 1
1115 wait_for_clean >&2
1116}
1117
1118function test_objectstore_tool() {
1119 local dir=$1
1120
1121 setup $dir || return 1
1122 run_mon $dir a --osd_pool_default_size=1 || return 1
1123 local osd=0
1124 run_mgr $dir x || return 1
1125 run_osd $dir $osd || return 1
c07f9fc5 1126 create_rbd_pool || return 1
7c673cae
FG
1127 wait_for_clean || return 1
1128 rados --pool rbd put GROUP /etc/group || return 1
1129 objectstore_tool $dir $osd GROUP get-bytes | \
1130 diff - /etc/group
1131 ! objectstore_tool $dir $osd NOTEXISTS get-bytes || return 1
1132 teardown $dir || return 1
1133}
1134
1135#######################################################################
1136
1137##
1138# Predicate checking if there is an ongoing recovery in the
1139# cluster. If any of the recovering_{keys,bytes,objects}_per_sec
1140# counters are reported by ceph status, it means recovery is in
1141# progress.
1142#
1143# @return 0 if recovery in progress, 1 otherwise
1144#
1145function get_is_making_recovery_progress() {
31f18b77
FG
1146 local recovery_progress
1147 recovery_progress+=".recovering_keys_per_sec + "
1148 recovery_progress+=".recovering_bytes_per_sec + "
1149 recovery_progress+=".recovering_objects_per_sec"
1150 local progress=$(ceph --format json status 2>/dev/null | \
1151 jq -r ".pgmap | $recovery_progress")
1152 test "$progress" != null
7c673cae
FG
1153}
1154
1155function test_get_is_making_recovery_progress() {
1156 local dir=$1
1157
1158 setup $dir || return 1
1159 run_mon $dir a || return 1
1160 run_mgr $dir x || return 1
1161 ! get_is_making_recovery_progress || return 1
1162 teardown $dir || return 1
1163}
1164
1165#######################################################################
1166
1167##
1168# Return the number of active PGs in the cluster. A PG is active if
1169# ceph pg dump pgs reports it both **active** and **clean** and that
1170# not **stale**.
1171#
1172# @param STDOUT the number of active PGs
1173# @return 0 on success, 1 on error
1174#
1175function get_num_active_clean() {
31f18b77
FG
1176 local expression
1177 expression+="select(contains(\"active\") and contains(\"clean\")) | "
1178 expression+="select(contains(\"stale\") | not)"
1179 ceph --format json pg dump pgs 2>/dev/null | \
1180 jq "[.[] | .state | $expression] | length"
7c673cae
FG
1181}
1182
1183function test_get_num_active_clean() {
1184 local dir=$1
1185
1186 setup $dir || return 1
1187 run_mon $dir a --osd_pool_default_size=1 || return 1
1188 run_mgr $dir x || return 1
1189 run_osd $dir 0 || return 1
c07f9fc5 1190 create_rbd_pool || return 1
7c673cae
FG
1191 wait_for_clean || return 1
1192 local num_active_clean=$(get_num_active_clean)
1193 test "$num_active_clean" = $PG_NUM || return 1
1194 teardown $dir || return 1
1195}
1196
1197#######################################################################
1198
1199##
1200# Return the number of PGs in the cluster, according to
1201# ceph pg dump pgs.
1202#
1203# @param STDOUT the number of PGs
1204# @return 0 on success, 1 on error
1205#
1206function get_num_pgs() {
31f18b77 1207 ceph --format json status 2>/dev/null | jq '.pgmap.num_pgs'
7c673cae
FG
1208}
1209
1210function test_get_num_pgs() {
1211 local dir=$1
1212
1213 setup $dir || return 1
1214 run_mon $dir a --osd_pool_default_size=1 || return 1
1215 run_mgr $dir x || return 1
1216 run_osd $dir 0 || return 1
c07f9fc5 1217 create_rbd_pool || return 1
7c673cae
FG
1218 wait_for_clean || return 1
1219 local num_pgs=$(get_num_pgs)
1220 test "$num_pgs" -gt 0 || return 1
1221 teardown $dir || return 1
1222}
1223
1224#######################################################################
1225
c07f9fc5
FG
1226##
1227# Return the OSD ids in use by at least one PG in the cluster (either
1228# in the up or the acting set), according to ceph pg dump pgs. Every
1229# OSD id shows as many times as they are used in up and acting sets.
1230# If an OSD id is in both the up and acting set of a given PG, it will
1231# show twice.
1232#
1233# @param STDOUT a sorted list of OSD ids
1234# @return 0 on success, 1 on error
1235#
1236function get_osd_id_used_by_pgs() {
1237 ceph --format json pg dump pgs 2>/dev/null | jq '.[] | .up[], .acting[]' | sort
1238}
1239
1240function test_get_osd_id_used_by_pgs() {
1241 local dir=$1
1242
1243 setup $dir || return 1
1244 run_mon $dir a --osd_pool_default_size=1 || return 1
1245 run_mgr $dir x || return 1
1246 run_osd $dir 0 || return 1
1247 create_rbd_pool || return 1
1248 wait_for_clean || return 1
1249 local osd_ids=$(get_osd_id_used_by_pgs | uniq)
1250 test "$osd_ids" = "0" || return 1
1251 teardown $dir || return 1
1252}
1253
1254#######################################################################
1255
1256##
1257# Wait until the OSD **id** shows **count** times in the
1258# PGs (see get_osd_id_used_by_pgs for more information about
1259# how OSD ids are counted).
1260#
1261# @param id the OSD id
1262# @param count the number of time it must show in the PGs
1263# @return 0 on success, 1 on error
1264#
1265function wait_osd_id_used_by_pgs() {
1266 local id=$1
1267 local count=$2
1268
1269 status=1
1270 for ((i=0; i < $TIMEOUT / 5; i++)); do
1271 echo $i
1272 if ! test $(get_osd_id_used_by_pgs | grep -c $id) = $count ; then
1273 sleep 5
1274 else
1275 status=0
1276 break
1277 fi
1278 done
1279 return $status
1280}
1281
1282function test_wait_osd_id_used_by_pgs() {
1283 local dir=$1
1284
1285 setup $dir || return 1
1286 run_mon $dir a --osd_pool_default_size=1 || return 1
1287 run_mgr $dir x || return 1
1288 run_osd $dir 0 || return 1
1289 create_rbd_pool || return 1
1290 wait_for_clean || return 1
1291 wait_osd_id_used_by_pgs 0 8 || return 1
1292 ! TIMEOUT=1 wait_osd_id_used_by_pgs 123 5 || return 1
1293 teardown $dir || return 1
1294}
1295
1296#######################################################################
1297
7c673cae
FG
1298##
1299# Return the date and time of the last completed scrub for **pgid**,
1300# as reported by ceph pg dump pgs. Note that a repair also sets this
1301# date.
1302#
1303# @param pgid the id of the PG
1304# @param STDOUT the date and time of the last scrub
1305# @return 0 on success, 1 on error
1306#
1307function get_last_scrub_stamp() {
1308 local pgid=$1
1309 local sname=${2:-last_scrub_stamp}
31f18b77
FG
1310 ceph --format json pg dump pgs 2>/dev/null | \
1311 jq -r ".[] | select(.pgid==\"$pgid\") | .$sname"
7c673cae
FG
1312}
1313
1314function test_get_last_scrub_stamp() {
1315 local dir=$1
1316
1317 setup $dir || return 1
1318 run_mon $dir a --osd_pool_default_size=1 || return 1
1319 run_mgr $dir x || return 1
1320 run_osd $dir 0 || return 1
c07f9fc5 1321 create_rbd_pool || return 1
7c673cae 1322 wait_for_clean || return 1
b5b8bbf5 1323 stamp=$(get_last_scrub_stamp 1.0)
7c673cae
FG
1324 test -n "$stamp" || return 1
1325 teardown $dir || return 1
1326}
1327
1328#######################################################################
1329
1330##
1331# Predicate checking if the cluster is clean, i.e. all of its PGs are
1332# in a clean state (see get_num_active_clean for a definition).
1333#
1334# @return 0 if the cluster is clean, 1 otherwise
1335#
1336function is_clean() {
1337 num_pgs=$(get_num_pgs)
1338 test $num_pgs != 0 || return 1
1339 test $(get_num_active_clean) = $num_pgs || return 1
1340}
1341
1342function test_is_clean() {
1343 local dir=$1
1344
1345 setup $dir || return 1
1346 run_mon $dir a --osd_pool_default_size=1 || return 1
1347 run_mgr $dir x || return 1
1348 run_osd $dir 0 || return 1
c07f9fc5 1349 create_rbd_pool || return 1
7c673cae
FG
1350 wait_for_clean || return 1
1351 is_clean || return 1
1352 teardown $dir || return 1
1353}
1354
1355#######################################################################
1356
f64942e4 1357calc() { $AWK "BEGIN{print $*}"; }
94b18763 1358
7c673cae
FG
1359##
1360# Return a list of numbers that are increasingly larger and whose
1361# total is **timeout** seconds. It can be used to have short sleep
1362# delay while waiting for an event on a fast machine. But if running
1363# very slowly the larger delays avoid stressing the machine even
1364# further or spamming the logs.
1365#
1366# @param timeout sum of all delays, in seconds
1367# @return a list of sleep delays
1368#
1369function get_timeout_delays() {
1370 local trace=$(shopt -q -o xtrace && echo true || echo false)
1371 $trace && shopt -u -o xtrace
1372 local timeout=$1
1373 local first_step=${2:-1}
1374
1375 local i
1376 local total="0"
1377 i=$first_step
94b18763
FG
1378 while test "$(calc $total + $i \<= $timeout)" = "1"; do
1379 echo -n "$(calc $i) "
1380 total=$(calc $total + $i)
1381 i=$(calc $i \* 2)
7c673cae 1382 done
94b18763
FG
1383 if test "$(calc $total \< $timeout)" = "1"; then
1384 echo -n "$(calc $timeout - $total) "
7c673cae
FG
1385 fi
1386 $trace && shopt -s -o xtrace
1387}
1388
1389function test_get_timeout_delays() {
1390 test "$(get_timeout_delays 1)" = "1 " || return 1
94b18763
FG
1391 test "$(get_timeout_delays 5)" = "1 2 2 " || return 1
1392 test "$(get_timeout_delays 6)" = "1 2 3 " || return 1
7c673cae 1393 test "$(get_timeout_delays 7)" = "1 2 4 " || return 1
94b18763
FG
1394 test "$(get_timeout_delays 8)" = "1 2 4 1 " || return 1
1395 test "$(get_timeout_delays 1 .1)" = "0.1 0.2 0.4 0.3 " || return 1
1396 test "$(get_timeout_delays 1.5 .1)" = "0.1 0.2 0.4 0.8 " || return 1
1397 test "$(get_timeout_delays 5 .1)" = "0.1 0.2 0.4 0.8 1.6 1.9 " || return 1
1398 test "$(get_timeout_delays 6 .1)" = "0.1 0.2 0.4 0.8 1.6 2.9 " || return 1
1399 test "$(get_timeout_delays 6.3 .1)" = "0.1 0.2 0.4 0.8 1.6 3.2 " || return 1
1400 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
1401}
1402
1403#######################################################################
1404
1405##
1406# Wait until the cluster becomes clean or if it does not make progress
1407# for $TIMEOUT seconds.
1408# Progress is measured either via the **get_is_making_recovery_progress**
1409# predicate or if the number of clean PGs changes (as returned by get_num_active_clean)
1410#
1411# @return 0 if the cluster is clean, 1 otherwise
1412#
1413function wait_for_clean() {
1414 local num_active_clean=-1
1415 local cur_active_clean
1416 local -a delays=($(get_timeout_delays $TIMEOUT .1))
1417 local -i loop=0
31f18b77 1418
3a9019d9 1419 flush_pg_stats || return 1
31f18b77
FG
1420 while test $(get_num_pgs) == 0 ; do
1421 sleep 1
1422 done
7c673cae
FG
1423
1424 while true ; do
1425 # Comparing get_num_active_clean & get_num_pgs is used to determine
1426 # if the cluster is clean. That's almost an inline of is_clean() to
1427 # get more performance by avoiding multiple calls of get_num_active_clean.
1428 cur_active_clean=$(get_num_active_clean)
1429 test $cur_active_clean = $(get_num_pgs) && break
1430 if test $cur_active_clean != $num_active_clean ; then
1431 loop=0
1432 num_active_clean=$cur_active_clean
1433 elif get_is_making_recovery_progress ; then
1434 loop=0
1435 elif (( $loop >= ${#delays[*]} )) ; then
1436 ceph report
1437 return 1
1438 fi
1439 sleep ${delays[$loop]}
1440 loop+=1
1441 done
1442 return 0
1443}
1444
1445function test_wait_for_clean() {
1446 local dir=$1
1447
1448 setup $dir || return 1
1449 run_mon $dir a --osd_pool_default_size=1 || return 1
1450 run_mgr $dir x || return 1
c07f9fc5 1451 create_rbd_pool || return 1
7c673cae
FG
1452 ! TIMEOUT=1 wait_for_clean || return 1
1453 run_osd $dir 0 || return 1
1454 wait_for_clean || return 1
1455 teardown $dir || return 1
1456}
1457
1458#######################################################################
1459
1460##
1adf2230
AA
1461# Wait until the cluster has health condition passed as arg
1462# again for $TIMEOUT seconds.
7c673cae 1463#
1adf2230
AA
1464# @param string to grep for in health detail
1465# @return 0 if the cluster health matches request, 1 otherwise
7c673cae
FG
1466#
1467function wait_for_health() {
1468 local grepstr=$1
1469 local -a delays=($(get_timeout_delays $TIMEOUT .1))
1470 local -i loop=0
1471
1472 while ! ceph health detail | grep "$grepstr" ; do
1473 if (( $loop >= ${#delays[*]} )) ; then
1474 ceph health detail
1475 return 1
1476 fi
1477 sleep ${delays[$loop]}
1478 loop+=1
1479 done
1480}
1481
1adf2230
AA
1482##
1483# Wait until the cluster becomes HEALTH_OK again or if it does not make progress
1484# for $TIMEOUT seconds.
1485#
1486# @return 0 if the cluster is HEALTHY, 1 otherwise
1487#
7c673cae
FG
1488function wait_for_health_ok() {
1489 wait_for_health "HEALTH_OK" || return 1
1490}
1491
1492function test_wait_for_health_ok() {
1493 local dir=$1
1494
1495 setup $dir || return 1
1496 run_mon $dir a --osd_pool_default_size=1 --osd_failsafe_full_ratio=.99 --mon_pg_warn_min_per_osd=0 || return 1
31f18b77 1497 run_mgr $dir x --mon_pg_warn_min_per_osd=0 || return 1
7c673cae 1498 run_osd $dir 0 || return 1
224ce89b
WB
1499 kill_daemons $dir TERM osd || return 1
1500 ! TIMEOUT=1 wait_for_health_ok || return 1
1501 activate_osd $dir 0 || return 1
7c673cae
FG
1502 wait_for_health_ok || return 1
1503 teardown $dir || return 1
1504}
1505
1506
1507#######################################################################
1508
1509##
1510# Run repair on **pgid** and wait until it completes. The repair
1511# function will fail if repair does not complete within $TIMEOUT
1512# seconds.
1513#
1514# @param pgid the id of the PG
1515# @return 0 on success, 1 on error
1516#
1517function repair() {
1518 local pgid=$1
1519 local last_scrub=$(get_last_scrub_stamp $pgid)
1520 ceph pg repair $pgid
1521 wait_for_scrub $pgid "$last_scrub"
1522}
1523
1524function test_repair() {
1525 local dir=$1
1526
1527 setup $dir || return 1
1528 run_mon $dir a --osd_pool_default_size=1 || return 1
1529 run_mgr $dir x || return 1
1530 run_osd $dir 0 || return 1
c07f9fc5 1531 create_rbd_pool || return 1
7c673cae 1532 wait_for_clean || return 1
b5b8bbf5 1533 repair 1.0 || return 1
7c673cae 1534 kill_daemons $dir KILL osd || return 1
b5b8bbf5 1535 ! TIMEOUT=1 repair 1.0 || return 1
7c673cae
FG
1536 teardown $dir || return 1
1537}
1538#######################################################################
1539
1540##
1541# Run scrub on **pgid** and wait until it completes. The pg_scrub
1542# function will fail if repair does not complete within $TIMEOUT
1543# seconds. The pg_scrub is complete whenever the
1544# **get_last_scrub_stamp** function reports a timestamp different from
1545# the one stored before starting the scrub.
1546#
1547# @param pgid the id of the PG
1548# @return 0 on success, 1 on error
1549#
1550function pg_scrub() {
1551 local pgid=$1
1552 local last_scrub=$(get_last_scrub_stamp $pgid)
1553 ceph pg scrub $pgid
1554 wait_for_scrub $pgid "$last_scrub"
1555}
1556
1557function pg_deep_scrub() {
1558 local pgid=$1
1559 local last_scrub=$(get_last_scrub_stamp $pgid last_deep_scrub_stamp)
1560 ceph pg deep-scrub $pgid
1561 wait_for_scrub $pgid "$last_scrub" last_deep_scrub_stamp
1562}
1563
1564function test_pg_scrub() {
1565 local dir=$1
1566
1567 setup $dir || return 1
1568 run_mon $dir a --osd_pool_default_size=1 || return 1
1569 run_mgr $dir x || return 1
1570 run_osd $dir 0 || return 1
c07f9fc5 1571 create_rbd_pool || return 1
7c673cae 1572 wait_for_clean || return 1
b5b8bbf5 1573 pg_scrub 1.0 || return 1
7c673cae 1574 kill_daemons $dir KILL osd || return 1
b5b8bbf5 1575 ! TIMEOUT=1 pg_scrub 1.0 || return 1
7c673cae
FG
1576 teardown $dir || return 1
1577}
1578
1579#######################################################################
1580
1581##
1582# Run the *command* and expect it to fail (i.e. return a non zero status).
1583# The output (stderr and stdout) is stored in a temporary file in *dir*
1584# and is expected to contain the string *expected*.
1585#
1586# Return 0 if the command failed and the string was found. Otherwise
1587# return 1 and cat the full output of the command on stderr for debug.
1588#
1589# @param dir temporary directory to store the output
1590# @param expected string to look for in the output
1591# @param command ... the command and its arguments
1592# @return 0 on success, 1 on error
1593#
1594
1595function expect_failure() {
1596 local dir=$1
1597 shift
1598 local expected="$1"
1599 shift
1600 local success
1601
1602 if "$@" > $dir/out 2>&1 ; then
1603 success=true
1604 else
1605 success=false
1606 fi
1607
1608 if $success || ! grep --quiet "$expected" $dir/out ; then
1609 cat $dir/out >&2
1610 return 1
1611 else
1612 return 0
1613 fi
1614}
1615
1616function test_expect_failure() {
1617 local dir=$1
1618
1619 setup $dir || return 1
1620 expect_failure $dir FAIL bash -c 'echo FAIL ; exit 1' || return 1
1621 # the command did not fail
1622 ! expect_failure $dir FAIL bash -c 'echo FAIL ; exit 0' > $dir/out || return 1
1623 grep --quiet FAIL $dir/out || return 1
1624 # the command failed but the output does not contain the expected string
1625 ! expect_failure $dir FAIL bash -c 'echo UNEXPECTED ; exit 1' > $dir/out || return 1
1626 ! grep --quiet FAIL $dir/out || return 1
1627 teardown $dir || return 1
1628}
1629
1630#######################################################################
1631
1632##
1633# Given the *last_scrub*, wait for scrub to happen on **pgid**. It
1634# will fail if scrub does not complete within $TIMEOUT seconds. The
1635# repair is complete whenever the **get_last_scrub_stamp** function
1636# reports a timestamp different from the one given in argument.
1637#
1638# @param pgid the id of the PG
1639# @param last_scrub timestamp of the last scrub for *pgid*
1640# @return 0 on success, 1 on error
1641#
1642function wait_for_scrub() {
1643 local pgid=$1
1644 local last_scrub="$2"
1645 local sname=${3:-last_scrub_stamp}
1646
1647 for ((i=0; i < $TIMEOUT; i++)); do
b5b8bbf5 1648 if test "$(get_last_scrub_stamp $pgid $sname)" '>' "$last_scrub" ; then
7c673cae
FG
1649 return 0
1650 fi
1651 sleep 1
1652 done
1653 return 1
1654}
1655
1656function test_wait_for_scrub() {
1657 local dir=$1
1658
1659 setup $dir || return 1
1660 run_mon $dir a --osd_pool_default_size=1 || return 1
1661 run_mgr $dir x || return 1
1662 run_osd $dir 0 || return 1
c07f9fc5 1663 create_rbd_pool || return 1
7c673cae 1664 wait_for_clean || return 1
b5b8bbf5 1665 local pgid=1.0
7c673cae
FG
1666 ceph pg repair $pgid
1667 local last_scrub=$(get_last_scrub_stamp $pgid)
1668 wait_for_scrub $pgid "$last_scrub" || return 1
1669 kill_daemons $dir KILL osd || return 1
1670 last_scrub=$(get_last_scrub_stamp $pgid)
1671 ! TIMEOUT=1 wait_for_scrub $pgid "$last_scrub" || return 1
1672 teardown $dir || return 1
1673}
1674
1675#######################################################################
1676
1677##
1678# Return 0 if the erasure code *plugin* is available, 1 otherwise.
1679#
1680# @param plugin erasure code plugin
1681# @return 0 on success, 1 on error
1682#
1683
1684function erasure_code_plugin_exists() {
1685 local plugin=$1
1686 local status
1687 local grepstr
1688 local s
1689 case `uname` in
1690 FreeBSD) grepstr="Cannot open.*$plugin" ;;
1691 *) grepstr="$plugin.*No such file" ;;
1692 esac
1693
1694 s=$(ceph osd erasure-code-profile set TESTPROFILE plugin=$plugin 2>&1)
1695 local status=$?
1696 if [ $status -eq 0 ]; then
1697 ceph osd erasure-code-profile rm TESTPROFILE
1698 elif ! echo $s | grep --quiet "$grepstr" ; then
1699 status=1
1700 # display why the string was rejected.
1701 echo $s
1702 fi
1703 return $status
1704}
1705
1706function test_erasure_code_plugin_exists() {
1707 local dir=$1
1708
1709 setup $dir || return 1
1710 run_mon $dir a || return 1
1711 run_mgr $dir x || return 1
1712 erasure_code_plugin_exists jerasure || return 1
1713 ! erasure_code_plugin_exists FAKE || return 1
1714 teardown $dir || return 1
1715}
1716
1717#######################################################################
1718
1719##
1720# Display all log files from **dir** on stdout.
1721#
1722# @param dir directory in which all data is stored
1723#
1724
1725function display_logs() {
1726 local dir=$1
1727
1728 find $dir -maxdepth 1 -name '*.log' | \
1729 while read file ; do
1730 echo "======================= $file"
1731 cat $file
1732 done
1733}
1734
1735function test_display_logs() {
1736 local dir=$1
1737
1738 setup $dir || return 1
1739 run_mon $dir a || return 1
1740 kill_daemons $dir || return 1
1741 display_logs $dir > $dir/log.out
1742 grep --quiet mon.a.log $dir/log.out || return 1
1743 teardown $dir || return 1
1744}
1745
1746#######################################################################
1747##
1748# Spawn a command in background and save the pid in the variable name
1749# passed in argument. To make the output reading easier, the output is
1750# prepend with the process id.
1751#
1752# Example:
1753# pids1=""
1754# run_in_background pids1 bash -c 'sleep 1; exit 1'
1755#
1756# @param pid_variable the variable name (not value) where the pids will be stored
1757# @param ... the command to execute
1758# @return only the pid_variable output should be considered and used with **wait_background**
1759#
1760function run_in_background() {
1761 local pid_variable=$1
94b18763 1762 shift
7c673cae 1763 # Execute the command and prepend the output with its pid
f64942e4 1764 # We enforce to return the exit status of the command and not the sed one.
94b18763 1765 ("$@" |& sed 's/^/'$$': /'; return "${PIPESTATUS[0]}") >&2 &
7c673cae
FG
1766 eval "$pid_variable+=\" $!\""
1767}
1768
94b18763
FG
1769function save_stdout {
1770 local out="$1"
1771 shift
1772 "$@" > "$out"
1773}
1774
7c673cae
FG
1775function test_run_in_background() {
1776 local pids
1777 run_in_background pids sleep 1
1778 run_in_background pids sleep 1
1779 test $(echo $pids | wc -w) = 2 || return 1
1780 wait $pids || return 1
1781}
1782
1783#######################################################################
1784##
1785# Wait for pids running in background to complete.
1786# This function is usually used after a **run_in_background** call
1787# Example:
1788# pids1=""
1789# run_in_background pids1 bash -c 'sleep 1; exit 1'
1790# wait_background pids1
1791#
1792# @param pids The variable name that contains the active PIDS. Set as empty at then end of the function.
1793# @return returns 1 if at least one process exits in error unless returns 0
1794#
1795function wait_background() {
1796 # We extract the PIDS from the variable name
1797 pids=${!1}
1798
1799 return_code=0
1800 for pid in $pids; do
1801 if ! wait $pid; then
1802 # If one process failed then return 1
1803 return_code=1
1804 fi
1805 done
1806
1807 # We empty the variable reporting that all process ended
1808 eval "$1=''"
1809
1810 return $return_code
1811}
1812
1813
1814function test_wait_background() {
1815 local pids=""
1816 run_in_background pids bash -c "sleep 1; exit 1"
1817 run_in_background pids bash -c "sleep 2; exit 0"
1818 wait_background pids
1819 if [ $? -ne 1 ]; then return 1; fi
1820
1821 run_in_background pids bash -c "sleep 1; exit 0"
1822 run_in_background pids bash -c "sleep 2; exit 0"
1823 wait_background pids
1824 if [ $? -ne 0 ]; then return 1; fi
1825
1826 if [ ! -z "$pids" ]; then return 1; fi
1827}
1828
31f18b77
FG
1829function flush_pg_stats()
1830{
1831 local timeout=${1:-$TIMEOUT}
1832
1833 ids=`ceph osd ls`
1834 seqs=''
1835 for osd in $ids; do
1836 seq=`ceph tell osd.$osd flush_pg_stats`
1837 seqs="$seqs $osd-$seq"
1838 done
1839
1840 for s in $seqs; do
1841 osd=`echo $s | cut -d - -f 1`
1842 seq=`echo $s | cut -d - -f 2`
1843 echo "waiting osd.$osd seq $seq"
1844 while test $(ceph osd last-stat-seq $osd) -lt $seq; do
1845 sleep 1
1846 if [ $((timeout--)) -eq 0 ]; then
1847 return 1
1848 fi
1849 done
1850 done
1851}
1852
1853function test_flush_pg_stats()
1854{
1855 local dir=$1
1856
1857 setup $dir || return 1
1858 run_mon $dir a --osd_pool_default_size=1 || return 1
1859 run_mgr $dir x || return 1
1860 run_osd $dir 0 || return 1
c07f9fc5 1861 create_rbd_pool || return 1
31f18b77 1862 rados -p rbd put obj /etc/group
3a9019d9 1863 flush_pg_stats || return 1
31f18b77
FG
1864 local jq_filter='.pools | .[] | select(.name == "rbd") | .stats'
1865 raw_bytes_used=`ceph df detail --format=json | jq "$jq_filter.raw_bytes_used"`
1866 bytes_used=`ceph df detail --format=json | jq "$jq_filter.bytes_used"`
1867 test $raw_bytes_used > 0 || return 1
1868 test $raw_bytes_used == $bytes_used || return 1
b5b8bbf5 1869 teardown $dir
31f18b77
FG
1870}
1871
7c673cae
FG
1872#######################################################################
1873
1874##
1875# Call the **run** function (which must be defined by the caller) with
1876# the **dir** argument followed by the caller argument list.
1877#
1878# If the **run** function returns on error, all logs found in **dir**
1879# are displayed for diagnostic purposes.
1880#
1881# **teardown** function is called when the **run** function returns
1882# (on success or on error), to cleanup leftovers. The CEPH_CONF is set
1883# to /dev/null and CEPH_ARGS is unset so that the tests are protected from
1884# external interferences.
1885#
1886# It is the responsibility of the **run** function to call the
1887# **setup** function to prepare the test environment (create a temporary
1888# directory etc.).
1889#
1890# The shell is required (via PS4) to display the function and line
1891# number whenever a statement is executed to help debugging.
1892#
1893# @param dir directory in which all data is stored
1894# @param ... arguments passed transparently to **run**
1895# @return 0 on success, 1 on error
1896#
1897function main() {
1898 local dir=td/$1
1899 shift
1900
1901 shopt -s -o xtrace
1902 PS4='${BASH_SOURCE[0]}:$LINENO: ${FUNCNAME[0]}: '
1903
1904 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
1905 #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
1906
1907 export CEPH_CONF=/dev/null
1908 unset CEPH_ARGS
1909
1910 local code
1911 if run $dir "$@" ; then
1912 code=0
1913 else
7c673cae
FG
1914 code=1
1915 fi
b5b8bbf5 1916 teardown $dir $code || return 1
7c673cae
FG
1917 return $code
1918}
1919
1920#######################################################################
1921
1922function run_tests() {
1923 shopt -s -o xtrace
1924 PS4='${BASH_SOURCE[0]}:$LINENO: ${FUNCNAME[0]}: '
1925
1926 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
1927 #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
1928
1929 export CEPH_MON="127.0.0.1:7109" # git grep '\<7109\>' : there must be only one
1930 export CEPH_ARGS
b5b8bbf5 1931 CEPH_ARGS+=" --fsid=$(uuidgen) --auth-supported=none "
7c673cae
FG
1932 CEPH_ARGS+="--mon-host=$CEPH_MON "
1933 export CEPH_CONF=/dev/null
1934
1935 local funcs=${@:-$(set | sed -n -e 's/^\(test_[0-9a-z_]*\) .*/\1/p')}
1936 local dir=td/ceph-helpers
1937
1938 for func in $funcs ; do
b5b8bbf5
FG
1939 if ! $func $dir; then
1940 teardown $dir 1
1941 return 1
1942 fi
7c673cae
FG
1943 done
1944}
1945
1946if test "$1" = TESTS ; then
1947 shift
1948 run_tests "$@"
b5b8bbf5 1949 exit $?
7c673cae
FG
1950fi
1951
224ce89b
WB
1952# NOTE:
1953# jq only support --exit-status|-e from version 1.4 forwards, which makes
1954# returning on error waaaay prettier and straightforward.
1955# However, the current automated upstream build is running with v1.3,
1956# which has no idea what -e is. Hence the convoluted error checking we
1957# need. Sad.
1958# The next time someone changes this code, please check if v1.4 is now
1959# a thing, and, if so, please change these to use -e. Thanks.
1960
1961# jq '.all.supported | select([.[] == "foo"] | any)'
1962function jq_success() {
1963 input="$1"
1964 filter="$2"
1965 expects="\"$3\""
1966
1967 in_escaped=$(printf %s "$input" | sed "s/'/'\\\\''/g")
1968 filter_escaped=$(printf %s "$filter" | sed "s/'/'\\\\''/g")
1969
1970 ret=$(echo "$in_escaped" | jq "$filter_escaped")
1971 if [[ "$ret" == "true" ]]; then
1972 return 0
1973 elif [[ -n "$expects" ]]; then
1974 if [[ "$ret" == "$expects" ]]; then
1975 return 0
1976 fi
1977 fi
1978 return 1
1979 input=$1
1980 filter=$2
1981 expects="$3"
1982
1983 ret="$(echo $input | jq \"$filter\")"
1984 if [[ "$ret" == "true" ]]; then
1985 return 0
1986 elif [[ -n "$expects" && "$ret" == "$expects" ]]; then
1987 return 0
1988 fi
1989 return 1
1990}
1991
b5b8bbf5
FG
1992function inject_eio() {
1993 local pooltype=$1
1994 shift
1995 local which=$1
1996 shift
1997 local poolname=$1
1998 shift
1999 local objname=$1
2000 shift
2001 local dir=$1
2002 shift
2003 local shard_id=$1
2004 shift
2005
2006 local -a initial_osds=($(get_osds $poolname $objname))
2007 local osd_id=${initial_osds[$shard_id]}
2008 if [ "$pooltype" != "ec" ]; then
2009 shard_id=""
2010 fi
2011 set_config osd $osd_id filestore_debug_inject_read_err true || return 1
2012 local loop=0
2013 while ( CEPH_ARGS='' ceph --admin-daemon $(get_asok_path osd.$osd_id) \
2014 inject${which}err $poolname $objname $shard_id | grep -q Invalid ); do
2015 loop=$(expr $loop + 1)
2016 if [ $loop = "10" ]; then
2017 return 1
2018 fi
2019 sleep 1
2020 done
2021}
2022
1adf2230
AA
2023function multidiff() {
2024 if ! diff $@ ; then
2025 if [ "$DIFFCOLOPTS" = "" ]; then
2026 return 1
2027 fi
2028 diff $DIFFCOLOPTS $@
2029 fi
2030}
2031
7c673cae 2032# Local Variables:
c07f9fc5 2033# compile-command: "cd ../../src ; make -j4 && ../qa/standalone/ceph-helpers.sh TESTS # test_get_config"
7c673cae 2034# End: