#!/usr/bin/env bash

# Copyright (C) 2012 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

SKIP_WITH_LVMPOLLD=1

# hints are currently disabled with lvmlockd
SKIP_WITH_LVMLOCKD=1

. lib/inittest

RUNDIR="/run"
test -d "$RUNDIR" || RUNDIR="/var/run"
HINTS="$RUNDIR/lvm/hints"
NOHINTS="$RUNDIR/lvm/nohints"
NEWHINTS="$RUNDIR/lvm/newhints"
PREV="$RUNDIR/lvm/prev-hints"

# TODO:
# Test commands that ignore hints
# Test flock


aux lvmconf 'devices/scan_lvs = 0'

aux prepare_devs 6

# no PVs yet so hints should have no devs
pvs
not grep scan: $HINTS

#
# test --nohints option
#

pvcreate "$dev1"
pvcreate "$dev2"
# pvs --nohints does not create hints
pvs --nohints |tee out
grep "$dev1" out
grep "$dev2" out
not grep "$dev1" $HINTS
not grep "$dev2" $HINTS
# pvs creates hints
pvs
grep "$dev1" $HINTS
grep "$dev2" $HINTS

# save hints with dev1 and dev2 before dev3 is created
cp $HINTS $PREV
# pvcreate --nohints invalidates hints
pvcreate --nohints "$dev3"
ls $NEWHINTS
# pvs --nohints does not update hints
pvs --nohints |tee out
grep "$dev1" out
grep "$dev2" out
grep "$dev3" out
not grep "$dev3" $HINTS
# restore old hint file without dev3
cp $PREV $HINTS
# pvs --nohints does not update hints
pvs --nohints |tee out
grep "$dev1" out
grep "$dev2" out
grep "$dev3" out
grep "$dev1" $HINTS
grep "$dev2" $HINTS
not grep "$dev3" $HINTS
# pvs updates hints
pvs |tee out
grep "$dev1" out
grep "$dev2" out
grep "$dev3" out
grep "$dev1" $HINTS
grep "$dev2" $HINTS
grep "$dev3" $HINTS

aux wipefs_a "$dev1"
aux wipefs_a "$dev2"
aux wipefs_a "$dev3"

#
# vg1 uses dev1,dev2
#
# Test basics that PVs are in hints, not non-PV devs,
# and that only PVs are scanned when using hints.
#

rm $HINTS

vgcreate $vg1 "$dev1" "$dev2"
lvcreate -n $lv1 -l 4 $vg1

# test that only the two PVs are in hints
pvs
grep -v -E "$dev1|$dev2" $HINTS > tmptest
not grep scan: tmptest

# test that 'pvs' submits only three reads, one for each PV in hints
# for initial scan, and one more in vg_read rescan check

if which strace; then
strace -e io_submit pvs 2>&1|tee tmptest
test "$(grep io_submit tmptest | wc -l)" -eq 3

# test that 'pvs -a' submits seven reads, one for each device,
# and one more in vg_read rescan check
strace -e io_submit pvs -a 2>&1|tee tmptest
test "$(grep io_submit tmptest | wc -l)" -eq 7
fi

#
# vg2 uses dev3,dev4
#
# Test common commands that cause hints to be refreshed:
# pvcreate/vgcreate/vgextend/vgreduce/vgremove/pvremove
#

not pvs "$dev3"
not grep "$dev3" $HINTS
cp $HINTS $PREV
pvcreate "$dev3"
grep "# Created empty" $HINTS
cat $NEWHINTS
# next cmd recreates hints
pvs "$dev3"
grep "$dev3" $HINTS
not diff $HINTS $PREV
not cat $NEWHINTS

not vgs $vg2
cp $HINTS $PREV
vgcreate $vg2 "$dev3"
grep "# Created empty" $HINTS
cat $NEWHINTS
# next cmd recreates hints
vgs $vg2
grep $vg2 $HINTS
not diff $HINTS $PREV
not cat $NEWHINTS

cp $HINTS $PREV
vgextend $vg2 "$dev4"
grep "# Created empty" $HINTS
cat $NEWHINTS
# next cmd recreates hints
vgs $vg2
grep "$dev4" $HINTS
not diff $HINTS $PREV
not cat $NEWHINTS

cp $HINTS $PREV
vgreduce $vg2 "$dev4"
grep "# Created empty" $HINTS
cat $NEWHINTS
# next cmd recreates hints
vgs $vg2
grep "$dev4" $HINTS
not diff $HINTS $PREV
not cat $NEWHINTS

cp $HINTS $PREV
vgremove $vg2
grep "# Created empty" $HINTS
cat $NEWHINTS
# next cmd recreates hints
not vgs $vg2
not grep $vg2 $HINTS
not diff $HINTS $PREV
not cat $NEWHINTS

cp $HINTS $PREV
pvremove "$dev3" "$dev4"
grep "# Created empty" $HINTS
cat $NEWHINTS
# next cmd recreates hints
not pvs "$dev3"
not pvs "$dev4"
not grep "$dev3" $HINTS
not grep "$dev4" $HINTS
not diff $HINTS $PREV
not cat $NEWHINTS

#
# Test that adding a new device and removing a device
# causes hints to be recreated.
#
# with a devices file the appearance of a new device on
# the system does not disturb lvm, so this test doesn't
# apply
#

if ! lvmdevices; then

not pvs "$dev5"

# create a new temp device that will cause hint hash to change
DEVNAME=${PREFIX}pv99
echo "0 $(blockdev --getsize "$dev5") linear $dev5 0" | dmsetup create $DEVNAME
dmsetup status $DEVNAME

cp $HINTS $PREV
# pvs ignores current hints because of different dev hash and refreshes new hints
pvs
# devs listed in hints before and after are the same
grep scan: $PREV > scan1
grep scan: $HINTS > scan2
diff scan1 scan2
# hash listed before and after are different
cat $PREV
cat $HINTS
grep devs_hash $PREV > devs_hash1
grep devs_hash $HINTS > devs_hash2
not diff devs_hash1 devs_hash2

# hints are stable/unchanging
cp $HINTS $PREV
pvs
diff $HINTS $PREV

# remove the temp device which will cause hint hash to change again
dmsetup remove $DEVNAME

cp $HINTS $PREV
# pvs ignores current hints because of different dev hash and refreshes new hints
pvs
# devs listed in hints before and after are the same
grep scan: $PREV > scan1
grep scan: $HINTS > scan2
diff scan1 scan2
# hash listed before and after are different
grep devs_hash $PREV > devs_hash1
grep devs_hash $HINTS > devs_hash2
not diff devs_hash1 devs_hash2

# end of new device test for non-devicesfile case
fi

#
# Test that hints don't change from a bunch of commands
# that use hints and shouldn't change it.
#

# first create some more metadata using vg2
pvcreate "$dev3" "$dev4"
vgcreate $vg2 "$dev3"
lvcreate -n $lv1 -l1 $vg2
lvcreate -n $lv2 -l1 $vg2

cp $HINTS $PREV
lvm fullreport
lvchange -ay $vg1
lvchange -an $vg1
lvcreate -l1 -n $lv2 $vg1
lvcreate -l1 -an -n $lv3 $vg1
lvchange -an $vg1
lvremove $vg1/$lv3
lvresize -l+1 $vg1/$lv2
lvresize -l-1 $vg1/$lv2
lvdisplay
pvdisplay
vgdisplay
lvs
pvs
vgs
vgchange -ay $vg2
vgchange -an $vg2
vgck $vg2
lvrename $vg1 $lv2 $lv3
# no change in hints after all that
diff $HINTS $PREV

#
# Test that changing the filter will cause hint refresh
#

rm $HINTS $PREV
vgs
cp $HINTS $PREV
# this changes the filter to exclude dev5 which is not a PV
aux hide_dev "$dev5"
# next cmd sees different filter, ignores hints, creates new hints
pvs
not diff $HINTS $PREV
# run cmds using new filter
pvs
cp $HINTS $PREV
vgs
# hints are stable once refreshed
diff $HINTS $PREV
# this changes the filter to include dev5
aux unhide_dev "$dev5"
# next cmd sees different filter, ignores hints, creates new hints
pvs
not diff $HINTS $PREV
# hints are stable
cp $HINTS $PREV
vgs
diff $HINTS $PREV

#
# Test that changing scan_lvs will cause hint refresh
# 

rm $HINTS $PREV
vgs
cp $HINTS $PREV
# change lvm.conf
aux lvmconf 'devices/scan_lvs = 1'
# next cmd sees new setting, ignores hints, creates new hints
pvs
not diff $HINTS $PREV
# run cmds using new filter
pvs
cp $HINTS $PREV
vgs
# hints are stable once refreshed
diff $HINTS $PREV
# change lvm.conf back
aux lvmconf 'devices/scan_lvs = 0'
# next cmd sees different scan_lvs, ignores hints, creates new hints
pvs
not diff $HINTS $PREV
# hints are stable once refreshed
cp $HINTS $PREV
pvs
diff $HINTS $PREV

#
# Test pvscan --cache to force hints refresh
#

# pvs (no change), pvscan (hints are new), pvs (no change)
rm $HINTS $PREV
pvs
cp $HINTS $PREV
# this next pvscan recreates the hints file
pvscan --cache
# the only diff will be "Created by pvscan ..." vs "Created by pvs ..."
not diff $HINTS $PREV
cp $HINTS $PREV
pvs
diff $HINTS $PREV
grep 'Created by pvscan' $HINTS
# dev4 is a PV not used by a VG, dev5 is not a PV
# using dd to copy skirts hint tracking so dev5 won't be seen
# (unless the dd triggers udev which triggers pvscan --cache $dev5,
# but I've not seen that happen in tests so far.)
dd if="$dev4" of="$dev5" bs=1M
# this pvs won't see dev5
pvs > foo
cat foo
grep "$dev4" foo
not grep "$dev5" foo
# no hints have changed after dd and pvs since dd cannot be detected
diff $HINTS $PREV
# force hints refresh, will see duplicate now
pvscan --cache
not diff $HINTS $PREV
cat $HINTS
pvs -a > foo
# after force refresh, both devs (dups) appear in output
cat foo
grep "$dev4" foo
grep "$dev5" foo
# clear PV from dev5
dd if=/dev/zero of="$dev5" bs=1M count=1
# this pvs won't use hints because of duplicate PVs,
# and will create new hints
cp $HINTS $PREV
pvs > foo
not diff $HINTS $PREV
grep "$dev4" foo
not grep "$dev5" foo
grep "$dev4" $HINTS
not grep "$dev5" $HINTS

#
# Test pvscan --cache <dev> forces refresh
#

rm $HINTS $PREV
pvs
cp $HINTS $PREV
# this next pvscan creates newhints to trigger a refresh
pvscan --cache "$dev4"
cat $NEWHINTS
# this next pvs creates new hints
pvs
# the only diff will be "Created by..."
not diff $HINTS $PREV


#
# Test pvck --repair forces refresh
#

rm $HINTS $PREV
pvs
cp $HINTS $PREV
pvck --repairtype label_header -y "$dev3"
cat $NEWHINTS
grep 'Created empty by pvck' $HINTS
# this next pvs creates new hints
pvs
# the only diff will be "Created by..."
not diff $HINTS $PREV


#
# Test incorrect dev-to-pvid info in hints is detected
# dev4 is a PV not in a VG
#

pvs
cp $HINTS tmp-old
# this pvchange will invalidate current hints
pvchange -u "$dev4"
grep "# Created empty" $HINTS
cat $NEWHINTS
# this next pvs will create new hints with the new uuid
pvs
grep "$dev4" $HINTS > tmp-newuuid
cp $HINTS tmp-new
not diff tmp-old tmp-new
# hints are stable
pvs
diff $HINTS tmp-new
# replace the current hints with the old hints with the old uuid
cp tmp-old $HINTS
# this next pvs will see wrong dev-to-pvid mapping and invalidate hints
pvs
cat $HINTS
cat $NEWHINTS
# this next pvs will create new hints with the new uuid
pvs
cat $HINTS
grep -f tmp-newuuid $HINTS
rm tmp-old tmp-new tmp-newuuid


#
# Test incorrent pvid-to-vgname info in hints is detected
#

# this vgcreate invalidates current hints
vgcreate $vg3 "$dev4"
# this pvs creates new hints
pvs
cp $HINTS tmp-old
# this vgrename will invalidate current hints
vgrename $vg3 $vg4
# this pvs will create new hints with the new vg name
pvs
cp $HINTS tmp-new
not diff tmp-old tmp-new
# replace the current hints with the old hints with the old vg name
cp tmp-old $HINTS
# this pvs will see wrong pvid-to-vgname mapping and invalidate hints
pvs
cat $NEWHINTS
# this pvs will create new hints with the new vg name
pvs
grep $vg4 $HINTS

vgremove -y $vg4
vgremove -y $vg2
vgremove -y $vg1
