blob: e729ab21d947c5a486b48fe604e588cb8c86e994 [file] [log] [blame]
#!/usr/bin/env bash
# Copyright (C) 2010-2013 Red Hat, Inc. All rights reserved.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions
# of the GNU General Public License v.2.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
# check.sh: assert various things about volumes
# USAGE:
# check linear VG LV
# check lv_on VG LV PV
# check mirror VG LV [LOGDEV|core]
# check mirror_nonredundant VG LV
# check mirror_legs VG LV N
# check mirror_images_on VG LV DEV [DEV...]
# ...
test -z "$BASH" || set -e -o pipefail
die() {
rm -f debug.log
echo -e "$@" >&2
return 1
}
lvl() {
lvs -a --noheadings "$@"
}
lvdevices() {
get lv_devices "$@"
}
mirror_images_redundant() {
local vg=$1
local lv=$vg/$2
lvs -a $vg -o+devices
for i in $(lvdevices $lv); do
echo "# $i:"
lvdevices $vg/$i | sort | uniq
done > check.tmp.all
(grep -v ^# check.tmp.all || true) | sort | uniq -d > check.tmp
test $(cat check.tmp | wc -l) -eq 0 || \
die "mirror images of $lv expected redundant, but are not:" \
$(cat check.tmp.all)
}
lv_err_list_() {
(echo "$2" | not grep -m 1 -q "$1") || \
echo "$3 on [ $(echo "$2" | grep "$1" | cut -b3- | tr '\n' ' ')] "
}
lv_on_diff_() {
declare -a xdevs=("${!1}") # pass in shell array
local expect=( "${@:4}" ) # make an array starting from 4th args...
local diff_e
# Find diff between 2 shell arrays, print them as stdin files
printf "%s\n" "${expect[@]}" | sort | uniq >_lv_on_diff1
printf "%s\n" "${xdevs[@]}" >_lv_on_diff2
diff_e=$(diff _lv_on_diff1 _lv_on_diff2) ||
die "LV $2/$3 $(lv_err_list_ "^>" "${diff_e}" found)$(lv_err_list_ "^<" "${diff_e}" "not found")."
}
# list devices for given LV
lv_on() {
local devs
devs=( $(lvdevices "$1/$2" | sort | uniq ) )
lv_on_diff_ devs[@] "${@}"
}
# list devices for given LV and all its subdevices
lv_tree_on() {
local devs
# Get sorted list of devices
devs=( $(get lv_tree_devices "$1" "$2") )
lv_on_diff_ devs[@] "${@}"
}
mirror_images_on() {
local vg=$1
local lv=$2
shift 2
for i in $(lvdevices $lv); do
lv_on $vg $lv $1
shift
done
}
mirror_log_on() {
local vg=$1
local lv=$2
local where=$3
if test "$where" = "core"; then
get lv_field $vg/$lv mirror_log | not grep mlog
else
lv_on $vg ${lv}_mlog "$where"
fi
}
lv_is_contiguous() {
local lv=$1/$2
test $(lvl --segments $lv | wc -l) -eq 1 || \
die "LV $lv expected to be contiguous, but is not:" \
$(lvl --segments $lv)
}
lv_is_clung() {
local lv=$1/$2
test $(lvdevices $lv | sort | uniq | wc -l) -eq 1 || \
die "LV $lv expected to be clung, but is not:" \
$(lvdevices $lv | sort | uniq)
}
mirror_images_contiguous() {
for i in $(lvdevices $1/$2); do
lv_is_contiguous $1 $i
done
}
mirror_images_clung() {
for i in $(lvdevices $1/$2); do
lv_is_clung $1 $i
done
}
mirror() {
mirror_nonredundant "$@"
mirror_images_redundant $1 $2
}
mirror_nonredundant() {
local lv=$1/$2
local attr=$(get lv_field $lv attr)
(echo "$attr" | grep "^......m...$" >/dev/null) || {
if (echo "$attr" | grep "^o.........$" >/dev/null) &&
lvs -a | fgrep "[${2}_mimage" >/dev/null; then
echo "TEST WARNING: $lv is a snapshot origin and looks like a mirror,"
echo "assuming it is actually a mirror"
else
die "$lv expected a mirror, but is not:" \
$(lvs $lv)
fi
}
test -z "$3" || mirror_log_on $1 $2 "$3"
}
mirror_legs() {
local expect=$3
test "$expect" -eq $(lvdevices $1/$2 | wc -w)
}
mirror_no_temporaries() {
local vg=$1
local lv=$2
(lvl -o name $vg | grep $lv | not grep "tmp") || \
die "$lv has temporary mirror images unexpectedly:" \
$(lvl $vg | grep $lv)
}
linear() {
local lv=$1/$2
test $(get lv_field $lv stripes -a) -eq 1 || \
die "$lv expected linear, but is not:" \
$(lvl $lv -o+devices)
}
# in_sync <VG> <LV>
# Works for "mirror" and "raid*"
in_sync() {
local a
local b
local idx
local type
local snap=""
local lvm_name="$1/$2"
local dm_name=$(echo $lvm_name | sed s:-:--: | sed s:/:-:)
a=( $(dmsetup status $dm_name) ) || \
die "Unable to get sync status of $1"
if [ ${a[2]} = "snapshot-origin" ]; then
a=( $(dmsetup status ${dm_name}-real) ) || \
die "Unable to get sync status of $1"
snap=": under snapshot"
fi
case ${a[2]} in
"raid")
# 6th argument is the sync ratio for RAID
idx=6
type=${a[3]}
if [ ${a[$(($idx + 1))]} != "idle" ]; then
echo "$lvm_name ($type$snap) is not in-sync"
return 1
fi
;;
"mirror")
# 4th Arg tells us how far to the sync ratio
idx=$((${a[3]} + 4))
type=${a[2]}
;;
*)
die "Unable to get sync ratio for target type '${a[2]}'"
;;
esac
b=( $(echo ${a[$idx]} | sed s:/:' ':) )
if [ ${b[0]} -eq 0 -o ${b[0]} != ${b[1]} ]; then
echo "$lvm_name ($type$snap) is not in-sync"
return 1
fi
[[ ${a[$(($idx - 1))]} =~ a ]] && \
die "$lvm_name ($type$snap) in-sync, but 'a' characters in health status"
echo "$lvm_name ($type$snap) is in-sync \"${a[@]}\""
}
active() {
local lv=$1/$2
(get lv_field $lv attr | grep "^....a.....$" >/dev/null) || \
die "$lv expected active, but lvs says it's not:" \
$(lvl $lv -o+devices)
dmsetup info $1-$2 >/dev/null ||
die "$lv expected active, lvs thinks it is but there are no mappings!"
}
inactive() {
local lv=$1/$2
(get lv_field $lv attr | grep "^....[-isd].....$" >/dev/null) || \
die "$lv expected inactive, but lvs says it's not:" \
$(lvl $lv -o+devices)
not dmsetup info $1-$2 2>/dev/null || \
die "$lv expected inactive, lvs thinks it is but there are mappings!"
}
# Check for list of LVs from given VG
lv_exists() {
local vg=$1
local lv=
while [ $# -gt 1 ]; do
shift
lv="$lv $vg/$1"
done
test -n "$lv" || lv=$vg
lvl $lv &>/dev/null || \
die "$lv expected to exist but does not"
}
lv_not_exists() {
local vg=$1
if test $# -le 1 ; then
if lvl $vg &>/dev/null ; then
die "$vg expected to not exist but it does!"
fi
else
while [ $# -gt 1 ]; do
shift
not lvl $vg/$1 &>/dev/null || die "$vg/$1 expected to not exist but it does!"
done
fi
rm -f debug.log
}
pv_field() {
local actual=$(get pv_field "$1" "$2" "${@:4}")
test "$actual" = "$3" || \
die "pv_field: PV=\"$1\", field=\"$2\", actual=\"$actual\", expected=\"$3\""
}
vg_field() {
local actual=$(get vg_field "$1" "$2" "${@:4}")
test "$actual" = "$3" || \
die "vg_field: vg=$1, field=\"$2\", actual=\"$actual\", expected=\"$3\""
}
vg_attr_bit() {
local actual=$(get vg_field "$2" vg_attr "${@:4}")
local offset=$1
case "$offset" in
perm*) offset=0 ;;
resiz*) offset=1 ;;
export*) offset=2 ;;
partial) offset=3 ;;
alloc*) offset=4 ;;
cluster*) offset=5 ;;
esac
test "${actual:$offset:1}" = "$3" || \
die "vg_attr_bit: vg=$2, ${offset} bit of \"$actual\" is \"${actual:$offset:1}\", but expected \"$3\""
}
lv_field() {
local actual=$(get lv_field "$1" "$2" "${@:4}")
test "$actual" = "$3" || \
die "lv_field: lv=$1, field=\"$2\", actual=\"$actual\", expected=\"$3\""
}
lvh_field() {
local actual=$(get lvh_field "$1" "$2" "${@:4}")
test "$actual" = "$3" || \
die "lvh_field: lv=$1, field=\"$2\", actual=\"$actual\", expected=\"$3\""
}
lva_field() {
local actual=$(get lva_field "$1" "$2" "${@:4}")
test "$actual" = "$3" || \
die "lva_field: lv=$1, field=\"$2\", actual=\"$actual\", expected=\"$3\""
}
lv_attr_bit() {
local actual=$(get lv_field "$2" lv_attr "${@:4}")
local offset=$1
case "$offset" in
type) offset=0 ;;
perm*) offset=1 ;;
alloc*) offset=2 ;;
fixed*) offset=3 ;;
state) offset=4 ;;
open) offset=5 ;;
target) offset=6 ;;
zero) offset=7 ;;
health) offset=8 ;;
skip) offset=9 ;;
esac
test "${actual:$offset:1}" = "$3" || \
die "lv_attr_bit: lv=$2, ${offset} bit of \"$actual\" is \"${actual:$offset:1}\", but expected \"$3\""
}
compare_fields() {
local cmd1=$1
local obj1=$2
local field1=$3
local cmd2=$4
local obj2=$5
local field2=$6
local val1=$($cmd1 --noheadings -o "$field1" "$obj1")
local val2=$($cmd2 --noheadings -o "$field2" "$obj2")
test "$val1" = "$val2" || \
die "compare_fields $obj1($field1): $val1 $obj2($field2): $val2"
}
compare_vg_field() {
local vg1=$1
local vg2=$2
local field=$3
local val1=$(vgs --noheadings -o "$field" $vg1)
local val2=$(vgs --noheadings -o "$field" $vg2)
test "$val1" = "$val2" || \
die "compare_vg_field: $vg1: $val1, $vg2: $val2"
}
pvlv_counts() {
local local_vg=$1
local num_pvs=$2
local num_lvs=$3
local num_snaps=$4
lvs -o+devices $local_vg
vg_field $local_vg pv_count $num_pvs
vg_field $local_vg lv_count $num_lvs
vg_field $local_vg snap_count $num_snaps
}
# Compare md5 check generated from get dev_md5sum
dev_md5sum() {
md5sum -c "md5.$1-$2" || \
(get lv_field $1/$2 "name,size,seg_pe_ranges"
die "LV $1/$2 has different MD5 check sum!")
}
sysfs() {
# read maj min and also convert hex to decimal
local maj=$(($(stat -L --printf=0x%t "$1")))
local min=$(($(stat -L --printf=0x%T "$1")))
local P="/sys/dev/block/$maj:$min/$2"
local val=$(< "$P") || return 0 # no sysfs ?
test "$val" -eq "$3" || \
die "$1: $P = $val differs from expected value $3!"
}
#set -x
unset LVM_VALGRIND
"$@"