summaryrefslogtreecommitdiff
path: root/test/lib/aux.sh
blob: 241bbd5951eba6719f8a9b83313f5a903a583111 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
#!/usr/bin/env bash
# Copyright (C) 2011-2017 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

. lib/utils

test -n "$BASH" && set -euE -o pipefail

run_valgrind() {
	# Execute script which may use $TESTNAME for creating individual
	# log files for each execute command
	exec "${VALGRIND:-valgrind}" "$@"
}

expect_failure() {
        echo "TEST EXPECT FAILURE"
}

check_daemon_in_builddir() {
	# skip if we don't have our own deamon...
	if test -z "${installed_testsuite+varset}"; then
		(which "$1" 2>/dev/null | grep "$abs_builddir" >/dev/null ) || skip "$1 is not in executed path."
	fi
	rm -f debug.log strace.log
}

create_corosync_conf() {
	COROSYNC_CONF="/etc/corosync/corosync.conf"
	COROSYNC_NODE=$(hostname)

	if test -a "$COROSYNC_CONF"; then
		if ! grep "created by lvm test suite" "$COROSYNC_CONF"; then
			rm "$COROSYNC_CONF"
		else
			mv "$COROSYNC_CONF" "$COROSYNC_CONF.prelvmtest"
		fi
	fi

	sed -e "s/@LOCAL_NODE@/$COROSYNC_NODE/" lib/test-corosync-conf > "$COROSYNC_CONF"
	echo "created new $COROSYNC_CONF"
}

DLM_CONF="/etc/dlm/dlm.conf"
create_dlm_conf() {
	if test -a "$DLM_CONF"; then
		if ! grep "created by lvm test suite" "$DLM_CONF"; then
			rm "$DLM_CONF"
		else
			mv "$DLM_CONF" "$DLM_CONF.prelvmtest"
		fi
	fi

	cp lib/test-dlm-conf "$DLM_CONF"
	echo "created new $DLM_CONF"
}

prepare_dlm() {
	if pgrep dlm_controld ; then
		echo "Cannot run while existing dlm_controld process exists."
		exit 1
	fi

	if pgrep corosync; then
		echo "Cannot run while existing corosync process exists."
		exit 1
	fi

	create_corosync_conf
	create_dlm_conf

	systemctl start corosync
	sleep 1
	if ! pgrep corosync; then
		echo "Failed to start corosync."
		exit 1
	fi

	systemctl start dlm
	sleep 1
	if ! pgrep dlm_controld; then
		echo "Failed to start dlm."
		exit 1
	fi
}

SANLOCK_CONF="/etc/sanlock/sanlock.conf"
create_sanlock_conf() {
	if test -a "$SANLOCK_CONF"; then
		if ! grep "created by lvm test suite" "$SANLOCK_CONF"; then
			rm "$SANLOCK_CONF"
		else
			mv "$SANLOCK_CONF" "$SANLOCK_CONF.prelvmtest"
		fi
	fi

	cp lib/test-sanlock-conf "$SANLOCK_CONF"
	echo "created new $SANLOCK_CONF"
}

prepare_sanlock() {
	if pgrep sanlock ; then
		echo "Cannot run while existing sanlock process exists"
		exit 1
	fi

	create_sanlock_conf

	systemctl start sanlock
	if ! pgrep sanlock; then
		echo "Failed to start sanlock"
		exit 1
	fi
}

prepare_idm() {
	if pgrep seagate_ilm; then
		echo "Cannot run while existing seagate_ilm process exists"
		exit 1
	fi

	seagate_ilm -D 0 -l 0 -L 7 -E 7 -S 7

	if ! pgrep seagate_ilm; then
		echo "Failed to start seagate_ilm"
		exit 1
	fi
}

prepare_lvmlockd() {
	if pgrep lvmlockd ; then
		echo "Cannot run while existing lvmlockd process exists"
		exit 1
	fi

	if test -n "$LVM_TEST_LOCK_TYPE_SANLOCK"; then
		# make check_lvmlockd_sanlock
		echo "starting lvmlockd for sanlock"
		lvmlockd -o 2

	elif test -n "$LVM_TEST_LOCK_TYPE_DLM"; then
		# make check_lvmlockd_dlm
		echo "starting lvmlockd for dlm"
		lvmlockd

	elif test -n "$LVM_TEST_LOCK_TYPE_IDM"; then
		# make check_lvmlockd_idm
		echo "starting lvmlockd for idm"
		lvmlockd -g idm

	elif test -n "$LVM_TEST_LVMLOCKD_TEST_DLM"; then
		# make check_lvmlockd_test
		echo "starting lvmlockd --test (dlm)"
		lvmlockd --test -g dlm

	elif test -n "$LVM_TEST_LVMLOCKD_TEST_SANLOCK"; then
		# FIXME: add option for this combination of --test and sanlock
		echo "starting lvmlockd --test (sanlock)"
		lvmlockd --test -g sanlock -o 2

	elif test -n "$LVM_TEST_LVMLOCKD_TEST_IDM"; then
		# make check_lvmlockd_test
		echo "starting lvmlockd --test (idm)"
		lvmlockd --test -g idm

	else
		echo "not starting lvmlockd"
		exit 0
	fi

	sleep 1
	if ! pgrep lvmlockd >LOCAL_LVMLOCKD; then
		echo "Failed to start lvmlockd"
		exit 1
	fi
}

prepare_clvmd() {
	test "${LVM_TEST_LOCKING:-0}" -ne 3 && return # not needed

	if pgrep clvmd ; then
		skip "Cannot use fake cluster locking with real clvmd ($(pgrep clvmd)) running."
	fi

	check_daemon_in_builddir clvmd

	test -e "$DM_DEV_DIR/control" || dmsetup table >/dev/null # create control node
	# skip if singlenode is not compiled in
	(clvmd --help 2>&1 | grep "Available cluster managers" | grep "singlenode" >/dev/null) || \
		skip "Compiled clvmd does not support singlenode for testing."

#	lvmconf "activation/monitoring = 1"
	local run_valgrind=""
	test "${LVM_VALGRIND_CLVMD:-0}" -eq 0 || run_valgrind="run_valgrind"
	rm -f "$CLVMD_PIDFILE"
	echo "<======== Starting CLVMD ========>"
	echo -n "## preparing clvmd..."
	# lvs is executed from clvmd - use our version
	LVM_LOG_FILE_EPOCH=CLVMD LVM_LOG_FILE_MAX_LINES=1000000 $run_valgrind clvmd -Isinglenode -d 1 -f &
	echo $! > LOCAL_CLVMD

	for i in {200..0} ; do
		test "$i" -eq 0 && die "Startup of clvmd is too slow."
		test -e "$CLVMD_PIDFILE" && test -e "${CLVMD_PIDFILE%/*}/lvm/clvmd.sock" && break
		echo -n .
		sleep .1
	done
	echo ok
}

prepare_dmeventd() {
	if pgrep dmeventd ; then
		skip "Cannot test dmeventd with real dmeventd ($(pgrep dmeventd)) running."
	fi

	check_daemon_in_builddir dmeventd
	lvmconf "activation/monitoring = 1"

	local run_valgrind=""
	test "${LVM_VALGRIND_DMEVENTD:-0}" -eq 0 || run_valgrind="run_valgrind"
	echo -n "## preparing dmeventd..."
#	LVM_LOG_FILE_EPOCH=DMEVENTD $run_valgrind dmeventd -fddddl "$@" 2>&1 &
	LVM_LOG_FILE_EPOCH=DMEVENTD $run_valgrind dmeventd -fddddl "$@" >debug.log_DMEVENTD_out 2>&1 &
	echo $! > LOCAL_DMEVENTD

	# FIXME wait for pipe in /var/run instead
	for i in {200..0} ; do
		test "$i" -eq 0 && die "Startup of dmeventd is too slow."
		test -e "${DMEVENTD_PIDFILE}" && break
		echo -n .
		sleep .1
	done
	echo ok
}

prepare_lvmpolld() {
	test -e LOCAL_LVMPOLLD || lvmconf "global/use_lvmpolld = 1"

	local run_valgrind=""
	test "${LVM_VALGRIND_LVMPOLLD:-0}" -eq 0 || run_valgrind="run_valgrind"

	kill_sleep_kill_ LOCAL_LVMPOLLD "${LVM_VALGRIND_LVMPOLLD:-0}"

	echo -n "## preparing lvmpolld..."
	$run_valgrind lvmpolld -f "$@" -s "$TESTDIR/lvmpolld.socket" -B "$TESTDIR/lib/lvm" -l all &
	echo $! > LOCAL_LVMPOLLD
	for i in {200..0} ; do
		test -e "$TESTDIR/lvmpolld.socket" && break
		echo -n .;
		sleep .1;
	done # wait for the socket
	test "$i" -gt 0 || die "Startup of lvmpolld is too slow."
	echo ok
}

lvmpolld_talk() {
	local use=nc
	if type -p socat >& /dev/null; then
		use=socat
	elif echo | not nc -U "$TESTDIR/lvmpolld.socket" ; then
		echo "WARNING: Neither socat nor nc -U seems to be available." 1>&2
		echo "## failed to contact lvmpolld."
		return 1
	fi

	if test "$use" = nc ; then
		nc -U "$TESTDIR/lvmpolld.socket"
	else
		socat "unix-connect:$TESTDIR/lvmpolld.socket" -
	fi | tee -a lvmpolld-talk.txt
}

lvmpolld_dump() {
	(echo 'request="dump"'; echo '##') | lvmpolld_talk "$@"
}

prepare_lvmdbusd() {
	local lvmdbusdebug=
	local daemon
	rm -f debug.log_LVMDBUSD_out

	kill_sleep_kill_ LOCAL_LVMDBUSD 0

        # FIXME: This is not correct! Daemon is auto started.
	echo -n "## checking lvmdbusd is NOT running..."
	if pgrep -f -l lvmdbusd | grep python3 || pgrep -x -l lvmdbusd ; then
		skip "Cannot run lvmdbusd while existing lvmdbusd process exists"
	fi
	echo ok

	# skip if we don't have our own lvmdbusd...
	echo -n "## find lvmdbusd to use..."
	if test -z "${installed_testsuite+varset}"; then
		# NOTE: this is always present - additional checks are needed:
		daemon="$abs_top_builddir/daemons/lvmdbusd/lvmdbusd"
		if test -x "$daemon" || chmod ugo+x "$daemon"; then
			echo "$daemon"
		else
			echo "Failed to make '$daemon' executable">&2
			return 1
		fi
		# Setup the python path so we can run
		export PYTHONPATH="$abs_top_builddir/daemons"
	else
		daemon=$(which lvmdbusd || :)
		echo "$daemon"
	fi
	test -x "$daemon" || skip "The lvmdbusd daemon is missing"
	which python3 >/dev/null || skip "Missing python3"

	python3 -c "import pyudev, dbus, gi.repository" || skip "Missing python modules"
	python3 -c "from json.decoder import JSONDecodeError" || skip "Python json module is missing JSONDecodeError"

	# Copy the needed file to run on the system bus if it doesn't
	# already exist
	if [ ! -f /etc/dbus-1/system.d/com.redhat.lvmdbus1.conf ]; then
		install -m 644 "$abs_top_builddir/scripts/com.redhat.lvmdbus1.conf" /etc/dbus-1/system.d/
	fi

	echo "## preparing lvmdbusd..."
	lvmconf "global/notify_dbus = 1"

	test "${LVM_DEBUG_LVMDBUS:-0}" != "0" && lvmdbusdebug="--debug"
	"$daemon" $lvmdbusdebug > debug.log_LVMDBUSD_out 2>&1 &
	local pid=$!

	sleep 1
	echo -n "## checking lvmdbusd IS running..."
	comm=
	# TODO: Is there a better check than wait 1 second and check pid?
	if ! comm=$(ps -p $pid -o comm=) >/dev/null || [[ $comm != lvmdbusd ]]; then
		echo "Failed to start lvmdbusd daemon"
		return 1
	fi
	echo "$pid" > LOCAL_LVMDBUSD
	echo ok
}

#
# Temporary solution to create some occupied thin metadata
# This heavily depends on thin metadata output format to stay as is.
# Currently it expects 2MB thin metadata and 200MB data volume size
# Argument specifies how many devices should be created.
#
prepare_thin_metadata() {
	local devices=$1
	local transaction_id=${2:-0}
	local data_block_size=${3:-128}
	local nr_data_blocks=${4:-3200}
	local i

	echo '<superblock uuid="" time="1" transaction="'"$transaction_id"'" data_block_size="'"$data_block_size"'" nr_data_blocks="'"$nr_data_blocks"'">'
	for i in $(seq 1 "$devices")
	do
		echo ' <device dev_id="'"$i"'" mapped_blocks="37" transaction="'"$i"'" creation_time="0" snap_time="1">'
		echo '  <range_mapping origin_begin="0" data_begin="0" length="37" time="0"/>'
		echo ' </device>'
	done
	echo "</superblock>"
}

teardown_devs_prefixed() {
	local prefix=$1
	local stray=${2:-0}
	local IFS=$IFS_NL
	local once=1
	local dm

	rm -rf "${TESTDIR:?}/dev/$prefix*"

	# Send idle message to frozen raids (with hope to unfreeze them)
	for dm in $(dm_status | grep -E "$prefix.*raid.*frozen"); do
		echo "## unfreezing: dmsetup message \"${dm%:*}\""
		dmsetup message "${dm%:*}" 0 "idle" &
	done

	# Resume suspended devices first
	for dm in $(dm_info name -S "name=~$PREFIX&&suspended=Suspended"); do
		test "$dm" != "No devices found" || break
		echo "## resuming: dmsetup resume \"$dm\""
		dmsetup clear "$dm"
		dmsetup resume "$dm" &
	done

	wait

	local mounts
	mounts=( $(grep "$prefix" /proc/mounts | cut -d' ' -f1) ) || true
	if test ${#mounts[@]} -gt 0; then
		test "$stray" -eq 0 || echo "## removing stray mounted devices containing $prefix:" "${mounts[@]}"
		if umount -fl "${mounts[@]}"; then
			udev_wait
		fi
	fi

	# Remove devices, start with closed (sorted by open count)
	# Run 'dmsetup remove' in parallel
	rm -f REMOVE_FAILED
	#local listdevs=( $(dm_info name,open --sort open,name | grep "$prefix.*:0") )
	#dmsetup remove --deferred ${listdevs[@]%%:0} || touch REMOVE_FAILED

	# 2nd. loop is trying --force removal which can possibly 'unstuck' some bloked operations
	for i in 0 1; do
		test "$i" = 1 && test "$stray" = 0 && break  # no stray device removal

		while :; do
			local sortby="name"
			local progress=0

			# HACK: sort also by minors - so we try to close 'possibly later' created device first
			test "$i" = 0 || sortby="-minor"

			for dm in $(dm_info name,open --separator ';'  --nameprefixes --unquoted --sort open,"$sortby" -S "name=~$prefix" --mangle none || true) ; do
				test "$dm" != "No devices found" || break 2
				DM_NAME=${dm##DM_NAME=}
				DM_NAME=${DM_NAME%%;DM_OPEN*}
				DM_OPEN=${dm##*;DM_OPEN=}
				local force="-f"
				if test "$i" = 0; then
					if test "$once" = 1 ; then
						once=0
						echo "## removing stray mapped devices with names beginning with $prefix: "
					fi
					test "$DM_OPEN" = 0 || break  # stop loop with 1st. opened device
					force=""
				fi

				# Succesfull 'remove' signals progress
				dmsetup remove $force "$DM_NAME" --mangle none && progress=1
			done

			test "$i" = 0 || break

			test "$progress" = 1 || break

			udev_wait
			wait
		done # looping till there are some removed devices
	done
}

teardown_devs() {
	# Delete any remaining dm/udev semaphores
	teardown_udev_cookies
	restore_dm_mirror

	test ! -f MD_DEV || cleanup_md_dev
	test ! -f DEVICES || teardown_devs_prefixed "$PREFIX"
	test ! -f RAMDISK || { modprobe -r brd || true ; }

	# NOTE: SCSI_DEBUG_DEV test must come before the LOOP test because
	# prepare_scsi_debug_dev() also sets LOOP to short-circuit prepare_loop()
	if test -f SCSI_DEBUG_DEV; then
		udev_wait
		test "${LVM_TEST_PARALLEL:-0}" -eq 1 || {
			if ! modprobe -r scsi_debug ; then
				sleep 1
				modprobe -r scsi_debug || true
			fi
		}
	else
		test ! -f LOOP || losetup -d "$(< LOOP)" || true
		test ! -f LOOPFILE || rm -f "$(< LOOPFILE)"
	fi

	not diff LOOP BACKING_DEV >/dev/null 2>&1 || rm -f BACKING_DEV
	rm -f DEVICES LOOP RAMDISK

	# Attempt to remove any loop devices that failed to get torn down if earlier tests aborted
	test "${LVM_TEST_PARALLEL:-0}" -eq 1 || test -z "$COMMON_PREFIX" || {
		local stray_loops
		stray_loops=( $(losetup -a | grep "$COMMON_PREFIX" | cut -d: -f1) ) || true
		test ${#stray_loops[@]} -eq 0 || {
			teardown_devs_prefixed "$COMMON_PREFIX" 1
			echo "## removing stray loop devices containing $COMMON_PREFIX:" "${stray_loops[@]}"
			for i in "${stray_loops[@]}" ; do test ! -b "$i" || losetup -d "$i" || true ; done
			# Leave test when udev processed all removed devices
			udev_wait
		}
	}
}

kill_sleep_kill_() {
	local pidfile=$1
	local slow=$2

	if test -s "$pidfile" ; then
		pid=$(< "$pidfile")
		rm -f "$pidfile"
		kill -TERM "$pid" 2>/dev/null || return 0
		for i in {0..10} ; do
			ps "$pid" >/dev/null || return 0
			if test "$slow" -eq 0 ; then sleep .2 ; else sleep 1 ; fi
			kill -KILL "$pid" 2>/dev/null || true
		done
	fi
}

print_procs_by_tag_() {
	(ps -o pid,args ehax | grep -we"LVM_TEST_TAG=${1:-kill_me_$PREFIX}") || true
}

count_processes_with_tag() {
	print_procs_by_tag_ | wc -l
}

kill_tagged_processes() {
	local pid
	local wait

	# read uses all vars within pipe subshell
	local pids=()
	while read -r pid wait; do
		if test -n "$pid" ; then
			echo "## killing tagged process: $pid ${wait:0:120}..."
			kill -TERM "$pid" 2>/dev/null || true
		fi
		pids+=( "$pid" )
	done < <(print_procs_by_tag_ "$@")

	test ${#pids[@]} -eq 0 && return

	# wait if process exited and eventually -KILL
	wait=0
	for pid in "${pids[@]}" ; do
		while ps "$pid" > /dev/null && test "$wait" -le 10; do
			sleep .2
			wait=$(( wait + 1 ))
		done
		test "$wait" -le 10 || kill -KILL "$pid" 2>/dev/null || true
	done
}

teardown() {
	local TEST_LEAKED_DEVICES=""
	echo -n "## teardown..."
	unset LVM_LOG_FILE_EPOCH

	if test -f TESTNAME ; then

	if test ! -f SKIP_THIS_TEST ; then
		# Evaluate left devices only for non-skipped tests
		TEST_LEAKED_DEVICES=$(dmsetup table | grep "$PREFIX" | \
			grep -Ev "${PREFIX}(pv|[0-9])" | \
			grep -v "$(cat ERR_DEV_NAME 2>/dev/null)" | \
			grep -v "$(cat ZERO_DEV_NAME 2>/dev/null)") || true
	fi

	kill_tagged_processes

	if test -n "$LVM_TEST_LVMLOCKD_TEST" ; then
		echo ""
		echo "## stopping lvmlockd in teardown"
		kill_sleep_kill_ LOCAL_LVMLOCKD 0
	fi

	dm_table | not grep -E -q "$vg|$vg1|$vg2|$vg3|$vg4" || {
		# Avoid activation of dmeventd if there is no pid
		cfg=$(test -s LOCAL_DMEVENTD || echo "--config activation{monitoring=0}")
		if dm_info suspended,name | grep "^Suspended:.*$PREFIX" >/dev/null ; then
			echo "## skipping vgremove, suspended devices detected."
		else
			vgremove -ff "$cfg"  \
			"$vg" "$vg1" "$vg2" "$vg3" "$vg4" &>/dev/null || rm -f debug.log strace.log
		fi
	}

	kill_sleep_kill_ LOCAL_LVMDBUSD 0

	echo -n .

	kill_sleep_kill_ LOCAL_LVMPOLLD "${LVM_VALGRIND_LVMPOLLD:-0}"

	echo -n .

	kill_sleep_kill_ LOCAL_CLVMD "${LVM_VALGRIND_CLVMD:-0}"

	echo -n .

	kill_sleep_kill_ LOCAL_DMEVENTD "${LVM_VALGRIND_DMEVENTD:-0}"

	echo -n .

	test -d "$DM_DEV_DIR/mapper" && teardown_devs

	echo -n .

	fi

	test -z "$TEST_LEAKED_DEVICES" || {
		echo "## unexpected devices left dm table:"
		echo "$TEST_LEAKED_DEVICES"
		return 1
	}

	if test "${LVM_TEST_PARALLEL:-0}" = 0 && test -z "$RUNNING_DMEVENTD"; then
		not pgrep dmeventd &>/dev/null # printed in STACKTRACE
	fi

	echo -n .

	test -n "$TESTDIR" && {
		cd "$TESTOLDPWD" || die "Failed to enter $TESTOLDPWD"
		# after this delete no further write is possible
		rm -rf "${TESTDIR:?}" || echo BLA
	}

	# Remove any dangling symlink in /dev/disk (our tests can confuse udev)
	test -d /dev/disk && {
		find /dev/disk -type l ! -exec /usr/bin/test -e {} \; -print0 | xargs -0 rm -f || true
	}

	# Remove any metadata archives and backups from this test on system
	rm -f /etc/lvm/archive/"${PREFIX}"* /etc/lvm/backup/"${PREFIX}"*

	echo "ok"
}

prepare_loop() {
	local size=$1
	shift # all other params are directly passed to all 'losetup' calls
	local i
	local slash

	test -f LOOP && LOOP=$(< LOOP)
	echo -n "## preparing loop device..."

	# skip if prepare_scsi_debug_dev() was used
	if test -f SCSI_DEBUG_DEV && test -f LOOP ; then
		echo "(skipped)"
		return 0
	fi

	test ! -e LOOP
	test -n "$DM_DEV_DIR"

	for i in 0 1 2 3 4 5 6 7; do
		test -e "$DM_DEV_DIR/loop$i" || mknod "$DM_DEV_DIR/loop$i" b 7 $i
	done

	echo -n .

	local LOOPFILE="$PWD/test.img"
	rm -f "$LOOPFILE"
	dd if=/dev/zero of="$LOOPFILE" bs=$((1024*1024)) count=0 seek=$(( size + 1 )) 2> /dev/null
	if LOOP=$(losetup "$@" -s -f "$LOOPFILE" 2>/dev/null); then
		:
	elif LOOP=$(losetup -f) && losetup "$@" "$LOOP" "$LOOPFILE"; then
		# no -s support
		:
	else
		# no -f support
		# Iterate through $DM_DEV_DIR/loop{,/}{0,1,2,3,4,5,6,7}
		for slash in '' /; do
			for i in 0 1 2 3 4 5 6 7; do
				local dev="$DM_DEV_DIR/loop$slash$i"
				! losetup "$dev" >/dev/null 2>&1 || continue
				# got a free
				losetup "$@" "$dev" "$LOOPFILE"
				LOOP=$dev
				break
			done
			test -z "$LOOP" || break
		done
	fi
	test -n "$LOOP" # confirm or fail
	touch NO_BLKDISCARD_Z    # loop devices do not support WRITE_ZEROS
	BACKING_DEV=$LOOP
	echo "$LOOP" > LOOP
	echo "$LOOP" > BACKING_DEV
	echo "ok ($LOOP)"
}

prepare_ramdisk() {
	local size=$1

	# if brd is unused, remove and use for test
	modprobe -r brd || return 0

	echo -n "## preparing ramdisk device..."
	modprobe brd rd_size=$((size * 1024)) || return

	BACKING_DEV=/dev/ram0
	echo "ok ($BACKING_DEV)"
	touch RAMDISK
}

prepare_real_devs() {
	aux lvmconf 'devices/scan = "/dev"'

	touch REAL_DEVICES

	if test -n "$LVM_TEST_DEVICE_LIST"; then
		local count=0
		while read path; do
			REAL_DEVICES[count]=$path
			count=$((  count + 1 ))
			aux extend_filter "a|$path|"
			dd if=/dev/zero of="$path" bs=32k count=1
			wipefs -a "$path" 2>/dev/null || true
		done < "$LVM_TEST_DEVICE_LIST"
	fi
	printf "%s\\n" "${REAL_DEVICES[@]}" > REAL_DEVICES
}

# A drop-in replacement for prepare_loop() that uses scsi_debug to create
# a ramdisk-based SCSI device upon which all LVM devices will be created
# - scripts must take care not to use a DEV_SIZE that will enduce OOM-killer
prepare_scsi_debug_dev() {
	local DEV_SIZE=$1
	shift # rest of params directly passed to modprobe
	local DEBUG_DEV

	rm -f debug.log strace.log
	test ! -f "SCSI_DEBUG_DEV" || return 0
	test ! -f LOOP
	test -n "$DM_DEV_DIR"

	# Skip test if scsi_debug module is unavailable or is already in use
	modprobe --dry-run scsi_debug || skip
	lsmod | not grep scsi_debug >/dev/null || skip

	# Create the scsi_debug device and determine the new scsi device's name
	# NOTE: it will _never_ make sense to pass num_tgts param;
	# last param wins.. so num_tgts=1 is imposed
	touch SCSI_DEBUG_DEV
	modprobe scsi_debug dev_size_mb="$(( DEV_SIZE + 2 ))" "$@" num_tgts=1 || skip

	for i in {1..20} ; do
		sleep .1 # allow for async Linux SCSI device registration
		DEBUG_DEV="/dev/$(grep -H scsi_debug /sys/block/sd*/device/model | cut -f4 -d /)"
		test -b "$DEBUG_DEV" && break
	done
	test -b "$DEBUG_DEV" || return 1 # should not happen

	# Create symlink to scsi_debug device in $DM_DEV_DIR
	SCSI_DEBUG_DEV="$DM_DEV_DIR/$(basename "$DEBUG_DEV")"
	echo "$SCSI_DEBUG_DEV" > SCSI_DEBUG_DEV
	echo "$SCSI_DEBUG_DEV" > BACKING_DEV
	# Setting $LOOP provides means for prepare_devs() override
	test "$DEBUG_DEV" = "$SCSI_DEBUG_DEV" || ln -snf "$DEBUG_DEV" "$SCSI_DEBUG_DEV"
}

cleanup_scsi_debug_dev() {
	teardown_devs
	rm -f SCSI_DEBUG_DEV LOOP
}

mdadm_create() {
	local devid
	local mddev

	which mdadm >/dev/null || skip "mdadm tool is missing!"

	cleanup_md_dev
	rm -f debug.log strace.log

	# try to find free MD node
	# using the old naming /dev/mdXXX
        # if we need more MD arrays test suite more likely leaked them
	for devid in {127..150} ; do
		grep -q "md${devid}" /proc/mdstat || break
	done
	test "$devid" -lt "150" || skip "Cannot find free /dev/mdXXX node!"
	mddev=/dev/md${devid}

	mdadm --create "$mddev" "$@" || {
		# Some older 'mdadm' version managed to open and close devices internaly
		# and reporting non-exclusive access on such device
		# let's just skip the test if this happens.
		# Note: It's pretty complex to get rid of consequences
		#       the following sequence avoid leaks on f19
		# TODO: maybe try here to recreate few times....
		mdadm --stop "$mddev" || true
		udev_wait
		while  [ "$#" -ne 0 ] ; do
			case "$1" in
			*"$PREFIX"*) mdadm --zero-superblock "$1" || true ;;
			esac
			shift
		done
		udev_wait
		skip "Test skipped, unreliable mdadm detected!"
	}

	for i in {10..0} ; do
		test -e "$mddev" && break
		echo "Waiting for $mddev."
		sleep .5
	done

	test -b "$mddev" || skip "mdadm has not created device!"
	echo "$mddev" > MD_DEV

	# LVM/DM will see this device
	case "$DM_DEV_DIR" in
	"/dev") echo "$mddev" > MD_DEV_PV ;;
	*)	rm -f "$DM_DEV_DIR/md${devid}"
		cp -LR "$mddev" "$DM_DEV_DIR"
		echo "${DM_DEV_DIR}/md${devid}" > MD_DEV_PV ;;
	esac

	rm -f MD_DEVICES
	while  [ "$#" -ne 0 ] ; do
		case "$1" in
		*"$PREFIX"*) echo "$1" >> MD_DEVICES ;;
		esac
		shift
	done
}

mdadm_assemble() {
	STRACE=
	[ "$DM_DEV_DIR" = "/dev" ] && mdadm -V 2>&1 | grep " v3.2" && {
		# use this 'trick' to slow down mdadm which otherwise
		# is racing with udev rule since mdadm internally
		# opens and closes raid leg devices in RW mode and then
		# tries to get exlusive access to the leg device during
		# insertion to kernel and fails during assembly
		# There can be some other affected version of mdadm.
		STRACE="strace -f -o /dev/null"
	}

	$STRACE mdadm --assemble "$@"
	udev_wait
}

cleanup_md_dev() {
	local IFS=$IFS_NL
	local i
	local dev
	local base
	local mddev

	test -f MD_DEV || return 0
	mddev=$(< MD_DEV)
	base=$(basename "$mddev")

	# try to find and remove any DM device on top of cleaned MD
	# assume  /dev/mdXXX is  9:MINOR
	local minor=${mddev##/dev/md}
	for i in $(dmsetup table | grep 9:"$minor" | cut -d: -f1) ; do
		dmsetup remove "$i" || {
			dmsetup --force remove "$i" || true
		}
	done

	for i in {0..10} ; do
		grep -q "$base" /proc/mdstat || break
		test "$i" = 0 || {
			sleep .1
			echo "$mddev is still present, stopping again"
			cat /proc/mdstat
		}
		mdadm --stop "$mddev" || true
		udev_wait  # wait till events are process, not zeroing to early
	done

	test "$DM_DEV_DIR" = "/dev" || rm -f "$(< MD_DEV_PV)"

	for dev in $(< MD_DEVICES); do
		mdadm --zero-superblock "$dev" 2>/dev/null || true
	done
	udev_wait
	rm -f MD_DEV MD_DEVICES MD_DEV_PV
}

wipefs_a() {
	local dev=$1
	local have_wipefs=1
	shift

	if test -n "$LVM_TEST_DEVICES_FILE"; then
		lvmdevices --deldev "$dev" || true
	fi

	if test -f HAVE_WIPEFS ; then
		have_wipefs=$(< HAVE_WIPEFS)
	else
		wipefs -V >/dev/null 2>&1 || have_wipefs=0
		echo "$have_wipefs" > HAVE_WIPEFS
	fi

	udev_wait
	if [ "$have_wipefs" = "1" ] ; then
		wipefs -a "$dev" || {
			echo "$dev: device in-use, retrying wipe again."
			sleep 1
			udev_wait
			wipefs -a "$dev"
		}
	else
		dd if=/dev/zero of="$dev" bs=4096 count=8 oflag=direct >/dev/null || true
		mdadm --zero-superblock "$dev" 2>/dev/null || true
	fi

	if test -n "$LVM_TEST_DEVICES_FILE"; then
		lvmdevices --adddev "$dev" || true
	fi

	udev_wait
}

cleanup_idm_context() {
	local dev=$1

	if [ -n "$LVM_TEST_LOCK_TYPE_IDM" ]; then
		sg_dev=$(sg_map26 "${dev}")
		echo "Cleanup IDM context for drive ${dev} ($sg_dev)"
		sg_raw -v -r 512 -o idm_tmp_data.bin "$sg_dev" \
			88 00 01 00 00 00 00 20 FF 01 00 00 00 01 00 00
		sg_raw -v -s 512 -i idm_tmp_data.bin "$sg_dev" \
			8E 00 FF 00 00 00 00 00 00 00 00 00 00 01 00 00
		rm idm_tmp_data.bin
	fi
}


#
# clear device either with blkdiscard -z or fallback to 'dd'
# $1  device_path
# TODO: add support for parametrized [OPTION] usage (Not usable ATM)
# TODO: -bs  blocksize  (defaults 512K)
# TODO: -count  count/length  (defaults to whole device, otherwise in BS units)
# TODO: -seek  offset/seek  (defaults 0, begining of zeroing area in BS unit)
clear_devs() {
	local bs=
	local count=
	local seek=

	while [ "$#" -ne 0 ] ; do
		case "$1" in
		"") ;;
		"--bs") bs=$2; shift ;;
		"--count") count=$2; shift ;;
		"--seek") seek=$2; shift ;;
		*TEST*) # Protection: only test devices with TEST in its path name can be zeroed
			test -e NO_BLKDISCARD_Z || {
				if blkdiscard -f -z "$1" ; then
					shift
					continue
				fi
				echo "Info: can't use 'blkdiscard -z' switch to 'dd'."
				touch NO_BLKDISCARD_Z
			}

			dd if=/dev/zero of="$1" bs=512K oflag=direct $seek $count || true
			;;
		esac
		shift
	done
}

#
# corrupt device content
# $1  file_path
# $2  string/pattern search for curruption
# $3  string/pattern replacing/corruptiong
corrupt_dev() {
	local a

	# search for string on a file
	# Note: returned string may possibly start with other ASCII chars
	# a[0] is position in file,  a[1] is the actual string
	a=( $(strings -t d -n 64 "$1" | grep -m 1 "$2") ) || true

	test -n "${a[0]-}" || return 0

	# Seek for the sequence and replace it with corruption pattern
	echo -n "${a[1]/$2/$3}" | dd of="$1" bs=1 seek="${a[0]}" conv=fdatasync
}

prepare_backing_dev() {
	local size=${1=32}
	shift

	if test -n "$LVM_TEST_BACKING_DEVICE"; then
		IFS=',' read -r -a BACKING_DEVICE_ARRAY <<< "$LVM_TEST_BACKING_DEVICE"

		for d in "${BACKING_DEVICE_ARRAY[@]}"; do
			if test ! -b "$d"; then
				echo "Device $d doesn't exist!"
				return 1
			fi
		done
	fi

	if test -f BACKING_DEV; then
		BACKING_DEV=$(< BACKING_DEV)
		return 0
	elif test -n "$LVM_TEST_BACKING_DEVICE"; then
		BACKING_DEV=${BACKING_DEVICE_ARRAY[0]}
		echo "$BACKING_DEV" > BACKING_DEV
		return 0
	elif test "${LVM_TEST_PREFER_BRD-1}" = "1" && \
	     test ! -d /sys/block/ram0 && \
	     kernel_at_least 4 16 0 && \
	     test "$size" -lt 16384; then
		# try to use ramdisk if possible, but for
		# big allocs (>16G) do not try to use ramdisk
		# Also we can't use BRD device prior kernel 4.16
		# since they were DAX based and lvm2 often relies
		# in save table loading between exiting backend device
		# and  bio-based 'error' device.
		# However with request based DAX brd device we get this:
		# device-mapper: ioctl: can't change device type after initial table load.
		prepare_ramdisk "$size" "$@" && return
		echo "(failed)"
	fi

	prepare_loop "$size" "$@"
}

prepare_devs() {
	local n=${1:-3}
	local devsize=${2:-34}
	local pvname=${3:-pv}
	local header_shift=1 # shift header from begin & end of device by 1MiB

	# sanlock requires more space for the internal sanlock lv
	# This could probably be lower, but what are the units?
	if test -n "$LVM_TEST_LOCK_TYPE_SANLOCK" ; then
		devsize=1024
	fi

	touch DEVICES
	prepare_backing_dev $(( n * devsize + 2 * header_shift ))
	blkdiscard "$BACKING_DEV" 2>/dev/null || true
	echo -n "## preparing $n devices..."

	local size=$(( devsize * 2048 )) # sectors
	local count=0
	rm -f CREATE_FAILED
	init_udev_transaction
	for i in $(seq 1 "$n"); do
		local name="${PREFIX}$pvname$i"
		local dev="$DM_DEV_DIR/mapper/$name"
		DEVICES[count]=$dev
		count=$((  count + 1 ))
		# If the backing device number can meet the requirement for PV devices,
		# then allocate a dedicated backing device for PV; otherwise, rollback
		# to use single backing device for device-mapper.
		if [ -n "$LVM_TEST_BACKING_DEVICE" ] && [ "$n" -le ${#BACKING_DEVICE_ARRAY[@]} ]; then
			echo 0 $size linear "${BACKING_DEVICE_ARRAY[$(( count - 1 ))]}" $(( header_shift * 2048 )) > "$name.table"
		else
			echo 0 $size linear "$BACKING_DEV" $(( ( i - 1 ) * size + ( header_shift * 2048 ) )) > "$name.table"
		fi
		dmsetup create -u "TEST-$name" "$name" "$name.table" || touch CREATE_FAILED &
		test -f CREATE_FAILED && break;
	done
	wait
	finish_udev_transaction

	if test -f CREATE_FAILED ; then
		if test -z "$LVM_TEST_BACKING_DEVICE"; then
			echo "failed"
			return 1
		fi
		LVM_TEST_BACKING_DEVICE=
		rm -f BACKING_DEV CREATE_FAILED
		prepare_devs "$@"
		return $?
	fi

	if [ -n "$LVM_TEST_BACKING_DEVICE" ]; then
		for d in "${BACKING_DEVICE_ARRAY[@]}"; do
			cnt=$(( $(blockdev --getsize64 "$d") / 1024 / 1024 ))
			cnt=$(( cnt < 1000 ? cnt : 1000 ))
			dd if=/dev/zero of="$d" bs=1MB count=$cnt
			wipefs -a "$d" 2>/dev/null || true
			cleanup_idm_context "$d"
		done
	fi

	# non-ephemeral devices need to be cleared between tests
	test -f LOOP -o -f RAMDISK || for d in "${DEVICES[@]}"; do
		# ensure disk header is always zeroed
		dd if=/dev/zero of="$d" bs=32k count=1
		wipefs -a "$d" 2>/dev/null || true
	done

	if test -n "$LVM_TEST_DEVICES_FILE"; then
		mkdir -p "$TESTDIR/etc/lvm/devices" || true
		rm "$TESTDIR/etc/lvm/devices/system.devices" || true
		touch "$TESTDIR/etc/lvm/devices/system.devices"
		for d in "${DEVICES[@]}"; do
			lvmdevices --adddev "$d" || true
		done
	fi

	#for i in `seq 1 $n`; do
	#	local name="${PREFIX}$pvname$i"
	#	dmsetup info -c $name
	#done
	#for i in `seq 1 $n`; do
	#	local name="${PREFIX}$pvname$i"
	#	dmsetup table $name
	#done

	printf "%s\\n" "${DEVICES[@]}" > DEVICES
#	( IFS=$'\n'; echo "${DEVICES[*]}" ) >DEVICES
	echo "ok"
}

common_dev_() {
	local tgtype=$1
	local dev=$2
	local name=${dev##*/}
	shift 2
	local read_ms=${1-0}
	local write_ms=${2-0}

	case "$tgtype" in
	delay)
		test "$read_ms" -eq 0 && test "$write_ms" -eq 0 && {
			# zero delay is just equivalent to 'enable_dev'
			enable_dev "$dev"
			return
		}
		shift 2
		;;
	delayzero)
		shift 2
		# zero delay is just equivalent to 'zero_dev'
		test "$read_ms" -eq 0 && test "$write_ms" -eq 0 && tgtype="zero"
		;;
	# error|zero target does not take read_ms & write_ms only offset list
	esac

	local pos
	local size
	local type
	local pvdev
	local offset

	read -r pos size type pvdev offset < "$name.table"

	for fromlen in "${@-0:}"; do
		from=${fromlen%%:*}
		len=${fromlen##*:}
		if test "$len" = "$fromlen"; then
			# Missing the colon at the end: empty len
			len=
		fi
		test -n "$len" || len=$(( size - from ))
		diff=$(( from - pos ))
		if test $diff -gt 0 ; then
			echo "$pos $diff $type $pvdev $(( pos + offset ))"
			pos=$(( pos + diff ))
		elif test $diff -lt 0 ; then
			die "Position error"
		fi

		case "$tgtype" in
		delay)
			echo "$from $len delay $pvdev $(( pos + offset )) $read_ms $pvdev $(( pos + offset )) $write_ms" ;;
		writeerror)
			echo "$from $len delay $pvdev $(( pos + offset )) 0 $(cat ERR_DEV) 0 0" ;;
		delayzero)
			echo "$from $len delay $(cat ZERO_DEV) 0 $read_ms $(cat ZERO_DEV) 0 $write_ms" ;;
		error|zero)
			echo "$from $len $tgtype" ;;
		esac
		pos=$(( pos + len ))
	done > "$name.devtable"
	diff=$(( size - pos ))
	test "$diff" -gt 0 && echo "$pos $diff $type $pvdev $(( pos + offset ))" >>"$name.devtable"

	restore_from_devtable "$dev"
}

# Replace linear PV device with its 'delayed' version
# Could be used to more deterministicaly hit some problems.
# Parameters: {device path} [read delay ms] [write delay ms] [offset[:[size]]]...
# Original device is restored when both delay params are 0 (or missing).
# If the size is missing, the remaing portion of device is taken
# i.e.  delay_dev "$dev1" 0 200 256:
delay_dev() {
	if test ! -f HAVE_DM_DELAY ; then
		target_at_least dm-delay 1 1 0 || return 0
		touch HAVE_DM_DELAY
	fi
	common_dev_ delay "$@"
}

disable_dev() {
	local dev
	local silent=""
	local error=""
	local notify=""

	while test -n "$1"; do
	    if test "$1" = "--silent"; then
		silent=1
		shift
	    elif test "$1" = "--error"; then
		error=1
		shift
	    else
		break
	    fi
	done

	udev_wait
	for dev in "$@"; do
		maj=$(($(stat -L --printf=0x%t "$dev")))
		min=$(($(stat -L --printf=0x%T "$dev")))
		echo "Disabling device $dev ($maj:$min)"
		notify="$notify $maj:$min"
		if test -n "$error"; then
		    echo 0 10000000 error | dmsetup load "$dev"
		    dmsetup resume "$dev"
		else
		    dmsetup remove -f "$dev" 2>/dev/null || true
		fi
	done
}

enable_dev() {
	local dev
	local silent=""

	if test "$1" = "--silent"; then
	    silent=1
	    shift
	fi

	rm -f debug.log strace.log
	init_udev_transaction
	for dev in "$@"; do
		local name=${dev##*/}
		dmsetup create -u "TEST-$name" "$name" "$name.table" 2>/dev/null || \
			dmsetup load "$name" "$name.table"
		# using device name (since device path does not exists yes with udev)
		dmsetup resume "$name"
	done
	finish_udev_transaction
}

# Try to remove list of DM device from table
remove_dm_devs() {
	local remove=( "$@" )
	local held
	local i

	for i in {1..50}; do
		held=()
		for d in "${remove[@]}" ; do
			dmsetup remove "$d" 2>/dev/null || {
				dmsetup info -c "$d" 2>/dev/null && {
					held+=( "$d" )
					dmsetup status "$d"
				}
			}
		done
		test ${#held[@]} -eq 0 && {
		        rm -f debug.log*
			return
		}
		remove=( "${held[@]}" )
	done
	die "Can't remove device(s) ${held[*]}"
}

# Throttle down performance of kcopyd when mirroring i.e. disk image
throttle_sys="/sys/module/dm_mirror/parameters/raid1_resync_throttle"
throttle_dm_mirror() {
	# if the kernel config file is present, validate whether the kernel uses HZ_1000
	# and return failure for this 'throttling' when it does NOT as without this setting
	# whole throttling is pointless on modern hardware
	local kconfig

	kconfig="/boot/config-$(uname -r)"
	if test -e "$kconfig" ; then
		grep -q "CONFIG_HZ_1000=y" "$kconfig" 2>/dev/null || {
			echo "WARNING: CONFIG_HZ_1000=y is NOT set in $kconfig -> throttling is unusable"
			return 1
		}
	fi
	test -e "$throttle_sys" || return
	test -f THROTTLE || cat "$throttle_sys" > THROTTLE
	echo "${1-1}" > "$throttle_sys"
}

# Restore original kcopyd throttle value and have mirroring fast again
restore_dm_mirror() {
	test ! -f THROTTLE || {
		cat THROTTLE > "$throttle_sys"
		rm -f THROTTLE
	}
}


# Once there is $name.devtable
# this is a quick way to restore to this table entry
restore_from_devtable() {
	local dev
	local silent=""

	if test "$1" = "--silent"; then
	    silent=1
	    shift
	fi

	rm -f debug.log strace.log
	init_udev_transaction
	for dev in "$@"; do
		local name=${dev##*/}
		dmsetup load "$name" "$name.devtable"
		if not dmsetup resume "$name" ; then
			dmsetup clear "$name"
			dmsetup resume "$name"
			finish_udev_transaction
			echo "Device $name has unusable table \"$(cat "$name.devtable")\""
			return 1
		fi
	done
	finish_udev_transaction
}

#
# Convert device to device with errors
# Takes the list of pairs of error segment from:len
# Combination with zero or delay is unsupported
# Original device table is replaced with multiple lines
# i.e.  error_dev "$dev1" 8:32 96:8
error_dev() {
	common_dev_ error "$@"
}

#
# Convert device to device with write errors but normal reads.
# For this 'delay' dev is used and reroutes 'reads' back to original device
# and for writes it will use extra new TEST-errordev (huge error target)
# i.e.  writeerror_dev "$dev1" 8:32
writeerror_dev() {
	local name=${PREFIX}-errordev

	if test ! -e ERR_DEV; then
		# delay target is used for error mapping
		if test ! -f HAVE_DM_DELAY ; then
			target_at_least dm-delay 1 1 0 || return 0
			touch HAVE_DM_DELAY
		fi
		dmsetup create -u "TEST-$name" "$name" --table "0 4611686018427387904 error"
		# Take major:minor of our error device
		echo "$name" > ERR_DEV_NAME
		dmsetup info -c  --noheadings -o major,minor "$name" > ERR_DEV
	fi

	common_dev_ writeerror "$@"
}

#
# Convert device to device with sections of delayed zero read and writes.
# For this 'delay' dev will use extra new TEST-zerodev (huge zero target)
# and reroutes reads and writes
# i.e.  delayzero_dev "$dev1" 8:32
delayzero_dev() {
	local name=${PREFIX}-zerodev

	if test ! -e ZERO_DEV; then
		# delay target is used for error mapping
		if test ! -f HAVE_DM_DELAY ; then
			target_at_least dm-delay 1 1 0 || return 0
			touch HAVE_DM_DELAY
		fi
		dmsetup create -u "TEST-$name" "$name" --table "0 4611686018427387904 zero"
		# Take major:minor of our error device
		echo "$name" > ZERO_DEV_NAME
		dmsetup info -c  --noheadings -o major,minor "$name" > ZERO_DEV
	fi

	common_dev_ delayzero "$@"
}

#
# Convert existing device to a device with zero segments
# Takes the list of pairs of zero segment from:len
# Combination with error or delay is unsupported
# Original device table is replaced with multiple lines
# i.e.  zero_dev "$dev1" 8:32 96:8
zero_dev() {
	common_dev_ zero "$@"
}

backup_dev() {
	local dev

	for dev in "$@"; do
		dd if="$dev" of="${dev##*/}.backup" bs=16K conv=fdatasync || \
			die "Cannot backup device: \"$dev\"  with size $(blockdev --getsize64 "$dev" || true) bytes."
	done
}

restore_dev() {
	local dev

	for dev in "$@"; do
		test -e "${dev##*/}.backup" || \
			die "Internal error: $dev not backed up, can't restore!"
		dd of="$dev" if="${dev##*/}.backup" bs=16K
	done
}

prepare_pvs() {
	prepare_devs "$@"
	pvcreate -ff "${DEVICES[@]}"
}

prepare_vg() {
	teardown_devs

	prepare_devs "$@"
	vgcreate $SHARED -s 512K "$vg" "${DEVICES[@]}"
}

extend_devices() {
	test -z "$LVM_TEST_DEVICES_FILE" && return

	for dev in "$@"; do
		lvmdevices --adddev "$dev"
	done
}

extend_filter() {
	local filter

	test -n "$LVM_TEST_DEVICES_FILE" && return

	filter=$(grep ^devices/global_filter CONFIG_VALUES | tail -n 1)
	for rx in "$@"; do
		filter=$(echo "$filter" | sed -e "s:\\[:[ \"$rx\", :")
	done
	lvmconf "$filter" "devices/scan_lvs = 1"
}

extend_filter_md() {
	local filter

	test -n "$LVM_TEST_DEVICES_FILE" && return

	filter=$(grep ^devices/global_filter CONFIG_VALUES | tail -n 1)
	for rx in "$@"; do
		filter=$(echo "$filter" | sed -e "s:\\[:[ \"$rx\", :")
	done
	lvmconf "$filter"
	lvmconf "devices/scan = [ \"$DM_DEV_DIR\", \"/dev\" ]"
}

extend_filter_LVMTEST() {
	extend_filter "a|$DM_DEV_DIR/$PREFIX|" "$@"
}

hide_dev() {
	local filter

	if test -n "$LVM_TEST_DEVICES_FILE"; then
		for dev in "$@"; do
			lvmdevices --deldev "$dev"
		done
	else
		filter=$(grep ^devices/global_filter CONFIG_VALUES | tail -n 1)
		for dev in "$@"; do
			filter=$(echo "$filter" | sed -e "s:\\[:[ \"r|$dev|\", :")
		done
		lvmconf "$filter"
	fi
}

unhide_dev() {
	local filter

	if test -n "$LVM_TEST_DEVICES_FILE"; then
		for dev in "$@"; do
			lvmdevices -y --adddev "$dev"
		done
	else
		filter=$(grep ^devices/global_filter CONFIG_VALUES | tail -n 1)
		for dev in "$@"; do
			filter=$(echo "$filter" | sed -e "s:\"r|$dev|\", ::")
		done
		lvmconf "$filter"
	fi
}

mkdev_md5sum() {
	rm -f debug.log strace.log
	mkfs.ext2 "$DM_DEV_DIR/$1/$2" || return 1
	md5sum "$DM_DEV_DIR/$1/$2" > "md5.$1-$2"
}

generate_config() {
	if test -n "$profile_name"; then
		config_values="PROFILE_VALUES_$profile_name"
		config="PROFILE_$profile_name"
		touch "$config_values"
	else
		config_values=CONFIG_VALUES
		config=CONFIG
	fi

	LVM_TEST_LOCKING=${LVM_TEST_LOCKING:-1}
	LVM_TEST_LVMPOLLD=${LVM_TEST_LVMPOLLD:-0}
	LVM_TEST_LVMLOCKD=${LVM_TEST_LVMLOCKD:-0}
	LVM_TEST_DEVICES_FILE=${LVM_TEST_DEVICES_FILE:-0}
        # FIXME:dct: This is harmful! Variables are unused here and are tested not being empty elsewhere:
	#LVM_TEST_LOCK_TYPE_SANLOCK=${LVM_TEST_LOCK_TYPE_SANLOCK:-0}
	#LVM_TEST_LOCK_TYPE_DLM=${LVM_TEST_LOCK_TYPE_DLM:-0}
	if test "$DM_DEV_DIR" = "/dev"; then
	    LVM_VERIFY_UDEV=${LVM_VERIFY_UDEV:-0}
	else
	    LVM_VERIFY_UDEV=${LVM_VERIFY_UDEV:-1}
	fi
	test -f "$config_values" || {
            cat > "$config_values" <<-EOF
activation/checks = 1
activation/monitoring = 0
activation/polling_interval = 1
activation/retry_deactivation = 1
activation/snapshot_autoextend_percent = 50
activation/snapshot_autoextend_threshold = 50
activation/verify_udev_operations = $LVM_VERIFY_UDEV
activation/raid_region_size = 512
allocation/wipe_signatures_when_zeroing_new_lvs = 0
allocation/vdo_slab_size_mb = 128
allocation/zero_metadata = 0
backup/archive = 0
backup/backup = 0
devices/cache_dir = "$TESTDIR/etc"
devices/default_data_alignment = 1
devices/dir = "$DM_DEV_DIR"
devices/md_component_detection = 0
devices/scan = "$DM_DEV_DIR"
devices/sysfs_scan = 1
devices/write_cache_state = 0
devices/use_devicesfile = $LVM_TEST_DEVICES_FILE
devices/filter = "a|.*|"
devices/global_filter = [ "a|$DM_DEV_DIR/mapper/${PREFIX}.*pv[0-9_]*$|", "r|.*|" ]
global/abort_on_internal_errors = 1
global/cache_check_executable = "$LVM_TEST_CACHE_CHECK_CMD"
global/cache_dump_executable = "$LVM_TEST_CACHE_DUMP_CMD"
global/cache_repair_executable = "$LVM_TEST_CACHE_REPAIR_CMD"
global/detect_internal_vg_cache_corruption = 1
global/fallback_to_local_locking = 0
global/locking_type=$LVM_TEST_LOCKING
global/notify_dbus = 0
global/si_unit_consistency = 1
global/thin_check_executable = "$LVM_TEST_THIN_CHECK_CMD"
global/thin_dump_executable = "$LVM_TEST_THIN_DUMP_CMD"
global/thin_repair_executable = "$LVM_TEST_THIN_REPAIR_CMD"
global/use_lvmpolld = $LVM_TEST_LVMPOLLD
global/use_lvmlockd = $LVM_TEST_LVMLOCKD
log/activation = 1
log/file = "$TESTDIR/debug.log"
log/indent = 1
log/level = 9
log/overwrite = 1
log/syslog = 0
log/verbose = 0
EOF
		# For 'rpm' builds use system installed binaries
		# and libraries and locking dir and some more built-in
		# defaults
		# For test suite run use binaries from builddir.
		test -z "${abs_top_builddir+varset}" || {
			cat >> "$config_values" <<-EOF
dmeventd/executable = "$abs_top_builddir/test/lib/dmeventd"
activation/udev_rules = 1
activation/udev_sync = 1
global/fsadm_executable = "$abs_top_builddir/test/lib/fsadm"
global/library_dir = "$TESTDIR/lib"
global/locking_dir = "$TESTDIR/var/lock/lvm"
EOF
		}
	}

	# append all parameters  (avoid adding empty \n)
	local v
	test $# -gt 0 && printf "%s\\n" "$@" >> "$config_values"

	declare -A CONF 2>/dev/null || {
		# Associative arrays is not available
		local s
		for s in $(cut -f1 -d/ "$config_values" | sort | uniq); do
			echo "$s {"
			local k
			for k in $(grep ^"$s"/ "$config_values" | cut -f1 -d= | sed -e 's, *$,,' | sort | uniq); do
				grep "^${k}[ \t=]" "$config_values" | tail -n 1 | sed -e "s,^$s/,	 ," || true
			done
			echo "}"
			echo
		done | tee "$config" | sed -e "s,^,## LVMCONF: ,"
		return 0
	}

	local sec
	local last_sec=""

	# read sequential list and put into associative array
	while IFS= read -r v; do
		CONF["${v%%[={ ]*}"]=${v#*/}
	done < "$config_values"

	# sort by section and iterate through them
	printf "%s\\n" "${!CONF[@]}" | sort | while read -r v ; do
		sec=${v%%/*} # split on section'/'param_name
		test "$sec" = "$last_sec" || {
			test -z "$last_sec" || echo "}"
			echo "$sec {"
			last_sec=$sec
		}
		echo "    ${CONF[$v]}"
	done > "$config"
	echo "}" >> "$config"

	sed -e "s,^,## LVMCONF: ," "$config"
}

lvmconf() {
	local profile_name=""
	test $# -eq 0 || {
		# Compare if passed args aren't already all in generated lvm.conf
		local needed=0
		for i in "$@"; do
			val=$(grep "${i%%[={ ]*}" CONFIG_VALUES 2>/dev/null | tail -1) || { needed=1; break; }
			test "$val" = "$i" || { needed=1; break; }
		done
		test "$needed" -eq 0 && {
			echo "## Skipping reconfiguring for: (" "$@" ")"
			return 0 # not needed
		}
	}
	generate_config "$@"
	mv -f CONFIG "$LVM_SYSTEM_DIR/lvm.conf"
}

profileconf() {
	local pdir="$LVM_SYSTEM_DIR/profile"
	local profile_name=$1
	shift
	generate_config "$@"
	mkdir -p "$pdir"
	mv -f "PROFILE_$profile_name" "$pdir/$profile_name.profile"
}

prepare_profiles() {
	local pdir="$LVM_SYSTEM_DIR/profile"
	local profile_name
	mkdir -p "$pdir"
	for profile_name in "$@"; do
		test -L "lib/$profile_name.profile" || skip
		cp "lib/$profile_name.profile" "$pdir/$profile_name.profile"
	done
}

unittest() {
	test -x "$TESTOLDPWD/unit/unit-test" || skip
	"$TESTOLDPWD/unit/unit-test" "${@}"
}

mirror_recovery_works() {
	case "$(uname -r)" in
	  3.3.4-5.fc17.i686|3.3.4-5.fc17.x86_64) return 1 ;;
	esac
}

raid456_replace_works() {
# The way kmem_cache aliasing is done in the kernel is broken.
# It causes RAID 4/5/6 tests to fail.
#
# The problem with kmem_cache* is this:
# *) Assume CONFIG_SLUB is set
# 1) kmem_cache_create(name="foo-a")
# - creates new kmem_cache structure
# 2) kmem_cache_create(name="foo-b")
# - If identical cache characteristics, it will be merged with the previously
#   created cache associated with "foo-a".  The cache's refcount will be
#   incremented and an alias will be created via sysfs_slab_alias().
# 3) kmem_cache_destroy(<ptr>)
# - Attempting to destroy cache associated with "foo-a", but instead the
#   refcount is simply decremented.  I don't even think the sysfs aliases are
#   ever removed...
# 4) kmem_cache_create(name="foo-a")
# - This FAILS because kmem_cache_sanity_check colides with the existing
#   name ("foo-a") associated with the non-removed cache.
#
# This is a problem for RAID (specifically dm-raid) because the name used
# for the kmem_cache_create is ("raid%d-%p", level, mddev).  If the cache
# persists for long enough, the memory address of an old mddev will be
# reused for a new mddev - causing an identical formulation of the cache
# name.  Even though kmem_cache_destory had long ago been used to delete
# the old cache, the merging of caches has cause the name and cache of that
# old instance to be preserved and causes a colision (and thus failure) in
# kmem_cache_create().  I see this regularly in testing the following
# kernels:
#
# This seems to be finaly resolved with this patch:
# http://www.redhat.com/archives/dm-devel/2014-March/msg00008.html
# so we need to put here exlusion for kernes which do trace SLUB
#
	case "$(uname -r)" in
	  3.6.*.fc18.i686*|3.6.*.fc18.x86_64) return 1 ;;
	  3.9.*.fc19.i686*|3.9.*.fc19.x86_64) return 1 ;;
	  3.1[0123].*.fc18.i686*|3.1[0123].*.fc18.x86_64) return 1 ;;
	  3.1[01234].*.fc19.i686*|3.1[01234].*.fc19.x86_64) return 1 ;;
	  3.1[123].*.fc20.i686*|3.1[123].*.fc20.x86_64) return 1 ;;
	  3.14.*.fc21.i686*|3.14.*.fc21.x86_64) return 1 ;;
	  3.15.*rc6*.fc21.i686*|3.15.*rc6*.fc21.x86_64) return 1 ;;
	  3.16.*rc4*.fc21.i686*|3.16.*rc4*.fc21.x86_64) return 1 ;;
	esac
}

#
# Some 32bit kernel cannot pass some erroring magic which forces
# thin-pool to be falling into Error state.
#
# Skip test on such kernels (see: https://bugzilla.redhat.com/1310661)
#
thin_pool_error_works_32() {
	case "$(uname -r)" in
	  2.6.32-618.*.i686) return 1 ;;
	  2.6.32-623.*.i686) return 1 ;;
	  2.6.32-573.1[28].1.el6.i686) return 1 ;;
	esac
}

thin_restore_needs_more_volumes() {
	case $("$LVM_TEST_THIN_RESTORE_CMD" -V) in
		# With older version of thin-tool we got slightly more compact metadata
		0.[0-6]*|0.7.0*) return 0 ;;
		0.8.5-2.el7) return 0 ;;
	esac
	return 1
}

udev_wait() {
	pgrep udev >/dev/null || return 0
	which udevadm &>/dev/null || return 0
	if test -n "${1-}" ; then
		udevadm settle --exit-if-exists="$1" 2>/dev/null || true
	else
		udevadm settle --timeout=15 2>/dev/null || true
	fi
}

# wait_for_sync <VG/LV>
wait_for_sync() {
	local i
	for i in {1..100} ; do
		check in_sync "$@" && return
		sleep .2
	done

	echo "Sync is taking too long - assume stuck"
	echo t >/proc/sysrq-trigger 2>/dev/null
	return 1
}

wait_recalc() {
	local checklv=$1

	for i in {1..100} ; do
		sync=$(get lv_field "$checklv" sync_percent | cut -d. -f1)
		echo "sync_percent is $sync"

		test "$sync" = "100" && return

		sleep .1
	done

	# TODO: There is some strange bug, first leg of RAID with integrity
	# enabled never gets in sync. I saw this in BB, but not when executing
	# the commands manually
#	if test -z "$sync"; then
#		echo "TEST\ WARNING: Resync of dm-integrity device '$checklv' failed"
#                dmsetup status "$DM_DEV_DIR/mapper/${checklv/\//-}"
#		exit
#	fi
	echo "Timeout waiting for recalc"
	dmsetup status "$DM_DEV_DIR/mapper/${checklv/\//-}"
	return 1
}

# Check if tests are running on 64bit architecture
can_use_16T() {
	test "$(getconf LONG_BIT)" -eq 64
}

# Check if major.minor.revision' string is 'at_least'
version_at_least() {
	local major
	local minor
	local revision
	IFS=".-" read -r major minor revision <<< "$1"
	shift

	test -n "${1:-}" || return 0
	test -n "$major" || return 1
	test "$major" -gt "$1" && return 0
	test "$major" -eq "$1" || return 1

	test -n "${2:-}" || return 0
	test -n "$minor" || return 1
	test "$minor" -gt "$2" && return 0
	test "$minor" -eq "$2" || return 1

	test -n "${3:-}" || return 0
	test "$revision" -ge "$3" 2>/dev/null || return 1
}
#
# Check wheter kernel [dm module] target exist
# at least in expected version
#
# [dm-]target-name major minor revision
#
# i.e.   dm_target_at_least  dm-thin-pool  1 0
target_at_least() {
	rm -f debug.log strace.log
	case "$1" in
	  dm-vdo) modprobe "kvdo" || true ;;
	  dm-*) modprobe "$1" || true ;;
	esac

	if test "$1" = dm-raid; then
		case "$(uname -r)" in
		  3.12.0*) return 1 ;;
		esac
	fi

	local version
	version=$(dmsetup targets 2>/dev/null | grep "^${1##dm-} " 2>/dev/null)
	version=${version##* v}

	version_at_least "$version" "${@:2}" || {
		echo "Found $1 version $version, but requested ${*:2}." >&2
		return 1
	}
}

# Check whether the kernel driver version is greater or equal
# to the specified version. This can be used to skip tests on
# kernels where they are known to not be supported.
#
# e.g. driver_at_least 4 33
#
driver_at_least() {
	local version
	version=$(dmsetup version | tail -1 2>/dev/null)
	version=${version##*:}
	version_at_least "$version" "$@" || {
		echo "Found driver version $version, but requested" "$@" "." >&2
		return 1
	}
}

have_thin() {
	lvm segtypes 2>/dev/null | grep thin$ >/dev/null || {
		echo "Thin is not built-in." >&2
		return 1
	}
	target_at_least dm-thin-pool "$@"

	declare -a CONF=()
	# disable thin_check if not present in system
	if test -n "$LVM_TEST_THIN_CHECK_CMD" && test ! -x "$LVM_TEST_THIN_CHECK_CMD"; then
		CONF[0]="global/thin_check_executable = \"\""
	fi
	if test -n "$LVM_TEST_THIN_DUMP_CMD" && test ! -x "$LVM_TEST_THIN_DUMP_CMD"; then
		CONF[1]="global/thin_dump_executable = \"\""
	fi
	if test -n "$LVM_TEST_THIN_REPAIR_CMD" && test ! -x "$LVM_TEST_THIN_REPAIR_CMD"; then
		CONF[2]="global/thin_repair_executable = \"\""
	fi
	if test ${#CONF[@]} -ne 0 ; then
		echo "TEST WARNING: Reconfiguring" "${CONF[@]}"
		lvmconf "${CONF[@]}"
	fi
}

have_vdo() {
	lvm segtypes 2>/dev/null | grep 'vdo$' >/dev/null || {
		echo "VDO is not built-in." >&2
		return 1
	}
	target_at_least dm-vdo "$@"
}

have_writecache() {
	lvm segtypes 2>/dev/null | grep 'writecache$' >/dev/null || {
		echo "writecache is not built-in." >&2
		return 1
	}
	target_at_least dm-writecache "$@"
}

have_integrity() {
	lvm segtypes 2>/dev/null | grep 'integrity$' >/dev/null || {
		echo "integrity is not built-in." >&2
		return 1
	}
	target_at_least dm-integrity "$@"
}

have_raid() {
	target_at_least dm-raid "$@"

	# some kernels have broken mdraid bitmaps, don't use them!
	# may oops kernel, we know for sure all FC24 are currently broken
	# in general any 4.1, 4.2 is likely useless unless patched
	case "$(uname -r)" in
	  4.[12].*fc24*) return 1 ;;
	esac
}

have_raid4 () {
	local r=0

	have_raid 1 8 0 && r=1
	have_raid 1 9 1 && r=0

	return $r
}

have_cache() {
	lvm segtypes 2>/dev/null | grep ' cache-pool$' >/dev/null || {
		echo "Cache is not built-in." >&2
		return 1
	}
	target_at_least dm-cache "$@"

	declare -a CONF=()
	# disable cache_check if not present in system
	if test -n "$LVM_TEST_CACHE_CHECK_CMD" && test ! -x "$LVM_TEST_CACHE_CHECK_CMD" ; then
		CONF[0]="global/cache_check_executable = \"\""
	fi
	if test -n "$LVM_TEST_CACHE_DUMP_CMD" && test ! -x "$LVM_TEST_CACHE_DUMP_CMD" ; then
		CONF[1]="global/cache_dump_executable = \"\""
	fi
	if test -n "$LVM_TEST_CACHE_REPAIR_CMD" && test ! -x "$LVM_TEST_CACHE_REPAIR_CMD" ; then
		CONF[2]="global/cache_repair_executable = \"\""
	fi
	if test ${#CONF[@]} -ne 0 ; then
		echo "TEST WARNING: Reconfiguring" "${CONF[@]}"
		lvmconf "${CONF[@]}"
	fi
}

have_tool_at_least() {
	local version
	version=$("$1" -V 2>/dev/null)
	version=${version%%-*}
	shift

	version_at_least "$version" "$@"
}

# check if lvm shell is build-in  (needs readline)
have_readline() {
	echo version | lvm &>/dev/null
}

have_multi_core() {
	which nproc &>/dev/null || return 0
	[ "$(nproc)" -ne 1 ]
}

dmsetup_wrapped() {
	udev_wait
	dmsetup "$@"
}

awk_parse_init_count_in_lvmpolld_dump() {
	printf '%s' \
	\
	$'BEGINFILE { x=0; answ=0 }' \
	$'{' \
		$'if (/.*{$/) { x++ }' \
		$'else if (/.*}$/) { x-- }' \
		$'else if ( x == 2 && $1 ~ "[[:space:]]*"vkey) { value=substr($2, 2); value=substr(value, 1, length(value) - 1); }' \
		$'if ( x == 2 && value == vvalue && $1 ~ /[[:space:]]*init_requests_count/) { answ=$2 }' \
		$'if (answ > 0) { exit 0 }' \
	$'}' \
	$'END { printf "%d", answ }'
}

check_lvmpolld_init_rq_count() {
	local ret
	ret=$(awk -v vvalue="$2" -v vkey="${3:-lvname}" -F= "$(awk_parse_init_count_in_lvmpolld_dump)" lvmpolld_dump.txt)
	test "$ret" -eq "$1" || {
		die "check_lvmpolld_init_rq_count failed. Expected $1, got $ret"
	}
}

wait_pvmove_lv_ready() {
	# given sleep .1 this is about 20 secs of waiting
	local lvid=()
	local all

	for i in {100..0}; do
		if [ -e LOCAL_LVMPOLLD ]; then
			if test "${#lvid[@]}" -eq "$#" ; then
				lvmpolld_dump > lvmpolld_dump.txt
				all=1
				for l in "${lvid[@]}" ; do
					check_lvmpolld_init_rq_count 1 "${l##LVM-}" lvid || all=0
				done
				test "$all" = 1 && return
			else
				# wait till wanted LV really appears
				lvid=( $(dmsetup info --noheadings -c -o uuid "$@" 2>/dev/null) ) || true
			fi
		else
			dmsetup info -c --noheadings -o tables_loaded "$@" >out 2>/dev/null || true
			test "$(grep -c Live out)" = "$#" && return
		fi
		sleep .1
	done

	test -e LOCAL_LVMPOLLD && die "Waiting for lvmpolld timed out"
	die "Waiting for pvmove LV to get activated has timed out"
}

# Holds device open with sleep which automatically expires after given timeout
# Prints  PID of running holding sleep process in background
hold_device_open() {
	local vgname=$1
	local lvname=$2
	local sec=${3-20} # default 20sec

	sleep "$sec" < "$DM_DEV_DIR/$vgname/$lvname" >/dev/null 2>&1 &
	SLEEP_PID=$!
	# wait till device is openned
	for i in $(seq 1 50) ; do
		if test "$(dmsetup info --noheadings -c -o open "$vgname"-"$lvname")" -ne 0 ; then
			echo "$SLEEP_PID"
			return
		fi
		sleep .1
	done

	die "$vgname-$lvname expected to be openned, but it's not!"
}

# return total memory size in kB units
total_mem() {
	local a
	local b

	while IFS=":" read -r a b ; do
		case "$a" in MemTotal*) echo "${b%% kB}" ; break ;; esac
	done < /proc/meminfo
}

kernel_at_least() {
	version_at_least "$(uname -r)" "$@"
}

test "${LVM_TEST_AUX_TRACE-0}" = "0" || set -x

test -f DEVICES && devs=$(< DEVICES)

if test "$1" = "dmsetup" ; then
    shift
    dmsetup_wrapped "$@"
else
    "$@"
fi