#!/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 # 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!" } # check raid_leg_status $vg $lv "Aaaaa" raid_leg_status() { local st=$(dmsetup status $1-$2) local val=$(echo "$st" | cut -d ' ' -f 6) test "$val" = "$3" || \ die "$1-$2 status $val != $3 ($st)" } grep_dmsetup() { dmsetup $1 $2 | tee out grep "${@:3}" out || die "Expected output from dmsetup $1 not found!" } #set -x unset LVM_VALGRIND "$@"