mirror of
git://sourceware.org/git/lvm2.git
synced 2025-10-01 21:44:22 +03:00
Compare commits
807 Commits
v2_02_152
...
dev-dct-cm
Author | SHA1 | Date | |
---|---|---|---|
|
5b5e37791c | ||
|
e916797ec2 | ||
|
51205a1bc0 | ||
|
a7840b31a1 | ||
|
4f60bcd9f2 | ||
|
6c119560ab | ||
|
2cf8f93fe6 | ||
|
d658ddfc70 | ||
|
268374c235 | ||
|
45e23131b8 | ||
|
6de05cf5f5 | ||
|
cd0736a945 | ||
|
68d6d342f8 | ||
|
e7da8e7e1f | ||
|
a7691cdebb | ||
|
6db5b91231 | ||
|
1bdcb01f63 | ||
|
b38564b8dc | ||
|
de3d054f78 | ||
|
4a2250f9ce | ||
|
d8fc4d093e | ||
|
e54cce245f | ||
|
59b29716e5 | ||
|
f8b3b0bc9a | ||
|
b11f4f93d7 | ||
|
0b1c796420 | ||
|
c1862ea84c | ||
|
2ec3e7dca8 | ||
|
ada5733c56 | ||
|
9e03fc3c2a | ||
|
eed708dbd9 | ||
|
6dce0e9489 | ||
|
461d340bd7 | ||
|
ee0c9e7b23 | ||
|
a9ee86ccf2 | ||
|
4e26024add | ||
|
0d95082aa9 | ||
|
9cbe4c1af9 | ||
|
cc19cc07f7 | ||
|
e3775173b4 | ||
|
ee13f265f0 | ||
|
221d8ff2a4 | ||
|
28b210f4fa | ||
|
1db4b81d5a | ||
|
4b4d19e3aa | ||
|
cd468b6218 | ||
|
33dd1f7747 | ||
|
e50d434a35 | ||
|
6af26273cb | ||
|
96118a2508 | ||
|
95abadd13c | ||
|
60de09b00c | ||
|
38dd79307a | ||
|
24803bbaad | ||
|
c8e8439b3d | ||
|
68e7d34965 | ||
|
4585785613 | ||
|
a9651adc84 | ||
|
e611f82a11 | ||
|
8270ff5702 | ||
|
e118b65d65 | ||
|
61ae07966d | ||
|
ff05ed7afd | ||
|
e84f527cd3 | ||
|
0468f5da6d | ||
|
021715e897 | ||
|
5eda393488 | ||
|
de78e8eae7 | ||
|
34da83d729 | ||
|
5c55c4ac18 | ||
|
900e899739 | ||
|
8859d4508a | ||
|
e57fd9d963 | ||
|
9c56902365 | ||
|
595af62ebd | ||
|
4fab833920 | ||
|
2830f72288 | ||
|
fe437a6e7d | ||
|
8f30069160 | ||
|
6f576483a8 | ||
|
c8b6c13015 | ||
|
f4ae43934a | ||
|
706d3ddf90 | ||
|
1186cf2ad4 | ||
|
20e74313cd | ||
|
be06fa695e | ||
|
3e45285b40 | ||
|
0c51f369a4 | ||
|
cb4e26dcb3 | ||
|
9f0195ec1e | ||
|
2e941beb44 | ||
|
088b3d036a | ||
|
a8bb8dfb08 | ||
|
d54ffcfcd1 | ||
|
a3f24aaf5c | ||
|
bf5d0a2651 | ||
|
c900cf7ed4 | ||
|
9e33781d95 | ||
|
43662fa081 | ||
|
77ffd39dfb | ||
|
9fe4f2337b | ||
|
cea441f4d1 | ||
|
00f883a4aa | ||
|
d70f112762 | ||
|
ee04f1fcfd | ||
|
e95a252974 | ||
|
49a1c4d4b0 | ||
|
bd96036835 | ||
|
be5eb995d3 | ||
|
a93616cf66 | ||
|
d906fd5201 | ||
|
063265eacd | ||
|
a882eb2b3b | ||
|
ec076b1df2 | ||
|
1bc546269a | ||
|
56c90ffa5e | ||
|
6ec8854fdb | ||
|
0a480c5c52 | ||
|
397c246fe0 | ||
|
5233a3468c | ||
|
f93cddeafd | ||
|
70be57c8da | ||
|
b5e093624d | ||
|
dbcfd59714 | ||
|
f2efd04052 | ||
|
38a6a39daa | ||
|
eb6b2a11e3 | ||
|
f1cad4c710 | ||
|
045772aa30 | ||
|
e40fbd08c8 | ||
|
7563e69cf1 | ||
|
094488ffab | ||
|
926a565781 | ||
|
7ea5758c91 | ||
|
5d455b28fc | ||
|
78e0fdb618 | ||
|
9f6b006272 | ||
|
994df22b60 | ||
|
a9c2a62939 | ||
|
67d4b3b7f2 | ||
|
414d39085c | ||
|
ed37b4a626 | ||
|
d18ea3f2cb | ||
|
ec821d37a8 | ||
|
c23d5f63fd | ||
|
9cb8865511 | ||
|
cb2b261510 | ||
|
5366e87574 | ||
|
65f59184c8 | ||
|
ac2fc7a366 | ||
|
72abf692e5 | ||
|
a029123679 | ||
|
b3b8834724 | ||
|
1a30adc062 | ||
|
561f773bb4 | ||
|
0e8449a5b9 | ||
|
c2c2721d00 | ||
|
776d5a25b4 | ||
|
c26cd48536 | ||
|
9c8c8fb63a | ||
|
5da35d879a | ||
|
d455300d7b | ||
|
629059ee84 | ||
|
cebcc8a604 | ||
|
08c94de798 | ||
|
d34e315b8d | ||
|
5314d36f3d | ||
|
a156fc9a54 | ||
|
5bf1778e33 | ||
|
4ddcbe22b7 | ||
|
5b55f9616d | ||
|
44f1f45e16 | ||
|
a4363fbcb5 | ||
|
b592ea5bbc | ||
|
06c7220f78 | ||
|
1768ca599b | ||
|
05dc70e22e | ||
|
ca7921f54b | ||
|
75a42845c1 | ||
|
6166cea5bf | ||
|
06e36fb434 | ||
|
e5ec348d68 | ||
|
454b891f6d | ||
|
9350aa7218 | ||
|
4b22cd81e6 | ||
|
a13440d7ca | ||
|
6064a5084e | ||
|
5838c41a35 | ||
|
01cc0dfae9 | ||
|
3d2b1ff5c4 | ||
|
a6bcdbe13c | ||
|
a54f073af8 | ||
|
e1f4fc22d2 | ||
|
ff3f5fa179 | ||
|
a1fb7b51b7 | ||
|
c8a14a29cd | ||
|
2e4821a847 | ||
|
d7b282c601 | ||
|
5d323c37f3 | ||
|
29d0317557 | ||
|
939f5310b9 | ||
|
3a4267ade4 | ||
|
b26604c8d8 | ||
|
16ed726610 | ||
|
52d1ddd601 | ||
|
be85c22f65 | ||
|
7412690dd2 | ||
|
257ce5733e | ||
|
5392a612dc | ||
|
1870b3293a | ||
|
96a0604bd0 | ||
|
756f51eac8 | ||
|
7c386bf4d4 | ||
|
70d4e2e8d5 | ||
|
1d1fe00846 | ||
|
313c40ed09 | ||
|
9241dbdf57 | ||
|
20318e4c6c | ||
|
9aed18a571 | ||
|
0985b2d0d4 | ||
|
2bb09b4015 | ||
|
4902034c89 | ||
|
2352ff24a5 | ||
|
a0a2c84a26 | ||
|
7e37e7fde4 | ||
|
784b46ef2a | ||
|
92d5a84410 | ||
|
81970d22d8 | ||
|
b493811968 | ||
|
2fde4399a0 | ||
|
fc93f10892 | ||
|
e758d722c7 | ||
|
b0e07d516e | ||
|
7ba7768111 | ||
|
bff470f83f | ||
|
40a4f5f38f | ||
|
68adf2c199 | ||
|
7837fbc1c1 | ||
|
2d65ce9711 | ||
|
db0e34535c | ||
|
b975532433 | ||
|
97ee5a1cd3 | ||
|
952e413328 | ||
|
7646741ec6 | ||
|
896912b14d | ||
|
88e6abc5a0 | ||
|
bf0eaafa6b | ||
|
e192fde687 | ||
|
ea0f604e70 | ||
|
c1a0a2c712 | ||
|
c27963c566 | ||
|
8c71fc1cc2 | ||
|
0336b41828 | ||
|
c0a0eedf2e | ||
|
73df2aedf9 | ||
|
114db6f745 | ||
|
d83f2d766d | ||
|
8e9d5d12ae | ||
|
c7bd33d951 | ||
|
8297276967 | ||
|
2ff893cd85 | ||
|
9c9b9b276a | ||
|
6d52b17dfc | ||
|
0973d6e331 | ||
|
a185a2bea2 | ||
|
93b61c07eb | ||
|
e30fb19030 | ||
|
2fed8d8515 | ||
|
480c1c9599 | ||
|
37b8b84fee | ||
|
b4f0503d1b | ||
|
6fcfa2855b | ||
|
5ac008116b | ||
|
29f7dc2922 | ||
|
e8985c71bc | ||
|
54bf15555b | ||
|
785e2c31f5 | ||
|
7111d48748 | ||
|
e805ef2d66 | ||
|
f21afddeb7 | ||
|
7d1125e5b7 | ||
|
d86caf952e | ||
|
9c21139284 | ||
|
5649834f7d | ||
|
1fde4bf4d0 | ||
|
ef69934746 | ||
|
06ce9b4e42 | ||
|
bb9789f2b3 | ||
|
48e14390c1 | ||
|
3d3f62e10a | ||
|
e5a5fd3669 | ||
|
dad02900f1 | ||
|
4bcbcdb1a2 | ||
|
175e0905d5 | ||
|
fc93085c7a | ||
|
6f90c954b7 | ||
|
c55134aa48 | ||
|
7d6cf12554 | ||
|
57fa5d4329 | ||
|
76ef2d15d8 | ||
|
de7f1deb80 | ||
|
6f236c3353 | ||
|
ba0c26a078 | ||
|
30884208d4 | ||
|
802bd34562 | ||
|
8f25ad6416 | ||
|
9aefe9aa7a | ||
|
d2c3b23e6d | ||
|
91f866f786 | ||
|
7482ff93b8 | ||
|
b66fa91c46 | ||
|
b1b0b134ec | ||
|
d1a25fe597 | ||
|
4a15abe865 | ||
|
5c3141a8b9 | ||
|
be497175e0 | ||
|
2b01dca28a | ||
|
8b1a368b59 | ||
|
fdc3fcbfce | ||
|
a234cebbed | ||
|
415c5fd5d8 | ||
|
6361bc4734 | ||
|
804a397643 | ||
|
d0d03f315b | ||
|
5765a28456 | ||
|
c490be9134 | ||
|
d0df1ff995 | ||
|
1ce958bc5e | ||
|
48d9c46daa | ||
|
52d2fa7022 | ||
|
d1ff254c3c | ||
|
b3fbcd1ff7 | ||
|
4ffe15bf6a | ||
|
d01b1b6cc1 | ||
|
a8e4790810 | ||
|
800c0d3dd6 | ||
|
237f84e038 | ||
|
6b6e258e0c | ||
|
6f47e79a88 | ||
|
a579ba2ac2 | ||
|
4bc351d658 | ||
|
20b21f4fd8 | ||
|
8d959b6c75 | ||
|
02ddd48c11 | ||
|
edb5d12463 | ||
|
43f4555893 | ||
|
d46de28a6c | ||
|
df02917d7e | ||
|
ea90a3d622 | ||
|
17fb64b569 | ||
|
c7b2654c25 | ||
|
8f62b7bfe5 | ||
|
9ad30b0b38 | ||
|
0c3a09f1d7 | ||
|
eaf7d06a82 | ||
|
3c377f8334 | ||
|
5636bfd83d | ||
|
f66ae6e80f | ||
|
070c0d31ab | ||
|
f9697ea006 | ||
|
d180bf6311 | ||
|
d0e15b86b5 | ||
|
1efb1c2343 | ||
|
7b1daa248f | ||
|
70bad99894 | ||
|
94141c7797 | ||
|
5397837a9f | ||
|
e4e29add68 | ||
|
3bc1adc404 | ||
|
ea543b5c6f | ||
|
4a544ae272 | ||
|
f6acf922e6 | ||
|
2c3b92b550 | ||
|
7fc1617819 | ||
|
5bb113c204 | ||
|
27a767d5e8 | ||
|
259f9dd718 | ||
|
b203d5e745 | ||
|
6ea250e2d0 | ||
|
7eb7909193 | ||
|
19e652b182 | ||
|
9d19c1075f | ||
|
6dc4d03508 | ||
|
f8872578e9 | ||
|
94207dfd68 | ||
|
da49d4d54e | ||
|
d2bf6742f8 | ||
|
252952ff33 | ||
|
da146ae9b9 | ||
|
4ef1f34549 | ||
|
4b4d467004 | ||
|
9c6a26f50a | ||
|
3d717b52c1 | ||
|
c425cdd115 | ||
|
081e569e14 | ||
|
e96d1ad3ef | ||
|
4de7a843eb | ||
|
d2bb19e883 | ||
|
a57bf37cdb | ||
|
12925d8b70 | ||
|
5af311ddd8 | ||
|
07be2b864f | ||
|
4e1bf7acd3 | ||
|
4661c6b6fb | ||
|
bbf574ab90 | ||
|
0b773eecd9 | ||
|
07587c2699 | ||
|
deb6f6a06c | ||
|
e1112227d1 | ||
|
3928c96a37 | ||
|
26f3b321f9 | ||
|
df5021a201 | ||
|
9c27573493 | ||
|
c77c59f3d9 | ||
|
dcbcc65dc2 | ||
|
c32f96f277 | ||
|
4ca55192e0 | ||
|
d5be748341 | ||
|
957480f4b9 | ||
|
1891fa8272 | ||
|
80394ae7cd | ||
|
5254971418 | ||
|
fd53d86eea | ||
|
42e76a1920 | ||
|
9f7e8f22dc | ||
|
c526c327d7 | ||
|
06a9dab730 | ||
|
6336b5dedf | ||
|
8756297a8d | ||
|
34c55d98ee | ||
|
309bdfa224 | ||
|
61cb58e549 | ||
|
feb69966d4 | ||
|
74565e41fc | ||
|
6a77a40501 | ||
|
2d1f03b616 | ||
|
58bfea6a6e | ||
|
7771793a56 | ||
|
3951ca9320 | ||
|
39921284a0 | ||
|
5cd39f1dc4 | ||
|
17cbcc85bd | ||
|
008c57714a | ||
|
6cb0c7bb5c | ||
|
bd1f4987eb | ||
|
db63587ce2 | ||
|
bce1bc4ca3 | ||
|
4bb57341bd | ||
|
c3caf4b80b | ||
|
db73d756e9 | ||
|
ae9cffba52 | ||
|
d901468a27 | ||
|
7ebe630b69 | ||
|
e104825916 | ||
|
12ecd95965 | ||
|
ebc7fc67c8 | ||
|
005adb0a0a | ||
|
cc4f036d36 | ||
|
059a383cf8 | ||
|
5e06b33c51 | ||
|
6233bcf52d | ||
|
589b752eeb | ||
|
0de56eeaad | ||
|
13cd3ff5a0 | ||
|
37d1b7b745 | ||
|
4d1c4e1f73 | ||
|
351bcf5f82 | ||
|
f6b98ec7ca | ||
|
8ca874bc1f | ||
|
bf1dfea393 | ||
|
c1a66d4fc6 | ||
|
a497b95db1 | ||
|
28658541da | ||
|
1faa208067 | ||
|
21b946dfb7 | ||
|
95ef0cdb46 | ||
|
03e03e9c11 | ||
|
5d3b136d38 | ||
|
69c721dd68 | ||
|
fb0a671419 | ||
|
6022715c81 | ||
|
dfcdfa057b | ||
|
4d4f48af9f | ||
|
f1dd0258f1 | ||
|
93034d0b69 | ||
|
e9c6fd3cff | ||
|
0f64f2d5fc | ||
|
53e92441a6 | ||
|
cf0fa3d693 | ||
|
14b74c360f | ||
|
fef4832a85 | ||
|
2b0dd0b051 | ||
|
b926511b4b | ||
|
0b05e4a874 | ||
|
c014425b6f | ||
|
b0964acadf | ||
|
01cc11d9f8 | ||
|
2047b8e565 | ||
|
f403ac8bf0 | ||
|
cf6c1665f1 | ||
|
e554e375ae | ||
|
837e7e513b | ||
|
a2bf3c6ee8 | ||
|
ca0a4cecec | ||
|
8b179e09a4 | ||
|
402d840aa7 | ||
|
b55daa28e8 | ||
|
0761863e28 | ||
|
b4cbc8f283 | ||
|
cda1622fef | ||
|
f1f2df7bc0 | ||
|
62050760aa | ||
|
2cb9794da2 | ||
|
4cd3dcbbc2 | ||
|
82e5766062 | ||
|
69f808ac8d | ||
|
4a66f02f88 | ||
|
81fad9e853 | ||
|
d382e66035 | ||
|
4420d41fea | ||
|
e5d1f3d6c5 | ||
|
2063d6bb06 | ||
|
b91390c211 | ||
|
2981d1e798 | ||
|
c0a505b0bb | ||
|
adc1fe4b3f | ||
|
52be93c54b | ||
|
62dce13c7a | ||
|
f27e95b25a | ||
|
d8c2677ab9 | ||
|
1df9822224 | ||
|
b0d8a524a2 | ||
|
37a33d7414 | ||
|
3fd4584886 | ||
|
b9d3e8c8a8 | ||
|
1e3411aa37 | ||
|
458918b319 | ||
|
2f638e07e8 | ||
|
103c188681 | ||
|
aed8bc8ae7 | ||
|
a8e39530ef | ||
|
802ed9d459 | ||
|
2282f53ac7 | ||
|
eac0706761 | ||
|
686acce23f | ||
|
6f216692b4 | ||
|
1d3532d0a7 | ||
|
e6e5c3d3ec | ||
|
7c5a08521b | ||
|
9e3ad37828 | ||
|
c11003c860 | ||
|
dd5d865020 | ||
|
5274c2f11b | ||
|
4f26eae40f | ||
|
0aadd6b0fb | ||
|
4b337b20d4 | ||
|
e514284c65 | ||
|
ec45be9976 | ||
|
79446ffad7 | ||
|
ff3c4ed1c0 | ||
|
a7c45ddc59 | ||
|
cc3e7c7c31 | ||
|
5a327755b4 | ||
|
17ad29ebba | ||
|
3985b12a2d | ||
|
1b11f09d2a | ||
|
f0768f636e | ||
|
a46f524247 | ||
|
887f071b25 | ||
|
15b932a70e | ||
|
d914151591 | ||
|
a910052a2e | ||
|
9a414245b9 | ||
|
14da3cc793 | ||
|
e88e9ee9ed | ||
|
5070e7fcf7 | ||
|
fc174f6b74 | ||
|
1c212371fa | ||
|
7c921f18e2 | ||
|
1bb7a155d1 | ||
|
e99a31c950 | ||
|
a67a5d4655 | ||
|
83d483269d | ||
|
e5b7ebcdda | ||
|
5a919e4805 | ||
|
4de15aa58d | ||
|
7092c6ba10 | ||
|
d44e653fe1 | ||
|
5013999c8b | ||
|
d30c2cec88 | ||
|
f45b689406 | ||
|
698082e678 | ||
|
6268d0a080 | ||
|
0a525832f9 | ||
|
3a4107c982 | ||
|
d578d8d3a1 | ||
|
58d414f7f5 | ||
|
fa69ed0bc8 | ||
|
dc8c5c1886 | ||
|
eb51be4fbe | ||
|
af36f5ad36 | ||
|
751163a743 | ||
|
6eeb66e51d | ||
|
79eaaee50a | ||
|
6513a7a449 | ||
|
ebd2758dab | ||
|
dfc516f9bf | ||
|
7e671e5dd0 | ||
|
6c269e639a | ||
|
f96de67490 | ||
|
47a29f6b2e | ||
|
b12961e7eb | ||
|
6ae22125c6 | ||
|
cae6591b9d | ||
|
d1d3820219 | ||
|
99ea03571a | ||
|
a77732c180 | ||
|
db6b4c1eef | ||
|
2593cab5c4 | ||
|
083e538030 | ||
|
92caebab95 | ||
|
3e8e8ddb46 | ||
|
5b93db6566 | ||
|
1127b090bd | ||
|
cee1aedf12 | ||
|
89e2aef63a | ||
|
c33c0545af | ||
|
0ab1187740 | ||
|
6ca28ca4c6 | ||
|
1b107adc1d | ||
|
2078b842fb | ||
|
f2facdc1d0 | ||
|
0ba5f4b8e9 | ||
|
102cc4c1e2 | ||
|
f50d4011cd | ||
|
fe63715f25 | ||
|
7d4a15e53a | ||
|
8cfc385491 | ||
|
31aaa3be71 | ||
|
b864a06221 | ||
|
e081203f3e | ||
|
bd26684d5d | ||
|
80ffaefb88 | ||
|
28b4c48e2a | ||
|
d23c5b9318 | ||
|
9c37b7ed7c | ||
|
26c43c6ce5 | ||
|
f1e7f35bb5 | ||
|
f8a93763e5 | ||
|
10a1b9e182 | ||
|
f752a95302 | ||
|
c099f531fb | ||
|
213434d426 | ||
|
bd0a0ae36b | ||
|
a08e02afbf | ||
|
79a74e9aae | ||
|
c36d4632a6 | ||
|
99c2a2b960 | ||
|
094fce3776 | ||
|
230b7ff0f6 | ||
|
a9fe57db1c | ||
|
9c8f912ea7 | ||
|
029f51e1a8 | ||
|
899a4ca275 | ||
|
ab7ade4095 | ||
|
38cc03605c | ||
|
a23655ab3b | ||
|
f77fe436af | ||
|
944ae4d2df | ||
|
15da467b52 | ||
|
ae0a8740c5 | ||
|
49a8d1a85e | ||
|
f2d1f5e927 | ||
|
2d384954ad | ||
|
a1febff857 | ||
|
cdf06044e1 | ||
|
b81186e535 | ||
|
b717e8fe1d | ||
|
41ebed7077 | ||
|
b717c5efae | ||
|
e04705b305 | ||
|
3bf43a65fe | ||
|
8a667f5887 | ||
|
ad414e5ad6 | ||
|
35612bd27c | ||
|
cfdc87b623 | ||
|
86f9271457 | ||
|
199b7b55c2 | ||
|
01156de6f7 | ||
|
ed6ffc7a34 | ||
|
851ccfccaf | ||
|
0e7f352c70 | ||
|
7ae05adf46 | ||
|
7c894911ae | ||
|
767c9d653e | ||
|
b321d2b1b9 | ||
|
68e0979724 | ||
|
8e4db009b8 | ||
|
8012c223e0 | ||
|
687bc5cecf | ||
|
fc37ee63c0 | ||
|
21cefd3f07 | ||
|
a5d65b4a51 | ||
|
d73a83e8cf | ||
|
d37a26b680 | ||
|
3d333e5a29 | ||
|
48877e215d | ||
|
5b3a4a9595 | ||
|
02d67848eb | ||
|
cfe7d2368c | ||
|
4fb224a553 | ||
|
e4ec6bcdd3 | ||
|
815f1ee26d | ||
|
7d4d0ff606 | ||
|
88eeb004e9 | ||
|
43dfc2011c | ||
|
a67adbfe2e | ||
|
090ecaabed | ||
|
04003cf1ff | ||
|
ecfb90de74 | ||
|
80603ad49a | ||
|
92eba53a79 | ||
|
f7f395667e | ||
|
067c0a23e5 | ||
|
98b41db324 | ||
|
cb49ceeaba | ||
|
1c07e67462 | ||
|
0424277c00 | ||
|
a4f8d1165c | ||
|
26889b3bb6 | ||
|
e9ee2cb6b6 | ||
|
780424639a | ||
|
b762c37555 | ||
|
5dc2ed0c71 | ||
|
2f2b3c9100 | ||
|
7fffcce924 | ||
|
adeb624c3b | ||
|
8c872852cd | ||
|
498da2414b | ||
|
b896f7de1e | ||
|
757f7d5508 | ||
|
3d3efd7ab8 | ||
|
9b640c3684 | ||
|
55683a659f | ||
|
bf8d00985a | ||
|
ad4ca55543 | ||
|
6247364caf | ||
|
9c083d34af | ||
|
197066c863 | ||
|
8fd886f735 | ||
|
e8ba5c9bd4 | ||
|
e694e0896b | ||
|
591ef307b3 | ||
|
7fd4119d24 | ||
|
ba9b7b69d9 | ||
|
e0c22df5c4 | ||
|
9f72a52302 | ||
|
7ef152c072 | ||
|
57e9df7dc5 | ||
|
15ca5883fb | ||
|
87d9406725 | ||
|
a6203657a0 | ||
|
e6cafdad36 | ||
|
b5314c2a6a | ||
|
f3d508630d | ||
|
5c18b0ce9c | ||
|
04987e7f49 | ||
|
e4caf0beeb | ||
|
1202713f94 | ||
|
2842a645fd | ||
|
db606591c0 | ||
|
144169b9b7 | ||
|
e2d823eced | ||
|
fa130722cb | ||
|
f194d5f169 | ||
|
4c5ad5a04c | ||
|
29a8012724 | ||
|
d4d1d5ac3e | ||
|
708eca39ea | ||
|
e84fb639f0 | ||
|
b1ea27b1e2 | ||
|
49d9582bed | ||
|
d4e434d1e6 | ||
|
d3d13e134a | ||
|
8b7a78c728 | ||
|
67da017fd2 | ||
|
3d2fbfe243 | ||
|
9539ee8098 | ||
|
a1dbd54885 | ||
|
08aeea6a12 | ||
|
07c1694ff5 | ||
|
7bb39af036 | ||
|
95f0e25601 | ||
|
673d4f7453 | ||
|
ed9162cd88 | ||
|
f6575ec824 | ||
|
d1ecbfa52d | ||
|
d3b15674df | ||
|
d0111563c2 | ||
|
fd79027cae | ||
|
4d116d7a28 | ||
|
def65507e6 | ||
|
ae805eea50 | ||
|
00dfca034c | ||
|
7c1e601164 | ||
|
1df6769aca | ||
|
795e47cec5 |
@@ -1 +1 @@
|
||||
1.02.124-git (2016-04-30)
|
||||
1.02.137-git (2016-11-05)
|
||||
|
222
WHATS_NEW
222
WHATS_NEW
@@ -1,3 +1,225 @@
|
||||
Version 2.02.168 -
|
||||
====================================
|
||||
Only log msg as debug if lvm2-lvmdbusd unit missing for D-Bus notification.
|
||||
Missing stripe filler now could be also 'zero'.
|
||||
lvconvert --repair accepts --interval and --background option.
|
||||
More efficiently prepare _rmeta devices when creating a new raid LV.
|
||||
|
||||
Version 2.02.167 - 5th November 2016
|
||||
====================================
|
||||
Use log_error in regex and sysfs filter to describe reason of failure.
|
||||
Fix blkdeactivate to deactivate dev stack if dev on top already unmounted.
|
||||
Prevent non-synced raid1 repair unless --force
|
||||
Prevent raid4 creation/conversion on non-supporting kernels
|
||||
Add direct striped -> raid4 conversion
|
||||
Fix raid4 parity image pair position on conversions from striped/raid0*
|
||||
Fix a few unconverted return code values for some lvconvert error path.
|
||||
Disable lvconvert of thin pool to raid while active.
|
||||
Disable systemd service start rate limiting for lvm2-pvscan@.service.
|
||||
|
||||
Version 2.02.166 - 26th September 2016
|
||||
======================================
|
||||
Fix lvm2-activation-generator to read all LVM2 config sources. (2.02.155)
|
||||
Fix lvchange-rebuild-raid.sh to cope with older target versions.
|
||||
Use dm_config_parse_without_dup_node_check() to speedup metadata reading.
|
||||
Fix lvconvert --repair regression
|
||||
Fix reported origin lv field for cache volumes. (2.02.133)
|
||||
Always specify snapshot cow LV for monitoring not internal LV. (2.02.165)
|
||||
Fix lvchange --discard|--zero for active thin-pool.
|
||||
Enforce 4MiB or 25% metadata free space for thin pool operations.
|
||||
Fix lock-holder device for thin pool with inactive thin volumes.
|
||||
Use --alloc normal for mirror logs even if the mimages were stricter.
|
||||
Use O_DIRECT to gather metadata in lvmdump.
|
||||
Ignore creation_time when checking for matching metadata for lvmetad.
|
||||
Fix possible NULL pointer derefence when checking for monitoring.
|
||||
Add lvmreport(7) man page.
|
||||
Don't install lvmraid(7) man page when raid excluded. (2.02.165)
|
||||
Report 0% as dirty (copy%) for cache without any used block.
|
||||
Fix lvm2api reporting of cache data and metadata percent.
|
||||
Restore reporting of metadata usage for cache volumes (2.02.155).
|
||||
Support raid scrubbing on cache origin LV.
|
||||
|
||||
Version 2.02.165 - 7th September 2016
|
||||
=====================================
|
||||
Add lvmraid(7) man page.
|
||||
Use udev db to check for mpath components before running pvscan for lvmetad.
|
||||
Use lsblk -s and lsblk -O in lvmdump only if these options are supported.
|
||||
Fix number of stripes shown in lvcreate raid10 message when too many.
|
||||
Change lvmdbusd to use new lvm shell facilities.
|
||||
Do not monitor cache-pool metadata when LV is just being cleared.
|
||||
Add allocation/cache_pool_max_chunks to prevent misuse of cache target.
|
||||
Give error not segfault in lvconvert --splitmirrors when PV lies outside LV.
|
||||
Fix typo in report/columns_as_rows config option name recognition (2.02.99).
|
||||
Avoid PV tags when checking allocation against parallel PVs.
|
||||
Disallow mirror conversions of raid10 volumes.
|
||||
Fix dmeventd unmonitoring when segment type (and dso) changes.
|
||||
Don't allow lvconvert --repair on raid0 devices or attempt to monitor them.
|
||||
No longer adjust incorrect number of raid stripes supplied to lvcreate.
|
||||
Move lcm and gcd to lib/misc.
|
||||
Fix vgsplit of external origins. (2.02.162)
|
||||
Prohibit creation of RAID LVs unless VG extent size is at least the page size.
|
||||
Suppress some unnecessary --stripesize parameter warnings.
|
||||
Fix 'pvmove -n name ...' to prohibit collocation of RAID SubLVs
|
||||
|
||||
Version 2.02.164 - 15th August 2016
|
||||
===================================
|
||||
Fix selection of PVs when allocating raid0_meta.
|
||||
Fix sdbus socket leak leading to hang in lvmnotify.
|
||||
Specify max stripes for raid LV types: raid0:64; 1:10; 4,5:63; 6:62; 10:32.
|
||||
Avoid double suffix when naming _rmeta LV paired with _rimage LV.
|
||||
|
||||
Version 2.02.163 - 10th August 2016
|
||||
===================================
|
||||
Add profile for lvmdbusd which uses lvm shell json report output.
|
||||
Restrict in-command modification of some parms in lvm shell.
|
||||
Apply LVM_COMMAND_PROFILE early for lvm shell.
|
||||
Refactor reporting so lvm shell log report collects whole of cmd execution.
|
||||
Support LVM_*_FD envvars to redirect output to file descriptors.
|
||||
Limit use of --corelog and --mirrorlog to mirrors in lvconvert.
|
||||
Reject --nosync option for RAID6 LVs in lvcreate.
|
||||
Do not refresh whole cmd context if profile dropped after processing LVM cmd.
|
||||
Support straightforward lvconvert between striped and raid4 LVs.
|
||||
Support straightforward lvconvert between raid1 and mirror LVs.
|
||||
Report supported conversions when asked for unsupported raid lvconvert.
|
||||
Add "--rebuild PV" option to lvchange to allow for PV selective rebuilds.
|
||||
Preserve existing mirror region size when using --repair.
|
||||
Forbid stripe parameters with lvconvert --repair.
|
||||
Unify stripe size validation into get_stripe_params to catch missing cases.
|
||||
Further lvconvert validation logic refactoring.
|
||||
|
||||
Version 2.02.162 - 28th July 2016
|
||||
=================================
|
||||
Extend vg_validate also to check raid configurations thoroughly.
|
||||
Support lvconvert -Zn also when doing full cache pool conversion.
|
||||
Suppress not zeroing warn when converting to thin LV for non-zeroing tpool.
|
||||
Fix automatic updates of PV extension headers to newest version.
|
||||
Improve lvconvert --trackchanges validation to require --splitmirrors 1.
|
||||
Add note about lastlog built-in command to lvm man page.
|
||||
Fix unrecognised segtype flag message.
|
||||
lvconvert not clears cache pool metadata ONLY with -Zn.
|
||||
Add allocation/raid_stripe_all_devices to reinstate previous behaviour.
|
||||
Create raid stripes across fixed small numbers of PVs instead of all PVs.
|
||||
Enabled lvconvert --uncache to work with partial VG.
|
||||
Disallow lvconvert --replace with raid0* LVs.
|
||||
Fix some lvmetad changed VG metadata notifications that sent uncommitted data.
|
||||
|
||||
Version 2.02.161 - 15th July 2016
|
||||
=================================
|
||||
Prohibit some lvchange/lvresize that were failing on raid0 volumes.
|
||||
Fix segfaults in complex vgsplits. (2.02.159)
|
||||
Reformat unwieldy lvconvert man page.
|
||||
Allow --force to be passed through to pvcreate from vgcreate. (2.02.144)
|
||||
Fix lvresize of filesystem when LV has already right size (2.02.141)
|
||||
New LVM_LOG_FILE_MAX_LINES env var to limit max size of created logs.
|
||||
|
||||
Version 2.02.160 - 6th July 2016
|
||||
================================
|
||||
Minor fixes from coverity.
|
||||
|
||||
Version 2.02.159 - 6th July 2016
|
||||
================================
|
||||
Add raid0_meta segment type that provides metadata space for raid conversions.
|
||||
Fix created link for a used pool for vgmknode.
|
||||
Introduce and use is_power_of_2 macro.
|
||||
Support conversions between striped and raid0 segment types.
|
||||
Add infrastructure for raid takeover lvconvert options.
|
||||
|
||||
Version 2.02.158 - 25th June 2016
|
||||
=================================
|
||||
Add a more efficient native vgimportclone command to replace the script.
|
||||
Make lvmlockd always attempt to connect to lvmetad if no connection exists.
|
||||
Let lvmetad handle new connections after shutdown signal.
|
||||
Disable lvmetad when vgcfgrestore begins and enable it again after.
|
||||
Make pvscan do activation if lvmetad is configured but not running.
|
||||
Fix rescanning the PVs for a single VG when using lvmetad.
|
||||
Pool metadata lvresize uses now same code as resize of normal volume.
|
||||
Preserve monitoring status when updating thin-pool metadata.
|
||||
Return 0 (inactive) when status cannot be queried in _lv_active().
|
||||
Switch to log_warn() for failing activation status query.
|
||||
Replace vgimportclone script with binary.
|
||||
While lvmetad is shutting down, continue handling all connections cleanly.
|
||||
Refactor lvconvert argument handling code.
|
||||
Notify lvmetad when vgcfgrestore changes VG metadata.
|
||||
Add --logonly option to report only cmd log for a command, not other reports.
|
||||
Add log/command_log_selection to configure default selection used on cmd log.
|
||||
Use 'orphan' object type in cmd log for groups to collect PVs not yet in VGs.
|
||||
Add lvm lastlog command for query and display of last cmd's log in lvm shell.
|
||||
Report per-object return codes via cmd log while processing multiple objects.
|
||||
Annotate processing code with log report hooks for per-object command log.
|
||||
Also pass common printed messages (besides warnings and errors) to log report.
|
||||
Log warnings and errors via report during cmd processing if this is enabled.
|
||||
Make it possible to iterate over internal 'orphan' VGs in process_each_vg fn.
|
||||
Make -S|--select option groupable that allows this option to be repeated.
|
||||
Make -O|--sort option groupable that allows this option to be repeated.
|
||||
Add --configreport option to select report for which next options are applied.
|
||||
Add support for priorities on grouping command arguments.
|
||||
Add report/{pvs,vgs,lvs,pvsegs,segs}_{cols,sort}_full to lvm.conf.
|
||||
Add lvm fullreport command for joined PV, VG, LV and segment report per VG.
|
||||
Integrate report group handling and cmd log report into cmd processing code.
|
||||
Add log/report_command_log to lvm.conf to enable or disable cmd log report.
|
||||
Add log/report_output_format to lvm.conf for default report output format.
|
||||
Recognize --reportformat {basic|json} option to select report output format.
|
||||
Add log/command_log_{sort,cols} to lvm.conf to configure command log report.
|
||||
Add log_object_{type,name,id,group,group_id} fields to cmd log.
|
||||
Add log_{seq_num,type,context,message,errno,ret_code} fields to cmd log.
|
||||
Add CMDLOG report type - a separate report type for command logging.
|
||||
|
||||
Version 2.02.157 - 17th June 2016
|
||||
=================================
|
||||
Change pvscan --cache -aay to scan locally if lvmetad fails.
|
||||
|
||||
Version 2.02.156 - 11th June 2016
|
||||
=================================
|
||||
Don't allow duplicate orphan PVs to be used with vgcreate/vgextend/pvcreate.
|
||||
Improve handling of lvmetad update failures.
|
||||
Yes/No prompt accepts '^[ ^t]*([Yy]([Ee]([Ss]|)|)|[Nn]([Oo]|))[ ^t]*$'.
|
||||
If available, also collect output from lsblk command when running lvmdump -s.
|
||||
|
||||
Version 2.02.155 - 3rd June 2016
|
||||
================================
|
||||
Reject PV tags on pvmove cmdline because only 1 PV is supported. (2.02.141)
|
||||
Fix compilation error when building with configure --disable-devmapper.
|
||||
Fix lvmconfig --type diff to display complete diff if config cascade used.
|
||||
Automatically filter out partitioned loop devices with partscan (losetup -P).
|
||||
Fix lvm devtypes internal error if -S used with field name from pvs/vgs/lvs.
|
||||
When reporting Data%,Snap%,Meta%,Cpy%Sync use single ioctl per LV.
|
||||
Add lvseg_percent_with_info_and_seg_status() for percent retrieval.
|
||||
Enhance internal seg_status handling to understand snapshots better.
|
||||
When refresh failed in suspend, call resume upon error path.
|
||||
Support passthrough cache mode when waiting for clean cache.
|
||||
Check cache status only for 'in-use' cache pools.
|
||||
Extend setup_task() to preset flushing for dm_task object.
|
||||
When checking LV is a merging COW, validate its a COW LV first.
|
||||
Correcting value in copy_percent() for 100%.
|
||||
Update vgreduce to use process_each_vg.
|
||||
Update lvconvert to use process_each_lv.
|
||||
Update pvscan to use process_each_vg for autoactivation.
|
||||
Add basic support for --type raid0 using md.
|
||||
Add support for lvchange --cachemode for cached LV.
|
||||
Fix liblvm2app error handling when setting up context.
|
||||
Delay liblvm2app init in python code until it is needed.
|
||||
Simplify thread locking in lvmetad to fix locking problems.
|
||||
Allow pvremove -ff to remove a duplicate PV.
|
||||
Fix lvm2-activation-generator to read lvm.conf without full command setup.
|
||||
Allow a minimal context to be used in lvm2app for reading lvm.conf.
|
||||
|
||||
Version 2.02.154 - 14th May 2016
|
||||
================================
|
||||
Fix liblvm segfault after failure initialising lvmetad connection.
|
||||
Retry open without O_NOATIME if it fails (not file owner/CAP_FOWNER).
|
||||
Split _report into one fn for options and arguments and one for processing.
|
||||
|
||||
Version 2.02.153 - 7th May 2016
|
||||
===============================
|
||||
Change warning messages related to duplicate PVs.
|
||||
A named device is always processed itself, not switched for a duplicate.
|
||||
Add PV attr "d" and report field "duplicate" for duplicate PVs.
|
||||
Add config setting to disallow VG changes when duplicate PVs exist.
|
||||
Use device size and active LVs to choose the preferred duplicate PV.
|
||||
Disable lvmetad when duplicate PVs are seen.
|
||||
Support --chunksize option also when caching LV when possible.
|
||||
Add function to check for target presence and version via 1 ioctl.
|
||||
|
||||
Version 2.02.152 - 30th April 2016
|
||||
==================================
|
||||
Use any inherited tags when wiping metadata sub LVs to ensure activation.
|
||||
|
100
WHATS_NEW_DM
100
WHATS_NEW_DM
@@ -1,3 +1,101 @@
|
||||
Version 1.02.137 -
|
||||
====================================
|
||||
|
||||
Version 1.02.136 - 5th November 2016
|
||||
====================================
|
||||
Log failure of raid device with log_error level.
|
||||
Use dm_log_with_errno and translate runtime to dm_log only when needed.
|
||||
Make log messages from dm and lvm library different from dmeventd.
|
||||
Notice and Info messages are again logged from dmeventd and its plugins.
|
||||
Dmeventd now also respects DM_ABORT_ON_INTERNAL_ERRORS as libdm based tool.
|
||||
Report as non default dm logging also when logging with errno was changed.
|
||||
Use log_level() macro to consistently decode message log level in dmeventd.
|
||||
Still produce output when dmsetup dependency tree building finds dev missing.
|
||||
Check and report pthread_sigmask() failure in dmeventd.
|
||||
Check mem alloc fail in _canonicalize_field_ids().
|
||||
Use unsigned math when checking more then 31 legs of raid.
|
||||
Fix 'dmstats delete' with dmsetup older than v1.02.129
|
||||
Fix stats walk segfault with dmsetup older than v1.02.129
|
||||
|
||||
Version 1.02.135 - 26th September 2016
|
||||
======================================
|
||||
Fix man entry for dmsetup status.
|
||||
Introduce new dm_config_parse_without_dup_node_check().
|
||||
Don't omit last entry in dmstats list --group.
|
||||
|
||||
Version 1.02.134 - 7th September 2016
|
||||
=====================================
|
||||
Improve explanation of udev fallback in libdevmapper.h.
|
||||
|
||||
Version 1.02.133 - 10th August 2016
|
||||
===================================
|
||||
Add dm_report_destroy_rows/dm_report_group_output_and_pop_all for lvm shell.
|
||||
Adjust group handling and json production for lvm shell.
|
||||
|
||||
Version 1.02.132 - 28th July 2016
|
||||
=================================
|
||||
Fix json reporting to escape '"' character that may appear in reported string.
|
||||
|
||||
Version 1.02.131 - 15th July 2016
|
||||
=================================
|
||||
Disable queueing on mpath devs in blk-availability systemd service/initscript.
|
||||
Add new -m|--mpathoption disablequeueing to blkdeactivate.
|
||||
Automatically group regions with 'create --segments' unless --nogroup.
|
||||
Fix resource leak when deleting the first member of a group.
|
||||
Allow --bounds with 'create --filemap' for dmstats.
|
||||
Enable creation of filemap regions with histograms.
|
||||
Enable histogram aggregation for regions with more than one area.
|
||||
Enable histogram aggregation for groups of regions.
|
||||
Add a --filemap option to 'dmstats create' to allow mapping of files.
|
||||
Add dm_stats_create_regions_from_fd() to map file extents to regions.
|
||||
|
||||
Version 1.02.130 - 6th July 2016
|
||||
================================
|
||||
Minor fixes from coverity.
|
||||
|
||||
Version 1.02.129 - 6th July 2016
|
||||
================================
|
||||
Update default dmstats field selections for groups.
|
||||
Add 'obj_type', 'group_id', and 'statsname' fields to dmstats reports.
|
||||
Add --area, --region, and --group to dmstats to control object selection.
|
||||
Add --alias, --groupid, --regions to dmstats for group creation and deletion.
|
||||
Add 'group' and 'ungroup' commands to dmstats.
|
||||
Allow dm_stats_delete_group() to optionally delete all group members.
|
||||
Add dm_stats_get_object_type() to return the type of object present.
|
||||
Add dm_stats_walk_init() allowing control of objects visited by walks.
|
||||
Add dm_stats_get_group_descriptor() to return the member list as a string.
|
||||
Introduce dm_stats_get_nr_groups() and dm_stats_group_present().
|
||||
Add dm_stats_{get,set}_alias() to set and retrieve alias names for groups.
|
||||
Add dm_stats_get_group_id() to return the group ID for a given region.
|
||||
Add dm_stats_{create,delete}_group() to allow grouping of stats regions.
|
||||
Add enum-driven dm_stats_get_{metric,counter}() interfaces.
|
||||
Add dm_bitset_parse_list() to parse a string representation of a bitset.
|
||||
Thin dmeventd plugin umounts lvm2 volume only when pool is 95% or more.
|
||||
|
||||
Version 1.02.128 - 25th June 2016
|
||||
=================================
|
||||
Recognize 'all' keyword used in selection as synonym for "" (no selection).
|
||||
Add dm_report_set_selection to set selection for multiple output of report.
|
||||
Add DM_REPORT_OUTPUT_MULTIPLE_TIMES flag for multiple output of same report.
|
||||
Move field width handling/sort init from dm_report_object to dm_report_output.
|
||||
Add _LOG_BYPASS_REPORT flag for bypassing any log report currently set.
|
||||
Introduce DM_REPORT_GROUP_JSON for report group with JSON output format.
|
||||
Introduce DM_REPORT_GROUP_BASIC for report group with basic report output.
|
||||
Introduce DM_REPORT_GROUP_SINGLE for report group having single report only.
|
||||
Add dm_report_group_{create,push,pop,destroy} to support report grouping.
|
||||
|
||||
Version 1.02.127 - 11th June 2016
|
||||
=================================
|
||||
Fix blkdeactivate regression causing skipping of dm + md devices. (1.02.126)
|
||||
|
||||
Version 1.02.126 - 3rd June 2016
|
||||
================================
|
||||
Report passthrough caching mode when parsing cache mode.
|
||||
|
||||
Version 1.02.125 - 14th May 2016
|
||||
================================
|
||||
Show library version in message even if dm driver version is unavailable.
|
||||
|
||||
Version 1.02.124 - 30th April 2016
|
||||
==================================
|
||||
Add dm_udev_wait_immediate to libdevmapper for waiting outside the library.
|
||||
@@ -93,7 +191,7 @@ Version 1.02.110 - 30th October 2015
|
||||
Correct use of max_write_behind parameter when generating raid target line.
|
||||
Fix dm-event systemd service to make sure it is executed before mounting.
|
||||
|
||||
Version 1.02.109 - 22nd September 2016
|
||||
Version 1.02.109 - 22nd September 2015
|
||||
======================================
|
||||
Update man pages for dmsetup and dmstats.
|
||||
Improve help text for dmsetup.
|
||||
|
@@ -24,7 +24,8 @@ PROFILES=$(PROFILE_TEMPLATES) \
|
||||
$(srcdir)/cache-mq.profile \
|
||||
$(srcdir)/cache-smq.profile \
|
||||
$(srcdir)/thin-generic.profile \
|
||||
$(srcdir)/thin-performance.profile
|
||||
$(srcdir)/thin-performance.profile \
|
||||
$(srcdir)/lvmdbusd.profile
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
|
@@ -16,15 +16,20 @@ allocation {
|
||||
cache_settings {
|
||||
}
|
||||
}
|
||||
|
||||
log {
|
||||
report_command_log=0
|
||||
command_log_sort="log_seq_num"
|
||||
command_log_cols="log_seq_num,log_type,log_context,log_object_type,log_object_name,log_object_id,log_object_group,log_object_group_id,log_message,log_errno,log_ret_code"
|
||||
command_log_selection="!(log_type=status && message=success)"
|
||||
}
|
||||
global {
|
||||
units="h"
|
||||
si_unit_consistency=1
|
||||
suffix=1
|
||||
lvdisplay_shows_full_device_path=0
|
||||
}
|
||||
|
||||
report {
|
||||
output_format="basic"
|
||||
compact_output=0
|
||||
compact_output_cols=""
|
||||
aligned=1
|
||||
@@ -34,7 +39,7 @@ report {
|
||||
list_item_separator=","
|
||||
prefixes=0
|
||||
quoted=1
|
||||
colums_as_rows=0
|
||||
columns_as_rows=0
|
||||
binary_values_as_numeric=0
|
||||
time_format="%Y-%m-%d %T %z"
|
||||
devtypes_sort="devtype_name"
|
||||
@@ -55,5 +60,15 @@ report {
|
||||
pvsegs_sort="pv_name,pvseg_start"
|
||||
pvsegs_cols="pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size"
|
||||
pvsegs_cols_verbose="pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size,lv_name,seg_start_pe,segtype,seg_pe_ranges"
|
||||
vgs_cols_full="vg_all"
|
||||
pvs_cols_full="pv_all"
|
||||
lvs_cols_full="lv_all"
|
||||
pvsegs_cols_full="pvseg_all,pv_uuid,lv_uuid"
|
||||
segs_cols_full="seg_all,lv_uuid"
|
||||
vgs_sort_full="vg_name"
|
||||
pvs_sort_full="pv_name"
|
||||
lvs_sort_full="vg_name,lv_name"
|
||||
pvsegs_sort_full="pv_uuid,pvseg_start"
|
||||
segs_sort_full="lv_uuid,seg_start"
|
||||
mark_hidden_devices=1
|
||||
}
|
||||
|
@@ -299,6 +299,19 @@ devices {
|
||||
# generally do. If enabled, discards will only be issued if both the
|
||||
# storage and kernel provide support.
|
||||
issue_discards = 0
|
||||
|
||||
# Configuration option devices/allow_changes_with_duplicate_pvs.
|
||||
# Allow VG modification while a PV appears on multiple devices.
|
||||
# When a PV appears on multiple devices, LVM attempts to choose the
|
||||
# best device to use for the PV. If the devices represent the same
|
||||
# underlying storage, the choice has minimal consequence. If the
|
||||
# devices represent different underlying storage, the wrong choice
|
||||
# can result in data loss if the VG is modified. Disabling this
|
||||
# setting is the safest option because it prevents modifying a VG
|
||||
# or activating LVs in it while a PV appears on multiple devices.
|
||||
# Enabling this setting allows the VG to be used as usual even with
|
||||
# uncertain devices.
|
||||
allow_changes_with_duplicate_pvs = 0
|
||||
}
|
||||
|
||||
# Configuration section allocation.
|
||||
@@ -364,6 +377,14 @@ allocation {
|
||||
# The default setting changed in version 2.02.85.
|
||||
mirror_logs_require_separate_pvs = 0
|
||||
|
||||
# Configuration option allocation/raid_stripe_all_devices.
|
||||
# Stripe across all PVs when RAID stripes are not specified.
|
||||
# If enabled, all PVs in the VG or on the command line are used for raid0/4/5/6/10
|
||||
# when the command does not specify the number of stripes to use.
|
||||
# This was the default behaviour until release 2.02.162.
|
||||
# This configuration option has an automatic default value.
|
||||
# raid_stripe_all_devices = 0
|
||||
|
||||
# Configuration option allocation/cache_pool_metadata_require_separate_pvs.
|
||||
# Cache pool metadata and data will always use different PVs.
|
||||
cache_pool_metadata_require_separate_pvs = 0
|
||||
@@ -407,6 +428,12 @@ allocation {
|
||||
# 32KiB to 1GiB in multiples of 32.
|
||||
# This configuration option does not have a default value defined.
|
||||
|
||||
# Configuration option allocation/cache_pool_max_chunks.
|
||||
# The maximum number of chunks in a cache pool.
|
||||
# For cache target v1.9 the recommended maximumm is 1000000 chunks.
|
||||
# Using cache pool with more chunks may degrade cache performance.
|
||||
# This configuration option does not have a default value defined.
|
||||
|
||||
# Configuration option allocation/thin_pool_metadata_require_separate_pvs.
|
||||
# Thin pool metdata and data will always use different PVs.
|
||||
thin_pool_metadata_require_separate_pvs = 0
|
||||
@@ -465,6 +492,55 @@ allocation {
|
||||
# How LVM log information is reported.
|
||||
log {
|
||||
|
||||
# Configuration option log/report_command_log.
|
||||
# Enable or disable LVM log reporting.
|
||||
# If enabled, LVM will collect a log of operations, messages,
|
||||
# per-object return codes with object identification and associated
|
||||
# error numbers (errnos) during LVM command processing. Then the
|
||||
# log is either reported solely or in addition to any existing
|
||||
# reports, depending on LVM command used. If it is a reporting command
|
||||
# (e.g. pvs, vgs, lvs, lvm fullreport), then the log is reported in
|
||||
# addition to any existing reports. Otherwise, there's only log report
|
||||
# on output. For all applicable LVM commands, you can request that
|
||||
# the output has only log report by using --logonly command line
|
||||
# option. Use log/command_log_cols and log/command_log_sort settings
|
||||
# to define fields to display and sort fields for the log report.
|
||||
# You can also use log/command_log_selection to define selection
|
||||
# criteria used each time the log is reported.
|
||||
# This configuration option has an automatic default value.
|
||||
# report_command_log = 0
|
||||
|
||||
# Configuration option log/command_log_sort.
|
||||
# List of columns to sort by when reporting command log.
|
||||
# See <lvm command> --logonly --configreport log -o help
|
||||
# for the list of possible fields.
|
||||
# This configuration option has an automatic default value.
|
||||
# command_log_sort = "log_seq_num"
|
||||
|
||||
# Configuration option log/command_log_cols.
|
||||
# List of columns to report when reporting command log.
|
||||
# See <lvm command> --logonly --configreport log -o help
|
||||
# for the list of possible fields.
|
||||
# This configuration option has an automatic default value.
|
||||
# command_log_cols = "log_seq_num,log_type,log_context,log_object_type,log_object_name,log_object_id,log_object_group,log_object_group_id,log_message,log_errno,log_ret_code"
|
||||
|
||||
# Configuration option log/command_log_selection.
|
||||
# Selection criteria used when reporting command log.
|
||||
# You can define selection criteria that are applied each
|
||||
# time log is reported. This way, it is possible to control the
|
||||
# amount of log that is displayed on output and you can select
|
||||
# only parts of the log that are important for you. To define
|
||||
# selection criteria, use fields from log report. See also
|
||||
# <lvm command> --logonly --configreport log -S help for the
|
||||
# list of possible fields and selection operators. You can also
|
||||
# define selection criteria for log report on command line directly
|
||||
# using <lvm command> --configreport log -S <selection criteria>
|
||||
# which has precedence over log/command_log_selection setting.
|
||||
# For more information about selection criteria in general, see
|
||||
# lvm(8) man page.
|
||||
# This configuration option has an automatic default value.
|
||||
# command_log_selection = "!(log_type=status && message=success)"
|
||||
|
||||
# Configuration option log/verbose.
|
||||
# Controls the messages sent to stdout or stderr.
|
||||
verbose = 0
|
||||
@@ -1538,6 +1614,22 @@ activation {
|
||||
# This configuration section has an automatic default value.
|
||||
# report {
|
||||
|
||||
# Configuration option report/output_format.
|
||||
# Format of LVM command's report output.
|
||||
# If there is more than one report per command, then the format
|
||||
# is applied for all reports. You can also change output format
|
||||
# directly on command line using --reportformat option which
|
||||
# has precedence over log/output_format setting.
|
||||
# Accepted values:
|
||||
# basic
|
||||
# Original format with columns and rows. If there is more than
|
||||
# one report per command, each report is prefixed with report's
|
||||
# name for identification.
|
||||
# json
|
||||
# JSON format.
|
||||
# This configuration option has an automatic default value.
|
||||
# output_format = "basic"
|
||||
|
||||
# Configuration option report/compact_output.
|
||||
# Do not print empty values for all report fields.
|
||||
# If enabled, all fields that don't have a value set for any of the
|
||||
@@ -1598,11 +1690,11 @@ activation {
|
||||
# This configuration option has an automatic default value.
|
||||
# quoted = 1
|
||||
|
||||
# Configuration option report/colums_as_rows.
|
||||
# Configuration option report/columns_as_rows.
|
||||
# Output each column as a row.
|
||||
# If set, this also implies report/prefixes=1.
|
||||
# This configuration option has an automatic default value.
|
||||
# colums_as_rows = 0
|
||||
# columns_as_rows = 0
|
||||
|
||||
# Configuration option report/binary_values_as_numeric.
|
||||
# Use binary values 0 or 1 instead of descriptive literal values.
|
||||
@@ -1854,6 +1946,66 @@ activation {
|
||||
# This configuration option has an automatic default value.
|
||||
# pvsegs_cols_verbose = "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size,lv_name,seg_start_pe,segtype,seg_pe_ranges"
|
||||
|
||||
# Configuration option report/vgs_cols_full.
|
||||
# List of columns to report for lvm fullreport's 'vgs' subreport.
|
||||
# See 'vgs -o help' for the list of possible fields.
|
||||
# This configuration option has an automatic default value.
|
||||
# vgs_cols_full = "vg_all"
|
||||
|
||||
# Configuration option report/pvs_cols_full.
|
||||
# List of columns to report for lvm fullreport's 'vgs' subreport.
|
||||
# See 'pvs -o help' for the list of possible fields.
|
||||
# This configuration option has an automatic default value.
|
||||
# pvs_cols_full = "pv_all"
|
||||
|
||||
# Configuration option report/lvs_cols_full.
|
||||
# List of columns to report for lvm fullreport's 'lvs' subreport.
|
||||
# See 'lvs -o help' for the list of possible fields.
|
||||
# This configuration option has an automatic default value.
|
||||
# lvs_cols_full = "lv_all"
|
||||
|
||||
# Configuration option report/pvsegs_cols_full.
|
||||
# List of columns to report for lvm fullreport's 'pvseg' subreport.
|
||||
# See 'pvs --segments -o help' for the list of possible fields.
|
||||
# This configuration option has an automatic default value.
|
||||
# pvsegs_cols_full = "pvseg_all,pv_uuid,lv_uuid"
|
||||
|
||||
# Configuration option report/segs_cols_full.
|
||||
# List of columns to report for lvm fullreport's 'seg' subreport.
|
||||
# See 'lvs --segments -o help' for the list of possible fields.
|
||||
# This configuration option has an automatic default value.
|
||||
# segs_cols_full = "seg_all,lv_uuid"
|
||||
|
||||
# Configuration option report/vgs_sort_full.
|
||||
# List of columns to sort by when reporting lvm fullreport's 'vgs' subreport.
|
||||
# See 'vgs -o help' for the list of possible fields.
|
||||
# This configuration option has an automatic default value.
|
||||
# vgs_sort_full = "vg_name"
|
||||
|
||||
# Configuration option report/pvs_sort_full.
|
||||
# List of columns to sort by when reporting lvm fullreport's 'vgs' subreport.
|
||||
# See 'pvs -o help' for the list of possible fields.
|
||||
# This configuration option has an automatic default value.
|
||||
# pvs_sort_full = "pv_name"
|
||||
|
||||
# Configuration option report/lvs_sort_full.
|
||||
# List of columns to sort by when reporting lvm fullreport's 'lvs' subreport.
|
||||
# See 'lvs -o help' for the list of possible fields.
|
||||
# This configuration option has an automatic default value.
|
||||
# lvs_sort_full = "vg_name,lv_name"
|
||||
|
||||
# Configuration option report/pvsegs_sort_full.
|
||||
# List of columns to sort by when reporting for lvm fullreport's 'pvseg' subreport.
|
||||
# See 'pvs --segments -o help' for the list of possible fields.
|
||||
# This configuration option has an automatic default value.
|
||||
# pvsegs_sort_full = "pv_uuid,pvseg_start"
|
||||
|
||||
# Configuration option report/segs_sort_full.
|
||||
# List of columns to sort by when reporting lvm fullreport's 'seg' subreport.
|
||||
# See 'lvs --segments -o help' for the list of possible fields.
|
||||
# This configuration option has an automatic default value.
|
||||
# segs_sort_full = "lv_uuid,seg_start"
|
||||
|
||||
# Configuration option report/mark_hidden_devices.
|
||||
# Use brackets [] to mark hidden devices.
|
||||
# This configuration option has an automatic default value.
|
||||
|
50
conf/lvmdbusd.profile
Normal file
50
conf/lvmdbusd.profile
Normal file
@@ -0,0 +1,50 @@
|
||||
#
|
||||
# DO NOT EDIT THIS FILE!
|
||||
#
|
||||
# LVM configuration profile used by lvmdbusd daemon.
|
||||
#
|
||||
# This sets up LVM to produce output in the most suitable format for processing
|
||||
# by lvmdbusd daemon which utilizes LVM shell to execute LVM commands.
|
||||
#
|
||||
# Do not edit this file in any way. This profile is distributed together with
|
||||
# lvmdbusd and it contains configuration that is important for lvmdbusd to
|
||||
# cooperate and interface with LVM correctly.
|
||||
#
|
||||
|
||||
global {
|
||||
# use bytes for expected and deterministic output
|
||||
units=b
|
||||
# no need for suffix if we have units set
|
||||
suffix=0
|
||||
}
|
||||
|
||||
report {
|
||||
compact_output=0
|
||||
compact_output_cols=""
|
||||
binary_values_as_numeric=0
|
||||
# time in number of seconds since the Epoch
|
||||
time_format="%s"
|
||||
mark_hidden_devices=1
|
||||
# lvmdbusd expects JSON output
|
||||
output_format=json
|
||||
# *_cols_full for lvm fullreport's fields which lvmdbusd relies on to update its state
|
||||
vgs_cols_full="vg_name,vg_uuid,vg_fmt,vg_size,vg_free,vg_sysid,vg_extent_size,vg_extent_count,vg_free_count,vg_profile,max_lv,max_pv,pv_count,lv_count,snap_count,vg_seqno,vg_mda_count,vg_mda_free,vg_mda_size,vg_mda_used_count,vg_attr,vg_tags"
|
||||
pvs_cols_full="pv_name,pv_uuid,pv_fmt,pv_size,pv_free,pv_used,dev_size,pv_mda_size,pv_mda_free,pv_ba_start,pv_ba_size,pe_start,pv_pe_count,pv_pe_alloc_count,pv_attr,pv_tags,vg_name,vg_uuid"
|
||||
lvs_cols_full="lv_uuid,lv_name,lv_path,lv_size,vg_name,pool_lv_uuid,pool_lv,origin_uuid,origin,data_percent,lv_attr,lv_tags,vg_uuid,lv_active,data_lv,metadata_lv,lv_parent,lv_role,lv_layout"
|
||||
pvsegs_cols_full="pvseg_start,pvseg_size,segtype,pv_uuid,lv_uuid,pv_name"
|
||||
segs_cols_full="seg_pe_ranges,segtype,lv_uuid"
|
||||
vgs_sort_full="vg_name"
|
||||
pvs_sort_full="pv_name"
|
||||
lvs_sort_full="vg_name,lv_name"
|
||||
pvsegs_sort_full="pv_uuid,pvseg_start"
|
||||
segs_sort_full="lv_uuid,seg_start"
|
||||
}
|
||||
|
||||
log {
|
||||
# lvmdbusd relies on command log report to inspect LVM command's execution status
|
||||
report_command_log=1
|
||||
# display only outermost LVM shell-related log that lvmdbusd inspects first after LVM command execution (it calls 'lastlog' for more detailed log afterwards if needed)
|
||||
command_log_selection="log_context=shell"
|
||||
command_log_cols="log_seq_num,log_type,log_context,log_object_type,log_object_name,log_object_id,log_object_group,log_object_group_id,log_message,log_errno,log_ret_code"
|
||||
command_log_sort="log_seq_num"
|
||||
}
|
7
configure
vendored
7
configure
vendored
@@ -676,6 +676,7 @@ PTHREAD_LIBS
|
||||
M_LIBS
|
||||
POOL
|
||||
PKGCONFIG
|
||||
ODIRECT
|
||||
OCFDIR
|
||||
OCF
|
||||
MIRRORS
|
||||
@@ -5875,7 +5876,7 @@ fi
|
||||
done
|
||||
|
||||
|
||||
for ac_header in termios.h sys/statvfs.h sys/timerfd.h
|
||||
for ac_header in termios.h sys/statvfs.h sys/timerfd.h linux/magic.h linux/fiemap.h
|
||||
do :
|
||||
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
|
||||
ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
|
||||
@@ -15339,10 +15340,11 @@ LVM_LIBAPI=`echo "$VER" | $AWK -F '[()]' '{print $2}'`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
################################################################################
|
||||
ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/raid/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile daemons/dmeventd/plugins/thin/Makefile daemons/lvmdbusd/Makefile daemons/lvmdbusd/path.py daemons/lvmetad/Makefile daemons/lvmpolld/Makefile daemons/lvmlockd/Makefile conf/Makefile conf/example.conf conf/lvmlocal.conf conf/command_profile_template.profile conf/metadata_profile_template.profile include/.symlinks include/Makefile lib/Makefile lib/format1/Makefile lib/format_pool/Makefile lib/locking/Makefile lib/mirror/Makefile lib/replicator/Makefile include/lvm-version.h lib/raid/Makefile lib/snapshot/Makefile lib/thin/Makefile lib/cache_segtype/Makefile libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/libdevmapper.pc liblvm/Makefile liblvm/liblvm2app.pc man/Makefile po/Makefile python/Makefile python/setup.py scripts/blkdeactivate.sh scripts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/clvmd_init_red_hat scripts/cmirrord_init_red_hat scripts/com.redhat.lvmdbus1.service scripts/dm_event_systemd_red_hat.service scripts/dm_event_systemd_red_hat.socket scripts/lvm2_cluster_activation_red_hat.sh scripts/lvm2_cluster_activation_systemd_red_hat.service scripts/lvm2_clvmd_systemd_red_hat.service scripts/lvm2_cmirrord_systemd_red_hat.service scripts/lvm2_lvmdbusd_systemd_red_hat.service scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmpolld_init_red_hat scripts/lvm2_lvmpolld_systemd_red_hat.service scripts/lvm2_lvmpolld_systemd_red_hat.socket scripts/lvm2_lvmlockd_systemd_red_hat.service scripts/lvm2_lvmlocking_systemd_red_hat.service scripts/lvm2_monitoring_init_red_hat scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_tmpfiles_red_hat.conf scripts/Makefile test/Makefile test/api/Makefile test/unit/Makefile tools/Makefile udev/Makefile unit-tests/datastruct/Makefile unit-tests/regex/Makefile unit-tests/mm/Makefile"
|
||||
ac_config_files="$ac_config_files Makefile make.tmpl daemons/Makefile daemons/clvmd/Makefile daemons/cmirrord/Makefile daemons/dmeventd/Makefile daemons/dmeventd/libdevmapper-event.pc daemons/dmeventd/plugins/Makefile daemons/dmeventd/plugins/lvm2/Makefile daemons/dmeventd/plugins/raid/Makefile daemons/dmeventd/plugins/mirror/Makefile daemons/dmeventd/plugins/snapshot/Makefile daemons/dmeventd/plugins/thin/Makefile daemons/lvmdbusd/Makefile daemons/lvmdbusd/path.py daemons/lvmetad/Makefile daemons/lvmpolld/Makefile daemons/lvmlockd/Makefile conf/Makefile conf/example.conf conf/lvmlocal.conf conf/command_profile_template.profile conf/metadata_profile_template.profile include/.symlinks include/Makefile lib/Makefile lib/format1/Makefile lib/format_pool/Makefile lib/locking/Makefile lib/mirror/Makefile lib/replicator/Makefile include/lvm-version.h lib/raid/Makefile lib/snapshot/Makefile lib/thin/Makefile lib/cache_segtype/Makefile libdaemon/Makefile libdaemon/client/Makefile libdaemon/server/Makefile libdm/Makefile libdm/libdevmapper.pc liblvm/Makefile liblvm/liblvm2app.pc man/Makefile po/Makefile python/Makefile python/setup.py scripts/blkdeactivate.sh scripts/blk_availability_init_red_hat scripts/blk_availability_systemd_red_hat.service scripts/clvmd_init_red_hat scripts/cmirrord_init_red_hat scripts/com.redhat.lvmdbus1.service scripts/dm_event_systemd_red_hat.service scripts/dm_event_systemd_red_hat.socket scripts/lvm2_cluster_activation_red_hat.sh scripts/lvm2_cluster_activation_systemd_red_hat.service scripts/lvm2_clvmd_systemd_red_hat.service scripts/lvm2_cmirrord_systemd_red_hat.service scripts/lvm2_lvmdbusd_systemd_red_hat.service scripts/lvm2_lvmetad_init_red_hat scripts/lvm2_lvmetad_systemd_red_hat.service scripts/lvm2_lvmetad_systemd_red_hat.socket scripts/lvm2_lvmpolld_init_red_hat scripts/lvm2_lvmpolld_systemd_red_hat.service scripts/lvm2_lvmpolld_systemd_red_hat.socket scripts/lvm2_lvmlockd_systemd_red_hat.service scripts/lvm2_lvmlocking_systemd_red_hat.service scripts/lvm2_monitoring_init_red_hat scripts/lvm2_monitoring_systemd_red_hat.service scripts/lvm2_pvscan_systemd_red_hat@.service scripts/lvm2_tmpfiles_red_hat.conf scripts/lvmdump.sh scripts/Makefile test/Makefile test/api/Makefile test/unit/Makefile tools/Makefile udev/Makefile unit-tests/datastruct/Makefile unit-tests/regex/Makefile unit-tests/mm/Makefile"
|
||||
|
||||
cat >confcache <<\_ACEOF
|
||||
# This file is a shell script that caches the results of configure
|
||||
@@ -16109,6 +16111,7 @@ do
|
||||
"scripts/lvm2_monitoring_systemd_red_hat.service") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_monitoring_systemd_red_hat.service" ;;
|
||||
"scripts/lvm2_pvscan_systemd_red_hat@.service") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_pvscan_systemd_red_hat@.service" ;;
|
||||
"scripts/lvm2_tmpfiles_red_hat.conf") CONFIG_FILES="$CONFIG_FILES scripts/lvm2_tmpfiles_red_hat.conf" ;;
|
||||
"scripts/lvmdump.sh") CONFIG_FILES="$CONFIG_FILES scripts/lvmdump.sh" ;;
|
||||
"scripts/Makefile") CONFIG_FILES="$CONFIG_FILES scripts/Makefile" ;;
|
||||
"test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;;
|
||||
"test/api/Makefile") CONFIG_FILES="$CONFIG_FILES test/api/Makefile" ;;
|
||||
|
@@ -103,7 +103,7 @@ AC_CHECK_HEADERS([assert.h ctype.h dirent.h errno.h fcntl.h float.h \
|
||||
sys/time.h sys/types.h sys/utsname.h sys/wait.h time.h \
|
||||
unistd.h], , [AC_MSG_ERROR(bailing out)])
|
||||
|
||||
AC_CHECK_HEADERS(termios.h sys/statvfs.h sys/timerfd.h)
|
||||
AC_CHECK_HEADERS(termios.h sys/statvfs.h sys/timerfd.h linux/magic.h linux/fiemap.h)
|
||||
|
||||
case "$host_os" in
|
||||
linux*)
|
||||
@@ -2068,6 +2068,7 @@ AC_SUBST(MIRRORS)
|
||||
AC_SUBST(MSGFMT)
|
||||
AC_SUBST(OCF)
|
||||
AC_SUBST(OCFDIR)
|
||||
AC_SUBST(ODIRECT)
|
||||
AC_SUBST(PKGCONFIG)
|
||||
AC_SUBST(POOL)
|
||||
AC_SUBST(M_LIBS)
|
||||
@@ -2213,6 +2214,7 @@ scripts/lvm2_monitoring_init_red_hat
|
||||
scripts/lvm2_monitoring_systemd_red_hat.service
|
||||
scripts/lvm2_pvscan_systemd_red_hat@.service
|
||||
scripts/lvm2_tmpfiles_red_hat.conf
|
||||
scripts/lvmdump.sh
|
||||
scripts/Makefile
|
||||
test/Makefile
|
||||
test/api/Makefile
|
||||
|
@@ -12,6 +12,7 @@
|
||||
#include "logging.h"
|
||||
#include "functions.h"
|
||||
|
||||
#include <sys/sysmacros.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
@@ -573,6 +574,12 @@ static int clog_ctr(struct dm_ulog_request *rq)
|
||||
for (argc = 0, p = rq->data; (p = strstr(p, " ")); p++, argc++)
|
||||
*p = '\0';
|
||||
|
||||
if (!argc) {
|
||||
LOG_ERROR("Received constructor request with bad data %s",
|
||||
rq->data);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
argv = malloc(argc * sizeof(char *));
|
||||
if (!argv)
|
||||
return -ENOMEM;
|
||||
|
@@ -99,13 +99,28 @@ static time_t _idle_since = 0;
|
||||
static char **_initial_registrations = 0;
|
||||
|
||||
/* FIXME Make configurable at runtime */
|
||||
__attribute__((format(printf, 4, 5)))
|
||||
static void _dmeventd_log(int level, const char *file, int line,
|
||||
const char *format, ...)
|
||||
|
||||
/* All libdm messages */
|
||||
__attribute__((format(printf, 5, 6)))
|
||||
static void _libdm_log(int level, const char *file, int line,
|
||||
int dm_errno_or_class, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
dm_event_log("dm", level, file, line, 0, format, ap);
|
||||
dm_event_log("#dm", level, file, line, dm_errno_or_class, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/* All dmeventd messages */
|
||||
#undef LOG_MESG
|
||||
#define LOG_MESG(l, f, ln, e, x...) _dmeventd_log(l, f, ln, e, ## x)
|
||||
__attribute__((format(printf, 5, 6)))
|
||||
static void _dmeventd_log(int level, const char *file, int line,
|
||||
int dm_errno_or_class, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
dm_event_log("dmeventd", level, file, line, dm_errno_or_class, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
@@ -829,17 +844,6 @@ static void _print_sigset(const char *prefix, const sigset_t *sigset)
|
||||
}
|
||||
#endif
|
||||
|
||||
static sigset_t _unblock_sigalrm(void)
|
||||
{
|
||||
sigset_t set, old;
|
||||
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIGALRM);
|
||||
pthread_sigmask(SIG_UNBLOCK, &set, &old);
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
enum {
|
||||
DM_WAIT_RETRY,
|
||||
DM_WAIT_INTR,
|
||||
@@ -849,7 +853,7 @@ enum {
|
||||
/* Wait on a device until an event occurs. */
|
||||
static int _event_wait(struct thread_status *thread)
|
||||
{
|
||||
sigset_t set;
|
||||
sigset_t set, old;
|
||||
int ret = DM_WAIT_RETRY;
|
||||
struct dm_info info;
|
||||
|
||||
@@ -859,7 +863,12 @@ static int _event_wait(struct thread_status *thread)
|
||||
* This is so that you can break out of waiting on an event,
|
||||
* either for a timeout event, or to cancel the thread.
|
||||
*/
|
||||
set = _unblock_sigalrm();
|
||||
sigemptyset(&set);
|
||||
sigaddset(&set, SIGALRM);
|
||||
if (pthread_sigmask(SIG_UNBLOCK, &set, &old) != 0) {
|
||||
log_sys_error("pthread_sigmask", "unblock alarm");
|
||||
return ret; /* What better */
|
||||
}
|
||||
|
||||
if (dm_task_run(thread->wait_task)) {
|
||||
thread->current_events |= DM_EVENT_DEVICE_ERROR;
|
||||
@@ -883,10 +892,11 @@ static int _event_wait(struct thread_status *thread)
|
||||
}
|
||||
}
|
||||
|
||||
pthread_sigmask(SIG_SETMASK, &set, NULL);
|
||||
if (pthread_sigmask(SIG_SETMASK, &old, NULL) != 0)
|
||||
log_sys_error("pthread_sigmask", "block alarm");
|
||||
|
||||
#ifdef DEBUG_SIGNALS
|
||||
_print_sigset("dmeventd blocking ", &set);
|
||||
_print_sigset("dmeventd blocking ", &old);
|
||||
#endif
|
||||
DEBUGLOG("Completed waitevent task for %s.", thread->device.name);
|
||||
|
||||
@@ -2196,7 +2206,7 @@ int main(int argc, char *argv[])
|
||||
openlog("dmeventd", LOG_PID, LOG_DAEMON);
|
||||
|
||||
dm_event_log_set(_debug_level, _use_syslog);
|
||||
dm_log_init(_dmeventd_log);
|
||||
dm_log_with_errno_init(_libdm_log);
|
||||
|
||||
(void) dm_prepare_selinux_context(DMEVENTD_PIDFILE, S_IFREG);
|
||||
if (dm_create_lockfile(DMEVENTD_PIDFILE) == 0)
|
||||
|
@@ -865,28 +865,38 @@ void dm_event_log(const char *subsys, int level, const char *file,
|
||||
int line, int dm_errno_or_class,
|
||||
const char *format, va_list ap)
|
||||
{
|
||||
static int _abort_on_internal_errors = -1;
|
||||
static pthread_mutex_t _log_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static time_t start = 0;
|
||||
const char *indent = "";
|
||||
FILE *stream = stdout;
|
||||
FILE *stream = log_stderr(level) ? stderr : stdout;
|
||||
int prio;
|
||||
time_t now;
|
||||
int log_with_debug = 0;
|
||||
|
||||
switch (level & ~(_LOG_STDERR | _LOG_ONCE)) {
|
||||
if (subsys[0] == '#') {
|
||||
/* Subsystems starting with '#' are logged
|
||||
* only when debugging is enabled. */
|
||||
log_with_debug++;
|
||||
subsys++;
|
||||
}
|
||||
|
||||
switch (log_level(level)) {
|
||||
case _LOG_DEBUG:
|
||||
/* Never shown without -ddd */
|
||||
if (_debug_level < 3)
|
||||
return;
|
||||
prio = LOG_DEBUG;
|
||||
indent = " ";
|
||||
break;
|
||||
case _LOG_INFO:
|
||||
if (_debug_level < 2)
|
||||
if (log_with_debug && _debug_level < 2)
|
||||
return;
|
||||
prio = LOG_INFO;
|
||||
indent = " ";
|
||||
break;
|
||||
case _LOG_NOTICE:
|
||||
if (_debug_level < 1)
|
||||
if (log_with_debug && _debug_level < 1)
|
||||
return;
|
||||
prio = LOG_NOTICE;
|
||||
indent = " ";
|
||||
@@ -912,12 +922,13 @@ void dm_event_log(const char *subsys, int level, const char *file,
|
||||
if (!start)
|
||||
start = now;
|
||||
now -= start;
|
||||
fprintf(stream, "[%2d:%02d] %8x:%-6s%s",
|
||||
(int)now / 60, (int)now % 60,
|
||||
// TODO: Maybe use shorter ID
|
||||
// ((int)(pthread_self()) >> 6) & 0xffff,
|
||||
(int)pthread_self(), subsys,
|
||||
(_debug_level > 3) ? "" : indent);
|
||||
if (_debug_level)
|
||||
fprintf(stream, "[%2d:%02d] %8x:%-6s%s",
|
||||
(int)now / 60, (int)now % 60,
|
||||
// TODO: Maybe use shorter ID
|
||||
// ((int)(pthread_self()) >> 6) & 0xffff,
|
||||
(int)pthread_self(), subsys,
|
||||
(_debug_level > 3) ? "" : indent);
|
||||
if (_debug_level > 3)
|
||||
fprintf(stream, "%28s:%4d %s", file, line, indent);
|
||||
vfprintf(stream, _(format), ap);
|
||||
@@ -926,6 +937,15 @@ void dm_event_log(const char *subsys, int level, const char *file,
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&_log_mutex);
|
||||
|
||||
if (_abort_on_internal_errors < 0)
|
||||
/* Set when env DM_ABORT_ON_INTERNAL_ERRORS is not "0" */
|
||||
_abort_on_internal_errors =
|
||||
strcmp(getenv("DM_ABORT_ON_INTERNAL_ERRORS") ? : "0", "0");
|
||||
|
||||
if (_abort_on_internal_errors &&
|
||||
!strncmp(format, INTERNAL_ERROR, sizeof(INTERNAL_ERROR) - 1))
|
||||
abort();
|
||||
}
|
||||
|
||||
#if 0 /* left out for now */
|
||||
|
@@ -32,7 +32,7 @@ static int _register_count = 0;
|
||||
static struct dm_pool *_mem_pool = NULL;
|
||||
static void *_lvm_handle = NULL;
|
||||
|
||||
DM_EVENT_LOG_FN("lvm")
|
||||
DM_EVENT_LOG_FN("#lvm")
|
||||
|
||||
static void _lvm2_print_log(int level, const char *file, int line,
|
||||
int dm_errno_or_class, const char *msg)
|
||||
|
@@ -73,8 +73,10 @@ static int _get_mirror_event(struct dso_state *state, char *params)
|
||||
unsigned i;
|
||||
struct dm_status_mirror *ms;
|
||||
|
||||
if (!dm_get_status_mirror(state->mem, params, &ms))
|
||||
goto_out;
|
||||
if (!dm_get_status_mirror(state->mem, params, &ms)) {
|
||||
log_error("Unable to parse mirror status string.");
|
||||
return ME_IGNORE;
|
||||
}
|
||||
|
||||
/* Check for bad mirror devices */
|
||||
for (i = 0; i < ms->dev_count; ++i)
|
||||
@@ -95,27 +97,23 @@ static int _get_mirror_event(struct dso_state *state, char *params)
|
||||
dm_pool_free(state->mem, ms);
|
||||
|
||||
return r;
|
||||
|
||||
out:
|
||||
log_error("Unable to parse mirror status string.");
|
||||
|
||||
return ME_IGNORE;
|
||||
}
|
||||
|
||||
static int _remove_failed_devices(const char *cmd_lvscan, const char *cmd_lvconvert)
|
||||
static int _remove_failed_devices(const char *cmd_lvscan, const char *cmd_lvconvert,
|
||||
const char *device)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (!dmeventd_lvm2_run_with_lock(cmd_lvscan))
|
||||
log_info("Re-scan of mirrored device failed.");
|
||||
log_warn("WARNING: Re-scan of mirrored device %s failed.", device);
|
||||
|
||||
/* if repair goes OK, report success even if lvscan has failed */
|
||||
r = dmeventd_lvm2_run_with_lock(cmd_lvconvert);
|
||||
if (!dmeventd_lvm2_run_with_lock(cmd_lvconvert)) {
|
||||
log_error("Repair of mirrored device %s failed.", device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_info("Repair of mirrored device %s.",
|
||||
(r) ? "finished successfully" : "failed");
|
||||
log_info("Repair of mirrored device %s finished successfully.", device);
|
||||
|
||||
return r;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void process_event(struct dm_task *dmt,
|
||||
@@ -154,7 +152,8 @@ void process_event(struct dm_task *dmt,
|
||||
case ME_FAILURE:
|
||||
log_error("Device failure in %s.", device);
|
||||
if (!_remove_failed_devices(state->cmd_lvscan,
|
||||
state->cmd_lvconvert))
|
||||
state->cmd_lvconvert,
|
||||
device))
|
||||
/* FIXME Why are all the error return codes unused? Get rid of them? */
|
||||
log_error("Failed to remove faulty devices in %s.",
|
||||
device);
|
||||
@@ -168,7 +167,7 @@ void process_event(struct dm_task *dmt,
|
||||
break;
|
||||
default:
|
||||
/* FIXME Provide value then! */
|
||||
log_info("Unknown event received.");
|
||||
log_warn("WARNING: %s received unknown event.", device);
|
||||
}
|
||||
} while (next);
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2015 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2005-2016 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@@ -13,14 +13,20 @@
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "defaults.h"
|
||||
#include "dmeventd_lvm.h"
|
||||
#include "libdevmapper-event.h"
|
||||
|
||||
/* Hold enough elements for the mximum number of RAID images */
|
||||
#define RAID_DEVS_ELEMS ((DEFAULT_RAID_MAX_IMAGES + 63) / 64)
|
||||
|
||||
struct dso_state {
|
||||
struct dm_pool *mem;
|
||||
char cmd_lvscan[512];
|
||||
char cmd_lvconvert[512];
|
||||
uint64_t raid_devs[RAID_DEVS_ELEMS];
|
||||
int failed;
|
||||
int warned;
|
||||
};
|
||||
|
||||
DM_EVENT_LOG_FN("raid")
|
||||
@@ -31,29 +37,47 @@ static int _process_raid_event(struct dso_state *state, char *params, const char
|
||||
{
|
||||
struct dm_status_raid *status;
|
||||
const char *d;
|
||||
int dead = 0, r = 1;
|
||||
|
||||
if (!dm_get_status_raid(state->mem, params, &status)) {
|
||||
log_error("Failed to process status line for %s.", device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((d = strchr(status->dev_health, 'D'))) {
|
||||
d = status->dev_health;
|
||||
while ((d = strchr(d, 'D'))) {
|
||||
uint32_t dev = (uint32_t)(d - status->dev_health);
|
||||
|
||||
if (!(state->raid_devs[dev / 64] & (UINT64_C(1) << (dev % 64))))
|
||||
log_error("Device #%u of %s array, %s, has failed.",
|
||||
dev, status->raid_type, device);
|
||||
|
||||
state->raid_devs[dev / 64] |= (UINT64_C(1) << (dev % 64));
|
||||
d++;
|
||||
dead = 1;
|
||||
}
|
||||
|
||||
if (dead) {
|
||||
if (status->insync_regions < status->total_regions) {
|
||||
if (!state->warned)
|
||||
log_warn("WARNING: waiting for resynchronization to finish "
|
||||
"before initiating repair on RAID device %s", device);
|
||||
|
||||
state->warned = 1;
|
||||
goto out; /* Not yet done syncing with accessible devices */
|
||||
}
|
||||
|
||||
if (state->failed)
|
||||
goto out; /* already reported */
|
||||
|
||||
log_error("Device #%d of %s array, %s, has failed.",
|
||||
(int)(d - status->dev_health),
|
||||
status->raid_type, device);
|
||||
|
||||
state->failed = 1;
|
||||
if (!dmeventd_lvm2_run_with_lock(state->cmd_lvscan))
|
||||
log_warn("WARNING: Re-scan of RAID device %s failed.", device);
|
||||
|
||||
/* if repair goes OK, report success even if lvscan has failed */
|
||||
if (!dmeventd_lvm2_run_with_lock(state->cmd_lvconvert)) {
|
||||
log_info("Repair of RAID device %s failed.", device);
|
||||
dm_pool_free(state->mem, status);
|
||||
return 0;
|
||||
log_error("Repair of RAID device %s failed.", device);
|
||||
r = 0;
|
||||
}
|
||||
} else {
|
||||
state->failed = 0;
|
||||
@@ -64,7 +88,7 @@ static int _process_raid_event(struct dso_state *state, char *params, const char
|
||||
out:
|
||||
dm_pool_free(state->mem, status);
|
||||
|
||||
return 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
void process_event(struct dm_task *dmt,
|
||||
|
@@ -16,6 +16,7 @@
|
||||
#include "dmeventd_lvm.h"
|
||||
#include "libdevmapper-event.h"
|
||||
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/wait.h>
|
||||
#include <stdarg.h>
|
||||
#include <pthread.h>
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2015 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2011-2016 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@@ -30,6 +30,9 @@
|
||||
|
||||
/* First warning when thin data or metadata is 80% full. */
|
||||
#define WARNING_THRESH (DM_PERCENT_1 * 80)
|
||||
/* Umount thin LVs when thin data or metadata LV is >=
|
||||
* and lvextend --use-policies has failed. */
|
||||
#define UMOUNT_THRESH (DM_PERCENT_1 * 95)
|
||||
/* Run a check every 5%. */
|
||||
#define CHECK_STEP (DM_PERCENT_1 * 5)
|
||||
/* Do not bother checking thin data or metadata is less than 50% full. */
|
||||
@@ -53,6 +56,53 @@ struct dso_state {
|
||||
|
||||
DM_EVENT_LOG_FN("thin")
|
||||
|
||||
#define UUID_PREFIX "LVM-"
|
||||
|
||||
/* Figure out device UUID has LVM- prefix and is OPEN */
|
||||
static int _has_unmountable_prefix(int major, int minor)
|
||||
{
|
||||
struct dm_task *dmt;
|
||||
struct dm_info info;
|
||||
const char *uuid;
|
||||
int r = 0;
|
||||
|
||||
if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
|
||||
return_0;
|
||||
|
||||
if (!dm_task_set_major_minor(dmt, major, minor, 1))
|
||||
goto_out;
|
||||
|
||||
if (!dm_task_no_flush(dmt))
|
||||
stack;
|
||||
|
||||
if (!dm_task_run(dmt))
|
||||
goto out;
|
||||
|
||||
if (!dm_task_get_info(dmt, &info))
|
||||
goto out;
|
||||
|
||||
if (!info.exists || !info.open_count)
|
||||
goto out; /* Not open -> not mounted */
|
||||
|
||||
if (!(uuid = dm_task_get_uuid(dmt)))
|
||||
goto out;
|
||||
|
||||
/* Check it's public mountable LV
|
||||
* has prefix LVM- and UUID size is 68 chars */
|
||||
if (memcmp(uuid, UUID_PREFIX, sizeof(UUID_PREFIX) - 1) ||
|
||||
strlen(uuid) != 68)
|
||||
goto out;
|
||||
|
||||
#if THIN_DEBUG
|
||||
log_debug("Found logical volume %s (%u:%u).", uuid, major, minor);
|
||||
#endif
|
||||
r = 1;
|
||||
out:
|
||||
dm_task_destroy(dmt);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Get dependencies for device, and try to find matching device */
|
||||
static int _has_deps(const char *name, int tp_major, int tp_minor, int *dev_minor)
|
||||
{
|
||||
@@ -90,6 +140,9 @@ static int _has_deps(const char *name, int tp_major, int tp_minor, int *dev_mino
|
||||
|
||||
*dev_minor = info.minor;
|
||||
|
||||
if (!_has_unmountable_prefix(major, info.minor))
|
||||
goto out;
|
||||
|
||||
#if THIN_DEBUG
|
||||
{
|
||||
char dev_name[PATH_MAX];
|
||||
@@ -193,20 +246,25 @@ static int _umount_device(char *buffer, unsigned major, unsigned minor,
|
||||
char *target, void *cb_data)
|
||||
{
|
||||
struct mountinfo_s *data = cb_data;
|
||||
char *words[10];
|
||||
|
||||
if ((major == data->info.major) && dm_bit(data->minors, minor)) {
|
||||
log_info("Unmounting thin volume %s from %s.",
|
||||
data->device, target);
|
||||
if (dm_split_words(buffer, DM_ARRAY_SIZE(words), 0, words) < DM_ARRAY_SIZE(words))
|
||||
words[9] = NULL; /* just don't show device name */
|
||||
log_info("Unmounting thin %s (%d:%d) of thin pool %s (%u:%u) from mount point \"%s\".",
|
||||
words[9] ? : "", major, minor, data->device,
|
||||
data->info.major, data->info.minor,
|
||||
target);
|
||||
if (!_run(UMOUNT_COMMAND, "-fl", target, NULL))
|
||||
log_error("Failed to umount thin %s from %s: %s.",
|
||||
data->device, target, strerror(errno));
|
||||
log_error("Failed to lazy umount thin %s (%d:%d) from %s: %s.",
|
||||
words[9], major, minor, target, strerror(errno));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find all thin pool users and try to umount them.
|
||||
* Find all thin pool LV users and try to umount them.
|
||||
* TODO: work with read-only thin pool support
|
||||
*/
|
||||
static void _umount(struct dm_task *dmt)
|
||||
@@ -240,18 +298,20 @@ out:
|
||||
dm_bitset_destroy(data.minors);
|
||||
}
|
||||
|
||||
static void _use_policy(struct dm_task *dmt, struct dso_state *state)
|
||||
static int _use_policy(struct dm_task *dmt, struct dso_state *state)
|
||||
{
|
||||
#if THIN_DEBUG
|
||||
log_info("dmeventd executes: %s.", state->cmd_str);
|
||||
log_debug("dmeventd executes: %s.", state->cmd_str);
|
||||
#endif
|
||||
if (!dmeventd_lvm2_run_with_lock(state->cmd_str)) {
|
||||
log_error("Failed to extend thin pool %s.",
|
||||
dm_task_get_name(dmt));
|
||||
_umount(dmt);
|
||||
state->fails++;
|
||||
} else
|
||||
state->fails = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
state->fails = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void process_event(struct dm_task *dmt,
|
||||
@@ -267,6 +327,13 @@ void process_event(struct dm_task *dmt,
|
||||
char *target_type = NULL;
|
||||
char *params;
|
||||
int needs_policy = 0;
|
||||
int needs_umount = 0;
|
||||
|
||||
#if THIN_DEBUG
|
||||
log_debug("Watch for tp-data:%.2f%% tp-metadata:%.2f%%.",
|
||||
dm_percent_to_float(state->data_percent_check),
|
||||
dm_percent_to_float(state->metadata_percent_check));
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/* No longer monitoring, waiting for remove */
|
||||
@@ -275,8 +342,10 @@ void process_event(struct dm_task *dmt,
|
||||
#endif
|
||||
if (event & DM_EVENT_DEVICE_ERROR) {
|
||||
/* Error -> no need to check and do instant resize */
|
||||
_use_policy(dmt, state);
|
||||
goto out;
|
||||
if (_use_policy(dmt, state))
|
||||
goto out;
|
||||
|
||||
stack;
|
||||
}
|
||||
|
||||
dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
|
||||
@@ -288,7 +357,7 @@ void process_event(struct dm_task *dmt,
|
||||
|
||||
if (!dm_get_status_thin_pool(state->mem, params, &tps)) {
|
||||
log_error("Failed to parse status.");
|
||||
_umount(dmt);
|
||||
needs_umount = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -323,6 +392,9 @@ void process_event(struct dm_task *dmt,
|
||||
log_warn("WARNING: Thin pool %s metadata is now %.2f%% full.",
|
||||
device, dm_percent_to_float(percent));
|
||||
needs_policy = 1;
|
||||
|
||||
if (percent >= UMOUNT_THRESH)
|
||||
needs_umount = 1;
|
||||
}
|
||||
|
||||
percent = dm_make_percent(tps->used_data_blocks, tps->total_data_blocks);
|
||||
@@ -337,17 +409,27 @@ void process_event(struct dm_task *dmt,
|
||||
log_warn("WARNING: Thin pool %s data is now %.2f%% full.",
|
||||
device, dm_percent_to_float(percent));
|
||||
needs_policy = 1;
|
||||
|
||||
if (percent >= UMOUNT_THRESH)
|
||||
needs_umount = 1;
|
||||
}
|
||||
|
||||
if (needs_policy)
|
||||
_use_policy(dmt, state);
|
||||
if (needs_policy &&
|
||||
_use_policy(dmt, state))
|
||||
needs_umount = 0; /* No umount when command was successful */
|
||||
out:
|
||||
if (needs_umount) {
|
||||
_umount(dmt);
|
||||
/* Until something changes, do not retry any more actions */
|
||||
state->data_percent_check = state->metadata_percent_check = (DM_PERCENT_1 * 101);
|
||||
}
|
||||
|
||||
if (tps)
|
||||
dm_pool_free(state->mem, tps);
|
||||
|
||||
if (state->fails >= MAX_FAILS) {
|
||||
log_warn("WARNING: Dropping monitoring of %s. "
|
||||
"lvm2 command fails too often (%u times in raw).",
|
||||
"lvm2 command fails too often (%u times in row).",
|
||||
device, state->fails);
|
||||
pthread_kill(pthread_self(), SIGALRM);
|
||||
}
|
||||
@@ -376,11 +458,11 @@ int register_device(const char *device,
|
||||
state->data_percent_check = CHECK_MINIMUM;
|
||||
*user = state;
|
||||
|
||||
log_info("Monitoring thin %s.", device);
|
||||
log_info("Monitoring thin pool %s.", device);
|
||||
|
||||
return 1;
|
||||
bad:
|
||||
log_error("Failed to monitor thin %s.", device);
|
||||
log_error("Failed to monitor thin pool %s.", device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -394,7 +476,7 @@ int unregister_device(const char *device,
|
||||
struct dso_state *state = *user;
|
||||
|
||||
dmeventd_lvm2_exit_with_pool(state);
|
||||
log_info("No longer monitoring thin %s.", device);
|
||||
log_info("No longer monitoring thin pool %s.", device);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@@ -63,3 +63,5 @@ install_lvm2: install_lvmdbusd
|
||||
|
||||
install: install_lvm2
|
||||
|
||||
DISTCLEAN_TARGETS+= \
|
||||
$(LVMDBUS_BUILDDIR_FILES)
|
||||
|
@@ -8,6 +8,7 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import dbus
|
||||
import dbus.service
|
||||
from . import cfg
|
||||
from .utils import get_properties, add_properties, get_object_property_diff, \
|
||||
log_debug
|
||||
|
@@ -7,19 +7,12 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import threading
|
||||
import subprocess
|
||||
from . import cfg
|
||||
import time
|
||||
from .cmdhandler import options_to_cli_args
|
||||
import dbus
|
||||
from .job import Job, JobState
|
||||
from .utils import pv_range_append, pv_dest_ranges, log_debug, log_error
|
||||
from .request import RequestEntry
|
||||
import traceback
|
||||
|
||||
_rlock = threading.RLock()
|
||||
_thread_list = list()
|
||||
from .utils import pv_range_append, pv_dest_ranges, log_error, log_debug
|
||||
import os
|
||||
|
||||
|
||||
def pv_move_lv_cmd(move_options, lv_full_name,
|
||||
@@ -43,40 +36,47 @@ def lv_merge_cmd(merge_options, lv_full_name):
|
||||
return cmd
|
||||
|
||||
|
||||
def _create_background_dbus_job(job_state):
|
||||
job_obj = Job(None, job_state)
|
||||
cfg.om.register_object(job_obj)
|
||||
return job_obj.dbus_object_path()
|
||||
def _move_merge(interface_name, command, job_state):
|
||||
# We need to execute these command stand alone by forking & exec'ing
|
||||
# the command always as we will be getting periodic output from them on
|
||||
# the status of the long running operation.
|
||||
command.insert(0, cfg.LVM_CMD)
|
||||
process = subprocess.Popen(command, stdout=subprocess.PIPE,
|
||||
env=os.environ,
|
||||
stderr=subprocess.PIPE, close_fds=True)
|
||||
|
||||
log_debug("Background process for %s is %d" %
|
||||
(str(command), process.pid))
|
||||
|
||||
def _move_merge(interface_name, cmd, time_out):
|
||||
# Create job object to be used while running the command
|
||||
rc = '/'
|
||||
job_state = JobState(None)
|
||||
add(cmd, job_state)
|
||||
lines_iterator = iter(process.stdout.readline, b"")
|
||||
for line in lines_iterator:
|
||||
line_str = line.decode("utf-8")
|
||||
|
||||
if time_out == -1:
|
||||
# Waiting forever
|
||||
done = job_state.Wait(time_out)
|
||||
if not done:
|
||||
ec, err_msg = job_state.GetError
|
||||
raise dbus.exceptions.DBusException(
|
||||
interface_name,
|
||||
'Exit code %s, stderr = %s' % (str(ec), err_msg))
|
||||
elif time_out == 0:
|
||||
# Immediately create and return a job
|
||||
rc = _create_background_dbus_job(job_state)
|
||||
# Check to see if the line has the correct number of separators
|
||||
try:
|
||||
if line_str.count(':') == 2:
|
||||
(device, ignore, percentage) = line_str.split(':')
|
||||
job_state.Percent = round(
|
||||
float(percentage.strip()[:-1]), 1)
|
||||
except ValueError:
|
||||
log_error("Trying to parse percentage which failed for %s" %
|
||||
line_str)
|
||||
|
||||
out = process.communicate()
|
||||
|
||||
if process.returncode == 0:
|
||||
job_state.Percent = 100
|
||||
else:
|
||||
# Willing to wait for a bit
|
||||
done = job_state.Wait(time_out)
|
||||
if not done:
|
||||
rc = _create_background_dbus_job(job_state)
|
||||
raise dbus.exceptions.DBusException(
|
||||
interface_name,
|
||||
'Exit code %s, stderr = %s' % (str(process.returncode), out[1]))
|
||||
|
||||
return rc
|
||||
cfg.load()
|
||||
return '/'
|
||||
|
||||
|
||||
def move(interface_name, lv_name, pv_src_obj, pv_source_range,
|
||||
pv_dests_and_ranges, move_options, time_out):
|
||||
pv_dests_and_ranges, move_options, job_state):
|
||||
"""
|
||||
Common code for the pvmove handling.
|
||||
:param interface_name: What dbus interface we are providing for
|
||||
@@ -85,8 +85,8 @@ def move(interface_name, lv_name, pv_src_obj, pv_source_range,
|
||||
:param pv_source_range: (0,0 to ignore, else start, end segments)
|
||||
:param pv_dests_and_ranges: Array of PV object paths and start/end segs
|
||||
:param move_options: Hash with optional arguments
|
||||
:param time_out:
|
||||
:return: Object path to job object
|
||||
:param job_state: Used to convey information about jobs between processes
|
||||
:return: '/' When complete, the empty object path
|
||||
"""
|
||||
pv_dests = []
|
||||
pv_src = cfg.om.get_object_by_path(pv_src_obj)
|
||||
@@ -104,109 +104,25 @@ def move(interface_name, lv_name, pv_src_obj, pv_source_range,
|
||||
|
||||
pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2]))
|
||||
|
||||
# Generate the command line for this command, but don't
|
||||
# execute it.
|
||||
cmd = pv_move_lv_cmd(move_options,
|
||||
lv_name,
|
||||
pv_src.lvm_id,
|
||||
pv_source_range,
|
||||
pv_dests)
|
||||
|
||||
return _move_merge(interface_name, cmd, time_out)
|
||||
return _move_merge(interface_name, cmd, job_state)
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
interface_name, 'pv_src_obj (%s) not found' % pv_src_obj)
|
||||
|
||||
|
||||
def merge(interface_name, lv_uuid, lv_name, merge_options, time_out):
|
||||
def merge(interface_name, lv_uuid, lv_name, merge_options, job_state):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
||||
if dbo:
|
||||
cmd = lv_merge_cmd(merge_options, dbo.lvm_id)
|
||||
return _move_merge(interface_name, cmd, time_out)
|
||||
return _move_merge(interface_name, cmd, job_state)
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
interface_name,
|
||||
'LV with uuid %s and name %s not present!' % (lv_uuid, lv_name))
|
||||
|
||||
|
||||
def background_reaper():
|
||||
while cfg.run.value != 0:
|
||||
with _rlock:
|
||||
num_threads = len(_thread_list) - 1
|
||||
if num_threads >= 0:
|
||||
for i in range(num_threads, -1, -1):
|
||||
_thread_list[i].join(0)
|
||||
if not _thread_list[i].is_alive():
|
||||
_thread_list.pop(i)
|
||||
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
def process_background_result(job_object, exit_code, error_msg):
|
||||
cfg.load()
|
||||
job_object.set_result(exit_code, error_msg)
|
||||
return None
|
||||
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
def empty_cb(disregard):
|
||||
pass
|
||||
|
||||
|
||||
def background_execute(command, background_job, skip_first_line=False):
|
||||
|
||||
# Wrap this whole operation in an exception handler, otherwise if we
|
||||
# hit a code bug we will silently exit this thread without anyone being
|
||||
# the wiser.
|
||||
try:
|
||||
process = subprocess.Popen(command, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, close_fds=True)
|
||||
lines_iterator = iter(process.stdout.readline, b"")
|
||||
for line in lines_iterator:
|
||||
line_str = line.decode("utf-8")
|
||||
|
||||
# Check to see if the line has the correct number of separators
|
||||
try:
|
||||
if line_str.count(':') == 2:
|
||||
(device, ignore, percentage) = line_str.split(':')
|
||||
background_job.Percent = \
|
||||
round(float(percentage.strip()[:-1]), 1)
|
||||
except ValueError:
|
||||
log_error("Trying to parse percentage which failed for %s" %
|
||||
line_str)
|
||||
|
||||
out = process.communicate()
|
||||
|
||||
if process.returncode == 0:
|
||||
background_job.Percent = 100
|
||||
|
||||
# Queue up the result so that it gets executed in same thread as others.
|
||||
r = RequestEntry(
|
||||
-1, process_background_result,
|
||||
(background_job, process.returncode, out[1]),
|
||||
empty_cb, empty_cb, False)
|
||||
cfg.worker_q.put(r)
|
||||
except Exception:
|
||||
# In the unlikely event that we blew up, lets notify fill out the
|
||||
# job object so that the client doesn't hang potentially forever!
|
||||
st = traceback.format_exc()
|
||||
error = "Exception in background thread: \n%s" % st
|
||||
log_error(error)
|
||||
r = RequestEntry(
|
||||
-1, process_background_result,
|
||||
(background_job, 1, error),
|
||||
empty_cb, empty_cb, False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
|
||||
def add(command, reporting_job):
|
||||
# Create the thread, get it running and then add it to the list
|
||||
t = threading.Thread(
|
||||
target=background_execute,
|
||||
name="thread: " + ' '.join(command),
|
||||
args=(command, reporting_job))
|
||||
t.start()
|
||||
|
||||
with _rlock:
|
||||
_thread_list.append(t)
|
||||
|
@@ -11,10 +11,8 @@ import os
|
||||
import multiprocessing
|
||||
import queue
|
||||
import itertools
|
||||
try:
|
||||
from . import path
|
||||
except SystemError:
|
||||
import path
|
||||
|
||||
from lvmdbusd import path
|
||||
|
||||
LVM_CMD = os.getenv('LVM_BINARY', path.LVM_BINARY)
|
||||
|
||||
@@ -24,14 +22,18 @@ om = None
|
||||
# This is the global bus connection
|
||||
bus = None
|
||||
|
||||
# Command line args
|
||||
args = None
|
||||
|
||||
# Set to true if we are depending on external events for updates
|
||||
ee = False
|
||||
|
||||
# Shared state variable across all processes
|
||||
run = multiprocessing.Value('i', 1)
|
||||
|
||||
# Debug
|
||||
DEBUG = True
|
||||
|
||||
# Use lvm shell
|
||||
USE_SHELL = False
|
||||
# If this is set to true, the current setup support lvm shell and we are
|
||||
# running in that mode of operation
|
||||
SHELL_IN_USE = None
|
||||
|
||||
# Lock used by pprint
|
||||
stdout_lock = multiprocessing.Lock()
|
||||
|
@@ -12,6 +12,8 @@ import time
|
||||
import threading
|
||||
from itertools import chain
|
||||
import collections
|
||||
import traceback
|
||||
import os
|
||||
|
||||
try:
|
||||
from . import cfg
|
||||
@@ -22,6 +24,11 @@ except SystemError:
|
||||
from utils import pv_dest_ranges, log_debug, log_error
|
||||
from lvm_shell_proxy import LVMShellProxy
|
||||
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
|
||||
SEP = '{|}'
|
||||
|
||||
total_time = 0.0
|
||||
@@ -31,10 +38,6 @@ total_count = 0
|
||||
# at the same time.
|
||||
cmd_lock = threading.RLock()
|
||||
|
||||
# The actual method which gets called to invoke the lvm command, can vary
|
||||
# from forking a new process to using lvm shell
|
||||
_t_call = None
|
||||
|
||||
|
||||
class LvmExecutionMeta(object):
|
||||
|
||||
@@ -95,7 +98,8 @@ def call_lvm(command, debug=False):
|
||||
# in different locations on the same box
|
||||
command.insert(0, cfg.LVM_CMD)
|
||||
|
||||
process = Popen(command, stdout=PIPE, stderr=PIPE, close_fds=True)
|
||||
process = Popen(command, stdout=PIPE, stderr=PIPE, close_fds=True,
|
||||
env=os.environ)
|
||||
out = process.communicate()
|
||||
|
||||
stdout_text = bytes(out[0]).decode("utf-8")
|
||||
@@ -104,37 +108,47 @@ def call_lvm(command, debug=False):
|
||||
if debug or process.returncode != 0:
|
||||
_debug_c(command, process.returncode, (stdout_text, stderr_text))
|
||||
|
||||
if process.returncode == 0:
|
||||
if cfg.DEBUG and out[1] and len(out[1]):
|
||||
log_error('WARNING: lvm is out-putting text to STDERR on success!')
|
||||
_debug_c(command, process.returncode, (stdout_text, stderr_text))
|
||||
|
||||
return process.returncode, stdout_text, stderr_text
|
||||
|
||||
# The actual method which gets called to invoke the lvm command, can vary
|
||||
# from forking a new process to using lvm shell
|
||||
_t_call = call_lvm
|
||||
|
||||
|
||||
def _shell_cfg():
|
||||
global _t_call
|
||||
log_debug('Using lvm shell!')
|
||||
lvm_shell = LVMShellProxy()
|
||||
_t_call = lvm_shell.call_lvm
|
||||
|
||||
|
||||
if cfg.USE_SHELL:
|
||||
_shell_cfg()
|
||||
else:
|
||||
_t_call = call_lvm
|
||||
try:
|
||||
lvm_shell = LVMShellProxy()
|
||||
_t_call = lvm_shell.call_lvm
|
||||
cfg.SHELL_IN_USE = lvm_shell
|
||||
return True
|
||||
except Exception:
|
||||
_t_call = call_lvm
|
||||
cfg.SHELL_IN_USE = None
|
||||
log_error(traceback.format_exc())
|
||||
log_error("Unable to utilize lvm shell, dropping back to fork & exec")
|
||||
return False
|
||||
|
||||
|
||||
def set_execution(shell):
|
||||
global _t_call
|
||||
with cmd_lock:
|
||||
_t_call = None
|
||||
if shell:
|
||||
log_debug('Using lvm shell!')
|
||||
lvm_shell = LVMShellProxy()
|
||||
_t_call = lvm_shell.call_lvm
|
||||
# If the user requested lvm shell and we are currently setup that
|
||||
# way, just return
|
||||
if cfg.SHELL_IN_USE and shell:
|
||||
return True
|
||||
else:
|
||||
_t_call = call_lvm
|
||||
if not shell and cfg.SHELL_IN_USE:
|
||||
cfg.SHELL_IN_USE.exit_shell()
|
||||
cfg.SHELL_IN_USE = None
|
||||
|
||||
_t_call = call_lvm
|
||||
if shell:
|
||||
if cfg.args.use_json:
|
||||
return _shell_cfg()
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def time_wrapper(command, debug=False):
|
||||
@@ -214,6 +228,10 @@ def pv_remove(device, remove_options):
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def _qt(tag_name):
|
||||
return '@%s' % tag_name
|
||||
|
||||
|
||||
def _tag(operation, what, add, rm, tag_options):
|
||||
cmd = [operation]
|
||||
cmd.extend(options_to_cli_args(tag_options))
|
||||
@@ -224,9 +242,11 @@ def _tag(operation, what, add, rm, tag_options):
|
||||
cmd.append(what)
|
||||
|
||||
if add:
|
||||
cmd.extend(list(chain.from_iterable(('--addtag', x) for x in add)))
|
||||
cmd.extend(list(chain.from_iterable(
|
||||
('--addtag', _qt(x)) for x in add)))
|
||||
if rm:
|
||||
cmd.extend(list(chain.from_iterable(('--deltag', x) for x in rm)))
|
||||
cmd.extend(list(chain.from_iterable(
|
||||
('--deltag', _qt(x)) for x in rm)))
|
||||
|
||||
return call(cmd, False)
|
||||
|
||||
@@ -406,7 +426,7 @@ def lv_cache_lv(cache_pool_full_name, lv_full_name, cache_options):
|
||||
# lvconvert --type cache --cachepool VG/CachePoolLV VG/OriginLV
|
||||
cmd = ['lvconvert']
|
||||
cmd.extend(options_to_cli_args(cache_options))
|
||||
cmd.extend(['--type', 'cache', '--cachepool',
|
||||
cmd.extend(['-y', '--type', 'cache', '--cachepool',
|
||||
cache_pool_full_name, lv_full_name])
|
||||
return call(cmd)
|
||||
|
||||
@@ -426,6 +446,68 @@ def lv_detach_cache(lv_full_name, detach_options, destroy_cache):
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def supports_json():
|
||||
cmd = ['help']
|
||||
rc, out, err = call(cmd)
|
||||
if rc == 0:
|
||||
if cfg.SHELL_IN_USE:
|
||||
return True
|
||||
else:
|
||||
if 'fullreport' in err:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def lvm_full_report_json():
|
||||
pv_columns = ['pv_name', 'pv_uuid', 'pv_fmt', 'pv_size', 'pv_free',
|
||||
'pv_used', 'dev_size', 'pv_mda_size', 'pv_mda_free',
|
||||
'pv_ba_start', 'pv_ba_size', 'pe_start', 'pv_pe_count',
|
||||
'pv_pe_alloc_count', 'pv_attr', 'pv_tags', 'vg_name',
|
||||
'vg_uuid', 'pv_missing']
|
||||
|
||||
pv_seg_columns = ['pvseg_start', 'pvseg_size', 'segtype',
|
||||
'pv_uuid', 'lv_uuid', 'pv_name']
|
||||
|
||||
vg_columns = ['vg_name', 'vg_uuid', 'vg_fmt', 'vg_size', 'vg_free',
|
||||
'vg_sysid', 'vg_extent_size', 'vg_extent_count',
|
||||
'vg_free_count', 'vg_profile', 'max_lv', 'max_pv',
|
||||
'pv_count', 'lv_count', 'snap_count', 'vg_seqno',
|
||||
'vg_mda_count', 'vg_mda_free', 'vg_mda_size',
|
||||
'vg_mda_used_count', 'vg_attr', 'vg_tags']
|
||||
|
||||
lv_columns = ['lv_uuid', 'lv_name', 'lv_path', 'lv_size',
|
||||
'vg_name', 'pool_lv_uuid', 'pool_lv', 'origin_uuid',
|
||||
'origin', 'data_percent',
|
||||
'lv_attr', 'lv_tags', 'vg_uuid', 'lv_active', 'data_lv',
|
||||
'metadata_lv', 'lv_parent', 'lv_role', 'lv_layout',
|
||||
'snap_percent', 'metadata_percent', 'copy_percent',
|
||||
'sync_percent', 'lv_metadata_size', 'move_pv', 'move_pv_uuid']
|
||||
|
||||
lv_seg_columns = ['seg_pe_ranges', 'segtype', 'lv_uuid']
|
||||
|
||||
cmd = _dc('fullreport', [
|
||||
'-a', # Need hidden too
|
||||
'--configreport', 'pv', '-o', ','.join(pv_columns),
|
||||
'--configreport', 'vg', '-o', ','.join(vg_columns),
|
||||
'--configreport', 'lv', '-o', ','.join(lv_columns),
|
||||
'--configreport', 'seg', '-o', ','.join(lv_seg_columns),
|
||||
'--configreport', 'pvseg', '-o', ','.join(pv_seg_columns),
|
||||
'--reportformat', 'json'
|
||||
])
|
||||
|
||||
rc, out, err = call(cmd)
|
||||
if rc == 0:
|
||||
# With the current implementation, if we are using the shell then we
|
||||
# are using JSON and JSON is returned back to us as it was parsed to
|
||||
# figure out if we completed OK or not
|
||||
if cfg.SHELL_IN_USE:
|
||||
assert(type(out) == dict)
|
||||
return out
|
||||
else:
|
||||
return json.loads(out)
|
||||
return None
|
||||
|
||||
|
||||
def pv_retrieve_with_segs(device=None):
|
||||
d = []
|
||||
err = ""
|
||||
@@ -436,7 +518,7 @@ def pv_retrieve_with_segs(device=None):
|
||||
'pv_used', 'dev_size', 'pv_mda_size', 'pv_mda_free',
|
||||
'pv_ba_start', 'pv_ba_size', 'pe_start', 'pv_pe_count',
|
||||
'pv_pe_alloc_count', 'pv_attr', 'pv_tags', 'vg_name',
|
||||
'vg_uuid', 'pv_seg_start', 'pvseg_size', 'segtype']
|
||||
'vg_uuid', 'pvseg_start', 'pvseg_size', 'segtype', 'pv_missing']
|
||||
|
||||
# Lvm has some issues where it returns failure when querying pvs when other
|
||||
# operations are in process, see:
|
||||
@@ -649,7 +731,9 @@ def lv_retrieve_with_segments():
|
||||
'origin', 'data_percent',
|
||||
'lv_attr', 'lv_tags', 'vg_uuid', 'lv_active', 'data_lv',
|
||||
'metadata_lv', 'seg_pe_ranges', 'segtype', 'lv_parent',
|
||||
'lv_role', 'lv_layout']
|
||||
'lv_role', 'lv_layout',
|
||||
'snap_percent', 'metadata_percent', 'copy_percent',
|
||||
'sync_percent', 'lv_metadata_size', 'move_pv', 'move_pv_uuid']
|
||||
|
||||
cmd = _dc('lvs', ['-a', '-o', ','.join(columns)])
|
||||
rc, out, err = call(cmd)
|
||||
|
@@ -11,20 +11,37 @@ from .pv import load_pvs
|
||||
from .vg import load_vgs
|
||||
from .lv import load_lvs
|
||||
from . import cfg
|
||||
from .utils import MThreadRunner, log_debug
|
||||
|
||||
|
||||
def load(refresh=True, emit_signal=True, cache_refresh=True, log=True):
|
||||
def _main_thread_load(refresh=True, emit_signal=True):
|
||||
num_total_changes = 0
|
||||
|
||||
num_total_changes += load_pvs(
|
||||
refresh=refresh,
|
||||
emit_signal=emit_signal,
|
||||
cache_refresh=False)[1]
|
||||
num_total_changes += load_vgs(
|
||||
refresh=refresh,
|
||||
emit_signal=emit_signal,
|
||||
cache_refresh=False)[1]
|
||||
num_total_changes += load_lvs(
|
||||
refresh=refresh,
|
||||
emit_signal=emit_signal,
|
||||
cache_refresh=False)[1]
|
||||
|
||||
return num_total_changes
|
||||
|
||||
|
||||
def load(refresh=True, emit_signal=True, cache_refresh=True, log=True,
|
||||
need_main_thread=True):
|
||||
# Go through and load all the PVs, VGs and LVs
|
||||
if cache_refresh:
|
||||
cfg.db.refresh(log)
|
||||
|
||||
num_total_changes += load_pvs(refresh=refresh, emit_signal=emit_signal,
|
||||
cache_refresh=False)[1]
|
||||
num_total_changes += load_vgs(refresh=refresh, emit_signal=emit_signal,
|
||||
cache_refresh=False)[1]
|
||||
num_total_changes += load_lvs(refresh=refresh, emit_signal=emit_signal,
|
||||
cache_refresh=False)[1]
|
||||
if need_main_thread:
|
||||
rc = MThreadRunner(_main_thread_load, refresh, emit_signal).done()
|
||||
else:
|
||||
rc = _main_thread_load(refresh, emit_signal)
|
||||
|
||||
return num_total_changes
|
||||
return rc
|
||||
|
@@ -8,24 +8,67 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from .automatedproperties import AutomatedProperties
|
||||
from .utils import job_obj_path_generate
|
||||
from .utils import job_obj_path_generate, mt_async_result, log_debug
|
||||
from . import cfg
|
||||
from .cfg import JOB_INTERFACE
|
||||
import dbus
|
||||
import threading
|
||||
from gi.repository import GLib
|
||||
|
||||
|
||||
# Class that handles a client waiting for something to be complete. We either
|
||||
# get a timeout or the operation is done.
|
||||
class WaitingClient(object):
|
||||
|
||||
# A timeout occurred
|
||||
@staticmethod
|
||||
def _timeout(wc):
|
||||
with wc.rlock:
|
||||
if wc.in_use:
|
||||
wc.in_use = False
|
||||
# Remove ourselves from waiting client
|
||||
wc.job_state.remove_waiting_client(wc)
|
||||
wc.timer_id = -1
|
||||
mt_async_result(wc.cb, wc.job_state.Complete)
|
||||
wc.job_state = None
|
||||
|
||||
def __init__(self, job_state, tmo, cb, cbe):
|
||||
self.rlock = threading.RLock()
|
||||
self.job_state = job_state
|
||||
self.cb = cb
|
||||
self.cbe = cbe
|
||||
self.in_use = True # Indicates if object is in play
|
||||
self.timer_id = -1
|
||||
if tmo > 0:
|
||||
self.timer_id = GLib.timeout_add_seconds(
|
||||
tmo, WaitingClient._timeout, self)
|
||||
|
||||
# The job finished before the timer popped and we are being notified that
|
||||
# it's done
|
||||
def notify(self):
|
||||
with self.rlock:
|
||||
if self.in_use:
|
||||
self.in_use = False
|
||||
# Clear timer
|
||||
if self.timer_id != -1:
|
||||
GLib.source_remove(self.timer_id)
|
||||
self.timer_id = -1
|
||||
|
||||
mt_async_result(self.cb, self.job_state.Complete)
|
||||
self.job_state = None
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
class JobState(object):
|
||||
def __init__(self, request):
|
||||
def __init__(self, request=None):
|
||||
self.rlock = threading.RLock()
|
||||
|
||||
self._percent = 0
|
||||
self._complete = False
|
||||
self._request = request
|
||||
self._cond = threading.Condition(self.rlock)
|
||||
self._ec = 0
|
||||
self._stderr = ''
|
||||
self._waiting_clients = []
|
||||
|
||||
# This is an lvm command that is just taking too long and doesn't
|
||||
# support background operation
|
||||
@@ -48,8 +91,6 @@ class JobState(object):
|
||||
with self.rlock:
|
||||
if self._request:
|
||||
self._complete = self._request.is_done()
|
||||
if self._complete:
|
||||
self._percent = 100
|
||||
|
||||
return self._complete
|
||||
|
||||
@@ -57,7 +98,8 @@ class JobState(object):
|
||||
def Complete(self, value):
|
||||
with self.rlock:
|
||||
self._complete = value
|
||||
self._cond.notify_all()
|
||||
self._percent = 100
|
||||
self.notify_waiting_clients()
|
||||
|
||||
@property
|
||||
def GetError(self):
|
||||
@@ -71,29 +113,10 @@ class JobState(object):
|
||||
else:
|
||||
return (-1, 'Job is not complete!')
|
||||
|
||||
def set_result(self, ec, msg):
|
||||
with self.rlock:
|
||||
self.Complete = True
|
||||
self._ec = ec
|
||||
self._stderr = msg
|
||||
|
||||
def dtor(self):
|
||||
with self.rlock:
|
||||
self._request = None
|
||||
|
||||
def Wait(self, timeout):
|
||||
try:
|
||||
with self._cond:
|
||||
# Check to see if we are done, before we wait
|
||||
if not self.Complete:
|
||||
if timeout != -1:
|
||||
self._cond.wait(timeout)
|
||||
else:
|
||||
self._cond.wait()
|
||||
return self.Complete
|
||||
except RuntimeError:
|
||||
return False
|
||||
|
||||
@property
|
||||
def Result(self):
|
||||
with self.rlock:
|
||||
@@ -101,10 +124,40 @@ class JobState(object):
|
||||
return self._request.result()
|
||||
return '/'
|
||||
|
||||
def add_waiting_client(self, client):
|
||||
with self.rlock:
|
||||
# Avoid race condition where it goes complete before we get added
|
||||
# to the list of waiting clients
|
||||
if self.Complete:
|
||||
client.notify()
|
||||
else:
|
||||
self._waiting_clients.append(client)
|
||||
|
||||
def remove_waiting_client(self, client):
|
||||
# If a waiting client timer pops before the job is done we will allow
|
||||
# the client to remove themselves from the list. As we have a lock
|
||||
# here and a lock in the waiting client too, and they can be obtained
|
||||
# in different orders, a dead lock can occur.
|
||||
# As this remove is really optional, we will try to acquire the lock
|
||||
# and remove. If we are unsuccessful it's not fatal, we just delay
|
||||
# the time when the objects can be garbage collected by python
|
||||
if self.rlock.acquire(False):
|
||||
try:
|
||||
self._waiting_clients.remove(client)
|
||||
finally:
|
||||
self.rlock.release()
|
||||
|
||||
def notify_waiting_clients(self):
|
||||
with self.rlock:
|
||||
for c in self._waiting_clients:
|
||||
c.notify()
|
||||
|
||||
self._waiting_clients = []
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
class Job(AutomatedProperties):
|
||||
_Percent_meta = ('y', JOB_INTERFACE)
|
||||
_Percent_meta = ('d', JOB_INTERFACE)
|
||||
_Complete_meta = ('b', JOB_INTERFACE)
|
||||
_Result_meta = ('o', JOB_INTERFACE)
|
||||
_GetError_meta = ('(is)', JOB_INTERFACE)
|
||||
@@ -120,15 +173,11 @@ class Job(AutomatedProperties):
|
||||
|
||||
@property
|
||||
def Percent(self):
|
||||
return self.state.Percent
|
||||
|
||||
@Percent.setter
|
||||
def Percent(self, value):
|
||||
self.state.Percent = value
|
||||
return dbus.Double(float(self.state.Percent))
|
||||
|
||||
@property
|
||||
def Complete(self):
|
||||
return self.state.Complete
|
||||
return dbus.Boolean(self.state.Complete)
|
||||
|
||||
@Complete.setter
|
||||
def Complete(self, value):
|
||||
@@ -136,10 +185,7 @@ class Job(AutomatedProperties):
|
||||
|
||||
@property
|
||||
def GetError(self):
|
||||
return self.state.GetError
|
||||
|
||||
def set_result(self, ec, msg):
|
||||
self.state.set_result(ec, msg)
|
||||
return dbus.Struct(self.state.GetError, signature="(is)")
|
||||
|
||||
@dbus.service.method(dbus_interface=JOB_INTERFACE)
|
||||
def Remove(self):
|
||||
@@ -152,13 +198,18 @@ class Job(AutomatedProperties):
|
||||
|
||||
@dbus.service.method(dbus_interface=JOB_INTERFACE,
|
||||
in_signature='i',
|
||||
out_signature='b')
|
||||
def Wait(self, timeout):
|
||||
return self.state.Wait(timeout)
|
||||
out_signature='b',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Wait(self, timeout, cb, cbe):
|
||||
if timeout == 0 or self.state.Complete:
|
||||
cb(dbus.Boolean(self.state.Complete))
|
||||
else:
|
||||
self.state.add_waiting_client(
|
||||
WaitingClient(self.state, timeout, cb, cbe))
|
||||
|
||||
@property
|
||||
def Result(self):
|
||||
return self.state.Result
|
||||
return dbus.ObjectPath(self.state.Result)
|
||||
|
||||
@property
|
||||
def lvm_id(self):
|
||||
|
@@ -21,7 +21,41 @@ from .utils import n, n32
|
||||
from .loader import common
|
||||
from .state import State
|
||||
from . import background
|
||||
from .utils import round_size
|
||||
from .utils import round_size, mt_remove_dbus_objects
|
||||
from .job import JobState
|
||||
|
||||
|
||||
# Try and build a key for a LV, so that we sort the LVs with least dependencies
|
||||
# first. This may be error prone because of the flexibility LVM
|
||||
# provides and what you can stack.
|
||||
def get_key(i):
|
||||
|
||||
name = i['lv_name']
|
||||
parent = i['lv_parent']
|
||||
pool = i['pool_lv']
|
||||
a1 = ""
|
||||
a2 = ""
|
||||
|
||||
if name[0] == '[':
|
||||
a1 = '#'
|
||||
|
||||
# We have a parent
|
||||
if parent:
|
||||
# Check if parent is hidden
|
||||
if parent[0] == '[':
|
||||
a2 = '##'
|
||||
else:
|
||||
a2 = '#'
|
||||
|
||||
# If a LV has a pool, then it should be sorted/loaded after the pool
|
||||
# lv, unless it's a hidden too, then after other hidden, but before visible
|
||||
if pool:
|
||||
if pool[0] != '[':
|
||||
a2 += '~'
|
||||
else:
|
||||
a1 = '$' + a1
|
||||
|
||||
return "%s%s%s" % (a1, a2, name)
|
||||
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
@@ -31,7 +65,13 @@ def lvs_state_retrieve(selection, cache_refresh=True):
|
||||
if cache_refresh:
|
||||
cfg.db.refresh()
|
||||
|
||||
for l in cfg.db.fetch_lvs(selection):
|
||||
# When building up the model, it's best to process LVs with the least
|
||||
# dependencies to those that are dependant upon other LVs. Otherwise, when
|
||||
# we are trying to gather information we could be in a position where we
|
||||
# don't have information available yet.
|
||||
lvs = sorted(cfg.db.fetch_lvs(selection), key=get_key)
|
||||
|
||||
for l in lvs:
|
||||
rc.append(LvState(
|
||||
l['lv_uuid'], l['lv_name'],
|
||||
l['lv_path'], n(l['lv_size']),
|
||||
@@ -41,7 +81,14 @@ def lvs_state_retrieve(selection, cache_refresh=True):
|
||||
n32(l['data_percent']), l['lv_attr'],
|
||||
l['lv_tags'], l['lv_active'], l['data_lv'],
|
||||
l['metadata_lv'], l['segtype'], l['lv_role'],
|
||||
l['lv_layout']))
|
||||
l['lv_layout'],
|
||||
n32(l['snap_percent']),
|
||||
n32(l['metadata_percent']),
|
||||
n32(l['copy_percent']),
|
||||
n32(l['sync_percent']),
|
||||
n(l['lv_metadata_size']),
|
||||
l['move_pv'],
|
||||
l['move_pv_uuid']))
|
||||
return rc
|
||||
|
||||
|
||||
@@ -61,9 +108,15 @@ class LvState(State):
|
||||
rc = []
|
||||
for pv in sorted(cfg.db.lv_contained_pv(uuid)):
|
||||
(pv_uuid, pv_name, pv_segs) = pv
|
||||
pv_obj = cfg.om.get_object_path_by_lvm_id(
|
||||
pv_uuid, pv_name, gen_new=False)
|
||||
rc.append((pv_obj, pv_segs))
|
||||
pv_obj = cfg.om.get_object_path_by_uuid_lvm_id(pv_uuid, pv_name)
|
||||
|
||||
segs_decorate = []
|
||||
for i in pv_segs:
|
||||
segs_decorate.append((dbus.UInt64(i[0]),
|
||||
dbus.UInt64(i[1]),
|
||||
dbus.String(i[2])))
|
||||
|
||||
rc.append((dbus.ObjectPath(pv_obj), segs_decorate))
|
||||
|
||||
return dbus.Array(rc, signature="(oa(tts))")
|
||||
|
||||
@@ -84,27 +137,28 @@ class LvState(State):
|
||||
|
||||
for l in cfg.db.hidden_lvs(self.Uuid):
|
||||
full_name = "%s/%s" % (vg_name, l[1])
|
||||
op = cfg.om.get_object_path_by_lvm_id(
|
||||
l[0], full_name, gen_new=False)
|
||||
op = cfg.om.get_object_path_by_uuid_lvm_id(l[0], full_name)
|
||||
assert op
|
||||
rc.append(op)
|
||||
rc.append(dbus.ObjectPath(op))
|
||||
return rc
|
||||
|
||||
def __init__(self, Uuid, Name, Path, SizeBytes,
|
||||
vg_name, vg_uuid, pool_lv_uuid, PoolLv,
|
||||
origin_uuid, OriginLv, DataPercent, Attr, Tags, active,
|
||||
data_lv, metadata_lv, segtypes, role, layout):
|
||||
data_lv, metadata_lv, segtypes, role, layout, SnapPercent,
|
||||
MetaDataPercent, CopyPercent, SyncPercent, MetaDataSizeBytes,
|
||||
move_pv, move_pv_uuid):
|
||||
utils.init_class_from_arguments(self)
|
||||
|
||||
# The segtypes is possibly an array with potentially dupes or a single
|
||||
# value
|
||||
self._segs = dbus.Array([], signature='s')
|
||||
if not isinstance(segtypes, list):
|
||||
self._segs.append(segtypes)
|
||||
self._segs.append(dbus.String(segtypes))
|
||||
else:
|
||||
self._segs.extend(set(segtypes))
|
||||
self._segs.extend([dbus.String(x) for x in set(segtypes)])
|
||||
|
||||
self.Vg = cfg.om.get_object_path_by_lvm_id(
|
||||
self.Vg = cfg.om.get_object_path_by_uuid_lvm_id(
|
||||
vg_uuid, vg_name, vg_obj_path_generate)
|
||||
|
||||
self.Devices = LvState._pv_devices(self.Uuid)
|
||||
@@ -112,15 +166,14 @@ class LvState(State):
|
||||
if PoolLv:
|
||||
gen = utils.lv_object_path_method(Name, (Attr, layout, role))
|
||||
|
||||
self.PoolLv = cfg.om.get_object_path_by_lvm_id(
|
||||
pool_lv_uuid, '%s/%s' % (vg_name, PoolLv),
|
||||
gen)
|
||||
self.PoolLv = cfg.om.get_object_path_by_uuid_lvm_id(
|
||||
pool_lv_uuid, '%s/%s' % (vg_name, PoolLv), gen)
|
||||
else:
|
||||
self.PoolLv = '/'
|
||||
|
||||
if OriginLv:
|
||||
self.OriginLv = \
|
||||
cfg.om.get_object_path_by_lvm_id(
|
||||
cfg.om.get_object_path_by_uuid_lvm_id(
|
||||
origin_uuid, '%s/%s' % (vg_name, OriginLv),
|
||||
vg_obj_path_generate)
|
||||
else:
|
||||
@@ -137,8 +190,6 @@ class LvState(State):
|
||||
self.Name, (self.Attr, self.layout, self.role))
|
||||
|
||||
def _object_type_create(self):
|
||||
if self.Name[0] == '[':
|
||||
return LvCommon
|
||||
if self.Attr[0] == 't':
|
||||
return LvThinPool
|
||||
elif self.Attr[0] == 'C':
|
||||
@@ -146,6 +197,8 @@ class LvState(State):
|
||||
return LvCachePool
|
||||
else:
|
||||
return LvCacheLv
|
||||
elif self.Name[0] == '[':
|
||||
return LvCommon
|
||||
elif self.OriginLv != '/':
|
||||
return LvSnapShot
|
||||
else:
|
||||
@@ -153,7 +206,7 @@ class LvState(State):
|
||||
|
||||
def create_dbus_object(self, path):
|
||||
if not path:
|
||||
path = cfg.om.get_object_path_by_lvm_id(
|
||||
path = cfg.om.get_object_path_by_uuid_lvm_id(
|
||||
self.Uuid, self.lvm_id, self._object_path_create())
|
||||
|
||||
obj_ctor = self._object_type_create()
|
||||
@@ -170,15 +223,23 @@ class LvState(State):
|
||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'Name', 's')
|
||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'Path', 's')
|
||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'SizeBytes', 't')
|
||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'DataPercent', 'u')
|
||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'SegType', 'as')
|
||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'Vg', 'o')
|
||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'OriginLv', 'o')
|
||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'PoolLv', 'o')
|
||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'Devices', "a(oa(tts))")
|
||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'HiddenLvs', "ao")
|
||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'Attr', 's')
|
||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'DataPercent', 'u')
|
||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'SnapPercent', 'u')
|
||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'DataPercent', 'u')
|
||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'MetaDataPercent', 'u')
|
||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'CopyPercent', 'u')
|
||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'SyncPercent', 'u')
|
||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'MetaDataSizeBytes', 't')
|
||||
class LvCommon(AutomatedProperties):
|
||||
_Tags_meta = ("as", LV_COMMON_INTERFACE)
|
||||
_Roles_meta = ("as", LV_COMMON_INTERFACE)
|
||||
_IsThinVolume_meta = ("b", LV_COMMON_INTERFACE)
|
||||
_IsThinPool_meta = ("b", LV_COMMON_INTERFACE)
|
||||
_Active_meta = ("b", LV_COMMON_INTERFACE)
|
||||
@@ -191,12 +252,25 @@ class LvCommon(AutomatedProperties):
|
||||
_FixedMinor_meta = ('b', LV_COMMON_INTERFACE)
|
||||
_ZeroBlocks_meta = ('b', LV_COMMON_INTERFACE)
|
||||
_SkipActivation_meta = ('b', LV_COMMON_INTERFACE)
|
||||
_MovePv_meta = ('o', LV_COMMON_INTERFACE)
|
||||
|
||||
def _get_move_pv(self):
|
||||
path = None
|
||||
|
||||
# It's likely that the move_pv is empty
|
||||
if self.state.move_pv_uuid and self.state.move_pv:
|
||||
path = cfg.om.get_object_path_by_uuid_lvm_id(
|
||||
self.state.move_pv_uuid, self.state.move_pv)
|
||||
if not path:
|
||||
path = '/'
|
||||
return path
|
||||
|
||||
# noinspection PyUnusedLocal,PyPep8Naming
|
||||
def __init__(self, object_path, object_state):
|
||||
super(LvCommon, self).__init__(object_path, lvs_state_retrieve)
|
||||
self.set_interface(LV_COMMON_INTERFACE)
|
||||
self.state = object_state
|
||||
self._move_pv = self._get_move_pv()
|
||||
|
||||
@property
|
||||
def VolumeType(self):
|
||||
@@ -211,14 +285,16 @@ class LvCommon(AutomatedProperties):
|
||||
'V': 'thin Volume', 't': 'thin pool', 'T': 'Thin pool data',
|
||||
'e': 'raid or pool metadata or pool metadata spare',
|
||||
'-': 'Unspecified'}
|
||||
return (self.state.Attr[0], type_map[self.state.Attr[0]])
|
||||
return dbus.Struct((self.state.Attr[0], type_map[self.state.Attr[0]]),
|
||||
signature="as")
|
||||
|
||||
@property
|
||||
def Permissions(self):
|
||||
type_map = {'w': 'writable', 'r': 'read-only',
|
||||
'R': 'Read-only activation of non-read-only volume',
|
||||
'-': 'Unspecified'}
|
||||
return (self.state.Attr[1], type_map[self.state.Attr[1]])
|
||||
return dbus.Struct((self.state.Attr[1], type_map[self.state.Attr[1]]),
|
||||
signature="(ss)")
|
||||
|
||||
@property
|
||||
def AllocationPolicy(self):
|
||||
@@ -227,11 +303,12 @@ class LvCommon(AutomatedProperties):
|
||||
'i': 'inherited', 'I': 'inherited locked',
|
||||
'l': 'cling', 'L': 'cling locked',
|
||||
'n': 'normal', 'N': 'normal locked', '-': 'Unspecified'}
|
||||
return (self.state.Attr[2], type_map[self.state.Attr[2]])
|
||||
return dbus.Struct((self.state.Attr[2], type_map[self.state.Attr[2]]),
|
||||
signature="(ss)")
|
||||
|
||||
@property
|
||||
def FixedMinor(self):
|
||||
return self.state.Attr[3] == 'm'
|
||||
return dbus.Boolean(self.state.Attr[3] == 'm')
|
||||
|
||||
@property
|
||||
def State(self):
|
||||
@@ -242,29 +319,32 @@ class LvCommon(AutomatedProperties):
|
||||
'd': 'mapped device present without tables',
|
||||
'i': 'mapped device present with inactive table',
|
||||
'X': 'unknown', '-': 'Unspecified'}
|
||||
return (self.state.Attr[4], type_map[self.state.Attr[4]])
|
||||
return dbus.Struct((self.state.Attr[4], type_map[self.state.Attr[4]]),
|
||||
signature="(ss)")
|
||||
|
||||
@property
|
||||
def TargetType(self):
|
||||
type_map = {'C': 'Cache', 'm': 'mirror', 'r': 'raid',
|
||||
's': 'snapshot', 't': 'thin', 'u': 'unknown',
|
||||
'v': 'virtual', '-': 'Unspecified'}
|
||||
return (self.state.Attr[6], type_map[self.state.Attr[6]])
|
||||
return dbus.Struct((self.state.Attr[6], type_map[self.state.Attr[6]]),
|
||||
signature="(ss)")
|
||||
|
||||
@property
|
||||
def ZeroBlocks(self):
|
||||
return self.state.Attr[7] == 'z'
|
||||
return dbus.Boolean(self.state.Attr[7] == 'z')
|
||||
|
||||
@property
|
||||
def Health(self):
|
||||
type_map = {'p': 'partial', 'r': 'refresh',
|
||||
'm': 'mismatches', 'w': 'writemostly',
|
||||
'X': 'X unknown', '-': 'Unspecified'}
|
||||
return (self.state.Attr[8], type_map[self.state.Attr[8]])
|
||||
return dbus.Struct((self.state.Attr[8], type_map[self.state.Attr[8]]),
|
||||
signature="(ss)")
|
||||
|
||||
@property
|
||||
def SkipActivation(self):
|
||||
return self.state.Attr[9] == 'k'
|
||||
return dbus.Boolean(self.state.Attr[9] == 'k')
|
||||
|
||||
def vg_name_lookup(self):
|
||||
return self.state.vg_name_lookup()
|
||||
@@ -280,32 +360,45 @@ class LvCommon(AutomatedProperties):
|
||||
def Tags(self):
|
||||
return utils.parse_tags(self.state.Tags)
|
||||
|
||||
@property
|
||||
def Roles(self):
|
||||
return utils.parse_tags(self.state.role)
|
||||
|
||||
@property
|
||||
def lvm_id(self):
|
||||
return self.state.lvm_id
|
||||
|
||||
@property
|
||||
def IsThinVolume(self):
|
||||
return self.state.Attr[0] == 'V'
|
||||
return dbus.Boolean(self.state.Attr[0] == 'V')
|
||||
|
||||
@property
|
||||
def IsThinPool(self):
|
||||
return self.state.Attr[0] == 't'
|
||||
return dbus.Boolean(self.state.Attr[0] == 't')
|
||||
|
||||
@property
|
||||
def Active(self):
|
||||
return self.state.active == "active"
|
||||
return dbus.Boolean(self.state.active == "active")
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=LV_COMMON_INTERFACE,
|
||||
in_signature='ia{sv}',
|
||||
out_signature='o')
|
||||
def _Future(self, tmo, open_options):
|
||||
raise dbus.exceptions.DBusException(LV_COMMON_INTERFACE, 'Do not use!')
|
||||
@property
|
||||
def MovePv(self):
|
||||
return dbus.ObjectPath(self._move_pv)
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
class Lv(LvCommon):
|
||||
def _fetch_hidden(self, name):
|
||||
|
||||
# The name is vg/name
|
||||
full_name = "%s/%s" % (self.vg_name_lookup(), name)
|
||||
return cfg.om.get_object_path_by_lvm_id(full_name)
|
||||
|
||||
def _get_data_meta(self):
|
||||
|
||||
# Get the data
|
||||
return (self._fetch_hidden(self.state.data_lv),
|
||||
self._fetch_hidden(self.state.metadata_lv))
|
||||
|
||||
# noinspection PyUnusedLocal,PyPep8Naming
|
||||
def __init__(self, object_path, object_state):
|
||||
super(Lv, self).__init__(object_path, object_state)
|
||||
@@ -322,7 +415,6 @@ class Lv(LvCommon):
|
||||
rc, out, err = cmdhandler.lv_remove(lv_name, remove_options)
|
||||
|
||||
if rc == 0:
|
||||
cfg.om.remove_object(dbo, True)
|
||||
cfg.load()
|
||||
else:
|
||||
# Need to work on error handling, need consistent
|
||||
@@ -388,14 +480,21 @@ class Lv(LvCommon):
|
||||
@dbus.service.method(
|
||||
dbus_interface=LV_INTERFACE,
|
||||
in_signature='o(tt)a(ott)ia{sv}',
|
||||
out_signature='o')
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Move(self, pv_src_obj, pv_source_range,
|
||||
pv_dests_and_ranges,
|
||||
tmo, move_options):
|
||||
return background.move(
|
||||
LV_INTERFACE, self.lvm_id, pv_src_obj,
|
||||
pv_source_range, pv_dests_and_ranges,
|
||||
move_options, tmo)
|
||||
tmo, move_options, cb, cbe):
|
||||
|
||||
job_state = JobState()
|
||||
|
||||
r = RequestEntry(
|
||||
tmo, background.move,
|
||||
(LV_INTERFACE, self.lvm_id, pv_src_obj, pv_source_range,
|
||||
pv_dests_and_ranges, move_options, job_state), cb, cbe, False,
|
||||
job_state)
|
||||
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _snap_shot(lv_uuid, lv_name, name, optional_size,
|
||||
@@ -408,8 +507,6 @@ class Lv(LvCommon):
|
||||
# it is a thin lv
|
||||
if not dbo.IsThinVolume:
|
||||
if optional_size == 0:
|
||||
# TODO: Should we pick a sane default or force user to
|
||||
# make a decision?
|
||||
space = dbo.SizeBytes / 80
|
||||
remainder = space % 512
|
||||
optional_size = space + 512 - remainder
|
||||
@@ -417,15 +514,9 @@ class Lv(LvCommon):
|
||||
rc, out, err = cmdhandler.vg_lv_snapshot(
|
||||
lv_name, snapshot_options, name, optional_size)
|
||||
if rc == 0:
|
||||
return_path = '/'
|
||||
cfg.load()
|
||||
full_name = "%s/%s" % (dbo.vg_name_lookup(), name)
|
||||
lvs = load_lvs([full_name], emit_signal=True)[0]
|
||||
for l in lvs:
|
||||
return_path = l.dbus_object_path()
|
||||
|
||||
# Refresh self and all included PVs
|
||||
cfg.load(cache_refresh=False)
|
||||
return return_path
|
||||
return cfg.om.get_object_path_by_lvm_id(full_name)
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
LV_INTERFACE,
|
||||
@@ -629,23 +720,6 @@ class LvThinPool(Lv):
|
||||
_DataLv_meta = ("o", THIN_POOL_INTERFACE)
|
||||
_MetaDataLv_meta = ("o", THIN_POOL_INTERFACE)
|
||||
|
||||
def _fetch_hidden(self, name):
|
||||
|
||||
# The name is vg/name
|
||||
full_name = "%s/%s" % (self.vg_name_lookup(), name)
|
||||
|
||||
o = cfg.om.get_object_by_lvm_id(full_name)
|
||||
if o:
|
||||
return o.dbus_object_path()
|
||||
|
||||
return '/'
|
||||
|
||||
def _get_data_meta(self):
|
||||
|
||||
# Get the data
|
||||
return (self._fetch_hidden(self.state.data_lv),
|
||||
self._fetch_hidden(self.state.metadata_lv))
|
||||
|
||||
def __init__(self, object_path, object_state):
|
||||
super(LvThinPool, self).__init__(object_path, object_state)
|
||||
self.set_interface(THIN_POOL_INTERFACE)
|
||||
@@ -653,11 +727,11 @@ class LvThinPool(Lv):
|
||||
|
||||
@property
|
||||
def DataLv(self):
|
||||
return self._data_lv
|
||||
return dbus.ObjectPath(self._data_lv)
|
||||
|
||||
@property
|
||||
def MetaDataLv(self):
|
||||
return self._metadata_lv
|
||||
return dbus.ObjectPath(self._metadata_lv)
|
||||
|
||||
@staticmethod
|
||||
def _lv_create(lv_uuid, lv_name, name, size_bytes, create_options):
|
||||
@@ -671,9 +745,8 @@ class LvThinPool(Lv):
|
||||
lv_name, create_options, name, size_bytes)
|
||||
if rc == 0:
|
||||
full_name = "%s/%s" % (dbo.vg_name_lookup(), name)
|
||||
lvs = load_lvs([full_name], emit_signal=True)[0]
|
||||
for l in lvs:
|
||||
lv_created = l.dbus_object_path()
|
||||
cfg.load()
|
||||
lv_created = cfg.om.get_object_path_by_lvm_id(full_name)
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
LV_INTERFACE,
|
||||
@@ -702,9 +775,21 @@ class LvThinPool(Lv):
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
class LvCachePool(Lv):
|
||||
_DataLv_meta = ("o", CACHE_POOL_INTERFACE)
|
||||
_MetaDataLv_meta = ("o", CACHE_POOL_INTERFACE)
|
||||
|
||||
def __init__(self, object_path, object_state):
|
||||
super(LvCachePool, self).__init__(object_path, object_state)
|
||||
self.set_interface(CACHE_POOL_INTERFACE)
|
||||
self._data_lv, self._metadata_lv = self._get_data_meta()
|
||||
|
||||
@property
|
||||
def DataLv(self):
|
||||
return dbus.ObjectPath(self._data_lv)
|
||||
|
||||
@property
|
||||
def MetaDataLv(self):
|
||||
return dbus.ObjectPath(self._metadata_lv)
|
||||
|
||||
@staticmethod
|
||||
def _cache_lv(lv_uuid, lv_name, lv_object_path, cache_options):
|
||||
@@ -723,12 +808,10 @@ class LvCachePool(Lv):
|
||||
# When we cache an LV, the cache pool and the lv that is getting
|
||||
# cached need to be removed from the object manager and
|
||||
# re-created as their interfaces have changed!
|
||||
cfg.om.remove_object(dbo, emit_signal=True)
|
||||
cfg.om.remove_object(lv_to_cache, emit_signal=True)
|
||||
mt_remove_dbus_objects((dbo, lv_to_cache))
|
||||
cfg.load()
|
||||
|
||||
lv_converted = \
|
||||
cfg.om.get_object_by_lvm_id(fcn).dbus_object_path()
|
||||
lv_converted = cfg.om.get_object_path_by_lvm_id(fcn)
|
||||
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
@@ -770,7 +853,7 @@ class LvCacheLv(Lv):
|
||||
|
||||
@property
|
||||
def CachePool(self):
|
||||
return self.state.PoolLv
|
||||
return dbus.ObjectPath(self.state.PoolLv)
|
||||
|
||||
@staticmethod
|
||||
def _detach_lv(lv_uuid, lv_name, detach_options, destroy_cache):
|
||||
@@ -787,13 +870,10 @@ class LvCacheLv(Lv):
|
||||
if rc == 0:
|
||||
# The cache pool gets removed as hidden and put back to
|
||||
# visible, so lets delete
|
||||
cfg.om.remove_object(cache_pool, emit_signal=True)
|
||||
cfg.om.remove_object(dbo, emit_signal=True)
|
||||
mt_remove_dbus_objects((cache_pool, dbo))
|
||||
cfg.load()
|
||||
|
||||
uncached_lv_path = \
|
||||
cfg.om.get_object_by_lvm_id(lv_name).dbus_object_path()
|
||||
|
||||
uncached_lv_path = cfg.om.get_object_path_by_lvm_id(lv_name)
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
LV_INTERFACE,
|
||||
@@ -827,7 +907,13 @@ class LvSnapShot(Lv):
|
||||
@dbus.service.method(
|
||||
dbus_interface=SNAPSHOT_INTERFACE,
|
||||
in_signature='ia{sv}',
|
||||
out_signature='o')
|
||||
def Merge(self, tmo, merge_options):
|
||||
return background.merge(SNAPSHOT_INTERFACE, self.Uuid, self.lvm_id,
|
||||
merge_options, tmo)
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Merge(self, tmo, merge_options, cb, cbe):
|
||||
job_state = JobState()
|
||||
|
||||
r = RequestEntry(tmo, background.merge,
|
||||
(SNAPSHOT_INTERFACE, self.Uuid, self.lvm_id,
|
||||
merge_options, job_state), cb, cbe, False,
|
||||
job_state)
|
||||
cfg.worker_q.put(r)
|
||||
|
@@ -14,17 +14,22 @@
|
||||
import subprocess
|
||||
import shlex
|
||||
from fcntl import fcntl, F_GETFL, F_SETFL
|
||||
from os import O_NONBLOCK
|
||||
import os
|
||||
import traceback
|
||||
import sys
|
||||
import re
|
||||
import tempfile
|
||||
import time
|
||||
import select
|
||||
import copy
|
||||
|
||||
try:
|
||||
from .cfg import LVM_CMD
|
||||
from .utils import log_debug, log_error
|
||||
except:
|
||||
from cfg import LVM_CMD
|
||||
from utils import log_debug, log_error
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
|
||||
|
||||
from lvmdbusd.cfg import LVM_CMD
|
||||
from lvmdbusd.utils import log_debug, log_error
|
||||
|
||||
SHELL_PROMPT = "lvm> "
|
||||
|
||||
@@ -38,42 +43,58 @@ def _quote_arg(arg):
|
||||
|
||||
class LVMShellProxy(object):
|
||||
def _read_until_prompt(self):
|
||||
prev_ec = None
|
||||
stdout = ""
|
||||
report = ""
|
||||
stderr = ""
|
||||
|
||||
# Try reading from all FDs to prevent one from filling up and causing
|
||||
# a hang. We are also assuming that we won't get the lvm prompt back
|
||||
# until we have already received all the output from stderr and the
|
||||
# report descriptor too.
|
||||
while not stdout.endswith(SHELL_PROMPT):
|
||||
try:
|
||||
tmp = self.lvm_shell.stdout.read()
|
||||
if tmp:
|
||||
stdout += tmp.decode("utf-8")
|
||||
except IOError:
|
||||
# nothing written yet
|
||||
rd_fd = [
|
||||
self.lvm_shell.stdout.fileno(),
|
||||
self.report_r,
|
||||
self.lvm_shell.stderr.fileno()]
|
||||
ready = select.select(rd_fd, [], [], 2)
|
||||
|
||||
for r in ready[0]:
|
||||
if r == self.lvm_shell.stdout.fileno():
|
||||
while True:
|
||||
tmp = self.lvm_shell.stdout.read()
|
||||
if tmp:
|
||||
stdout += tmp.decode("utf-8")
|
||||
else:
|
||||
break
|
||||
|
||||
elif r == self.report_r:
|
||||
while True:
|
||||
tmp = os.read(self.report_r, 16384)
|
||||
if tmp:
|
||||
report += tmp.decode("utf-8")
|
||||
if len(tmp) != 16384:
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
elif r == self.lvm_shell.stderr.fileno():
|
||||
while True:
|
||||
tmp = self.lvm_shell.stderr.read()
|
||||
if tmp:
|
||||
stderr += tmp.decode("utf-8")
|
||||
else:
|
||||
break
|
||||
|
||||
# Check to see if the lvm process died on us
|
||||
if self.lvm_shell.poll():
|
||||
raise Exception(self.lvm_shell.returncode, "%s" % stderr)
|
||||
|
||||
except IOError as ioe:
|
||||
log_debug(str(ioe))
|
||||
pass
|
||||
|
||||
# strip the prompt from the STDOUT before returning and grab the exit
|
||||
# code if it's available
|
||||
m = self.re.match(stdout)
|
||||
if m:
|
||||
prev_ec = int(m.group(2))
|
||||
strip_idx = -1 * len(m.group(1))
|
||||
else:
|
||||
strip_idx = -1 * len(SHELL_PROMPT)
|
||||
|
||||
return stdout[:strip_idx], prev_ec
|
||||
|
||||
def _read_line(self):
|
||||
while True:
|
||||
try:
|
||||
tmp = self.lvm_shell.stdout.readline()
|
||||
if tmp:
|
||||
return tmp.decode("utf-8")
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
def _discard_echo(self, expected):
|
||||
line = ""
|
||||
while line != expected:
|
||||
# GNU readline inserts some interesting characters at times...
|
||||
line += self._read_line().replace(' \r', '')
|
||||
return stdout, report, stderr
|
||||
|
||||
def _write_cmd(self, cmd):
|
||||
cmd_bytes = bytes(cmd, "utf-8")
|
||||
@@ -81,39 +102,94 @@ class LVMShellProxy(object):
|
||||
assert (num_written == len(cmd_bytes))
|
||||
self.lvm_shell.stdin.flush()
|
||||
|
||||
def _lvm_echos(self):
|
||||
echo = False
|
||||
cmd = "version\n"
|
||||
self._write_cmd(cmd)
|
||||
line = self._read_line()
|
||||
|
||||
if line == cmd:
|
||||
echo = True
|
||||
|
||||
self._read_until_prompt()
|
||||
|
||||
return echo
|
||||
|
||||
def __init__(self):
|
||||
self.re = re.compile(".*(\[(-?[0-9]+)\] lvm> $)", re.DOTALL)
|
||||
|
||||
# Create a temp directory
|
||||
tmp_dir = tempfile.mkdtemp(prefix="lvmdbus_")
|
||||
tmp_file = "%s/lvmdbus_report" % (tmp_dir)
|
||||
|
||||
try:
|
||||
# Lets create fifo for the report output
|
||||
os.mkfifo(tmp_file, 0o600)
|
||||
except FileExistsError:
|
||||
pass
|
||||
|
||||
self.report_r = os.open(tmp_file, os.O_NONBLOCK)
|
||||
|
||||
# Setup the environment for using our own socket for reporting
|
||||
local_env = copy.deepcopy(os.environ)
|
||||
local_env["LVM_REPORT_FD"] = "32"
|
||||
local_env["LVM_COMMAND_PROFILE"] = "lvmdbusd"
|
||||
|
||||
# Disable the abort logic if lvm logs too much, which easily happens
|
||||
# when utilizing the lvm shell.
|
||||
local_env["LVM_LOG_FILE_MAX_LINES"] = "0"
|
||||
|
||||
flags = fcntl(self.report_r, F_GETFL)
|
||||
fcntl(self.report_r, F_SETFL, flags | os.O_NONBLOCK)
|
||||
|
||||
# run the lvm shell
|
||||
self.lvm_shell = subprocess.Popen(
|
||||
[LVM_CMD], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, close_fds=True)
|
||||
flags = fcntl(self.lvm_shell.stdout, F_GETFL)
|
||||
fcntl(self.lvm_shell.stdout, F_SETFL, flags | O_NONBLOCK)
|
||||
flags = fcntl(self.lvm_shell.stderr, F_GETFL)
|
||||
fcntl(self.lvm_shell.stderr, F_SETFL, flags | O_NONBLOCK)
|
||||
[LVM_CMD + " 32>%s" % tmp_file],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, env=local_env,
|
||||
stderr=subprocess.PIPE, close_fds=True, shell=True)
|
||||
|
||||
# wait for the first prompt
|
||||
self._read_until_prompt()
|
||||
try:
|
||||
flags = fcntl(self.lvm_shell.stdout, F_GETFL)
|
||||
fcntl(self.lvm_shell.stdout, F_SETFL, flags | os.O_NONBLOCK)
|
||||
flags = fcntl(self.lvm_shell.stderr, F_GETFL)
|
||||
fcntl(self.lvm_shell.stderr, F_SETFL, flags | os.O_NONBLOCK)
|
||||
|
||||
# Check to see if the version of LVM we are using is running with
|
||||
# gnu readline which will echo our writes from stdin to stdout
|
||||
self.echo = self._lvm_echos()
|
||||
# wait for the first prompt
|
||||
errors = self._read_until_prompt()[2]
|
||||
if errors and len(errors):
|
||||
raise RuntimeError(errors)
|
||||
except:
|
||||
raise
|
||||
finally:
|
||||
# These will get deleted when the FD count goes to zero so we can be
|
||||
# sure to clean up correctly no matter how we finish
|
||||
os.unlink(tmp_file)
|
||||
os.rmdir(tmp_dir)
|
||||
|
||||
def get_error_msg(self):
|
||||
# We got an error, lets go fetch the error message
|
||||
self._write_cmd('lastlog\n')
|
||||
|
||||
# read everything from the STDOUT to the next prompt
|
||||
stdout, report, stderr = self._read_until_prompt()
|
||||
|
||||
try:
|
||||
log = json.loads(report)
|
||||
|
||||
if 'log' in log:
|
||||
error_msg = ""
|
||||
# Walk the entire log array and build an error string
|
||||
for log_entry in log['log']:
|
||||
if log_entry['log_type'] == "error":
|
||||
if error_msg:
|
||||
error_msg += ', ' + log_entry['log_message']
|
||||
else:
|
||||
error_msg = log_entry['log_message']
|
||||
|
||||
return error_msg
|
||||
|
||||
return 'No error reason provided! (missing "log" section)'
|
||||
except ValueError:
|
||||
log_error("Invalid JSON returned from LVM")
|
||||
log_error("BEGIN>>\n%s\n<<END" % report)
|
||||
return "Invalid JSON returned from LVM when retrieving exit code"
|
||||
|
||||
def call_lvm(self, argv, debug=False):
|
||||
rc = 1
|
||||
error_msg = ""
|
||||
json_result = ""
|
||||
|
||||
if self.lvm_shell.poll():
|
||||
raise Exception(
|
||||
self.lvm_shell.returncode,
|
||||
"Underlying lvm shell process is not present!")
|
||||
|
||||
# create the command string
|
||||
cmd = " ".join(_quote_arg(arg) for arg in argv)
|
||||
cmd += "\n"
|
||||
@@ -121,46 +197,36 @@ class LVMShellProxy(object):
|
||||
# run the command by writing it to the shell's STDIN
|
||||
self._write_cmd(cmd)
|
||||
|
||||
# If lvm is utilizing gnu readline, it echos stdin to stdout
|
||||
if self.echo:
|
||||
self._discard_echo(cmd)
|
||||
|
||||
# read everything from the STDOUT to the next prompt
|
||||
stdout, exit_code = self._read_until_prompt()
|
||||
stdout, report, stderr = self._read_until_prompt()
|
||||
|
||||
# read everything from STDERR if there's something (we waited for the
|
||||
# prompt on STDOUT so there should be all or nothing at this point on
|
||||
# STDERR)
|
||||
stderr = None
|
||||
try:
|
||||
t_error = self.lvm_shell.stderr.read()
|
||||
if t_error:
|
||||
stderr = t_error.decode("utf-8")
|
||||
except IOError:
|
||||
# nothing on STDERR
|
||||
pass
|
||||
|
||||
if exit_code is not None:
|
||||
rc = exit_code
|
||||
else:
|
||||
# LVM does write to stderr even when it did complete successfully,
|
||||
# so without having the exit code in the prompt we can never be
|
||||
# sure.
|
||||
if stderr:
|
||||
rc = 1
|
||||
else:
|
||||
rc = 0
|
||||
# Parse the report to see what happened
|
||||
if report and len(report):
|
||||
json_result = json.loads(report)
|
||||
if 'log' in json_result:
|
||||
if json_result['log'][-1:][0]['log_ret_code'] == '1':
|
||||
rc = 0
|
||||
else:
|
||||
error_msg = self.get_error_msg()
|
||||
|
||||
if debug or rc != 0:
|
||||
log_error(('CMD: %s' % cmd))
|
||||
log_error(("EC = %d" % rc))
|
||||
log_error(("STDOUT=\n %s\n" % stdout))
|
||||
log_error(("STDERR=\n %s\n" % stderr))
|
||||
log_error(("ERROR_MSG=\n %s\n" % error_msg))
|
||||
|
||||
return (rc, stdout, stderr)
|
||||
return rc, json_result, error_msg
|
||||
|
||||
def exit_shell(self):
|
||||
try:
|
||||
self._write_cmd('exit\n')
|
||||
except Exception as e:
|
||||
log_error(str(e))
|
||||
|
||||
def __del__(self):
|
||||
self.lvm_shell.terminate()
|
||||
try:
|
||||
self.lvm_shell.terminate()
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -170,10 +236,15 @@ if __name__ == "__main__":
|
||||
while in_line:
|
||||
in_line = input("lvm> ")
|
||||
if in_line:
|
||||
ret, out, err, = shell.call_lvm(in_line.split())
|
||||
print(("RET: %d" % ret))
|
||||
start = time.time()
|
||||
ret, out, err = shell.call_lvm(in_line.split())
|
||||
end = time.time()
|
||||
|
||||
print(("RC: %d" % ret))
|
||||
print(("OUT:\n%s" % out))
|
||||
print(("ERR:\n%s" % err))
|
||||
|
||||
print("Command = %f seconds" % (end - start))
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except EOFError:
|
||||
|
@@ -13,17 +13,14 @@ from collections import OrderedDict
|
||||
|
||||
import pprint as prettyprint
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
from . import cmdhandler
|
||||
from .utils import log_debug, log_error
|
||||
except SystemError:
|
||||
import cmdhandler
|
||||
from utils import log_debug
|
||||
from lvmdbusd import cmdhandler
|
||||
from lvmdbusd.utils import log_debug, log_error
|
||||
|
||||
|
||||
class DataStore(object):
|
||||
def __init__(self):
|
||||
def __init__(self, usejson=True):
|
||||
self.pvs = {}
|
||||
self.vgs = {}
|
||||
self.lvs = {}
|
||||
@@ -41,6 +38,11 @@ class DataStore(object):
|
||||
# self.refresh()
|
||||
self.num_refreshes = 0
|
||||
|
||||
if usejson:
|
||||
self.json = cmdhandler.supports_json()
|
||||
else:
|
||||
self.json = usejson
|
||||
|
||||
@staticmethod
|
||||
def _insert_record(table, key, record, allowed_multiple):
|
||||
if key in table:
|
||||
@@ -77,7 +79,58 @@ class DataStore(object):
|
||||
for p in pvs:
|
||||
DataStore._insert_record(
|
||||
c_pvs, p['pv_uuid'], p,
|
||||
['pv_seg_start', 'pvseg_size', 'segtype'])
|
||||
['pvseg_start', 'pvseg_size', 'segtype'])
|
||||
|
||||
for p in c_pvs.values():
|
||||
# Capture which PVs are associated with which VG
|
||||
if p['vg_uuid'] not in c_pvs_in_vgs:
|
||||
c_pvs_in_vgs[p['vg_uuid']] = []
|
||||
|
||||
if p['vg_name']:
|
||||
c_pvs_in_vgs[p['vg_uuid']].append(
|
||||
(p['pv_name'], p['pv_uuid']))
|
||||
|
||||
# Lookup for translating between /dev/<name> and pv uuid
|
||||
c_lookup[p['pv_name']] = p['pv_uuid']
|
||||
|
||||
return c_pvs, c_lookup, c_pvs_in_vgs
|
||||
|
||||
@staticmethod
|
||||
def _parse_pvs_json(_all):
|
||||
|
||||
c_pvs = OrderedDict()
|
||||
c_lookup = {}
|
||||
c_pvs_in_vgs = {}
|
||||
|
||||
# Each item item in the report is a collection of information pertaining
|
||||
# to the vg
|
||||
for r in _all['report']:
|
||||
tmp_pv = []
|
||||
|
||||
# Get the pv data for this VG.
|
||||
if 'pv' in r:
|
||||
tmp_pv.extend(r['pv'])
|
||||
|
||||
# Sort them
|
||||
sorted_tmp_pv = sorted(tmp_pv, key=lambda pk: pk['pv_name'])
|
||||
|
||||
# Add them to result set
|
||||
for p in sorted_tmp_pv:
|
||||
c_pvs[p['pv_uuid']] = p
|
||||
|
||||
if 'pvseg' in r:
|
||||
for s in r['pvseg']:
|
||||
r = c_pvs[s['pv_uuid']]
|
||||
r.setdefault('pvseg_start', []).append(s['pvseg_start'])
|
||||
r.setdefault('pvseg_size', []).append(s['pvseg_size'])
|
||||
r.setdefault('segtype', []).append(s['segtype'])
|
||||
|
||||
# TODO: Remove this bug work around when we have orphan segs.
|
||||
for i in c_pvs.values():
|
||||
if 'pvseg_start' not in i:
|
||||
i['pvseg_start'] = '0'
|
||||
i['pvseg_size'] = i['pv_pe_count']
|
||||
i['segtype'] = 'free'
|
||||
|
||||
for p in c_pvs.values():
|
||||
# Capture which PVs are associated with which VG
|
||||
@@ -107,20 +160,31 @@ class DataStore(object):
|
||||
return c_vgs, c_lookup
|
||||
|
||||
@staticmethod
|
||||
def _parse_lvs(_lvs):
|
||||
lvs = sorted(_lvs, key=lambda vk: vk['lv_name'])
|
||||
def _parse_vgs_json(_all):
|
||||
|
||||
c_lvs = OrderedDict()
|
||||
c_lvs_in_vgs = {}
|
||||
c_lvs_hidden = {}
|
||||
c_lv_full_lookup = {}
|
||||
tmp_vg = []
|
||||
for r in _all['report']:
|
||||
# Get the pv data for this VG.
|
||||
if 'vg' in r:
|
||||
tmp_vg.extend(r['vg'])
|
||||
|
||||
for i in lvs:
|
||||
full_name = "%s/%s" % (i['vg_name'], i['lv_name'])
|
||||
c_lv_full_lookup[full_name] = i['lv_uuid']
|
||||
DataStore._insert_record(
|
||||
c_lvs, i['lv_uuid'], i,
|
||||
['seg_pe_ranges', 'segtype'])
|
||||
# Sort for consistent output, however this is optional
|
||||
vgs = sorted(tmp_vg, key=lambda vk: vk['vg_name'])
|
||||
|
||||
c_vgs = OrderedDict()
|
||||
c_lookup = {}
|
||||
|
||||
for i in vgs:
|
||||
c_lookup[i['vg_name']] = i['vg_uuid']
|
||||
c_vgs[i['vg_uuid']] = i
|
||||
|
||||
return c_vgs, c_lookup
|
||||
|
||||
@staticmethod
|
||||
def _parse_lvs_common(c_lvs, c_lv_full_lookup):
|
||||
|
||||
c_lvs_in_vgs = OrderedDict()
|
||||
c_lvs_hidden = OrderedDict()
|
||||
|
||||
for i in c_lvs.values():
|
||||
if i['vg_uuid'] not in c_lvs_in_vgs:
|
||||
@@ -150,6 +214,48 @@ class DataStore(object):
|
||||
|
||||
return c_lvs, c_lvs_in_vgs, c_lvs_hidden, c_lv_full_lookup
|
||||
|
||||
@staticmethod
|
||||
def _parse_lvs(_lvs):
|
||||
lvs = sorted(_lvs, key=lambda vk: vk['lv_name'])
|
||||
|
||||
c_lvs = OrderedDict()
|
||||
c_lv_full_lookup = OrderedDict()
|
||||
|
||||
for i in lvs:
|
||||
full_name = "%s/%s" % (i['vg_name'], i['lv_name'])
|
||||
c_lv_full_lookup[full_name] = i['lv_uuid']
|
||||
DataStore._insert_record(
|
||||
c_lvs, i['lv_uuid'], i,
|
||||
['seg_pe_ranges', 'segtype'])
|
||||
|
||||
return DataStore._parse_lvs_common(c_lvs, c_lv_full_lookup)
|
||||
|
||||
@staticmethod
|
||||
def _parse_lvs_json(_all):
|
||||
|
||||
c_lvs = OrderedDict()
|
||||
c_lv_full_lookup = {}
|
||||
|
||||
# Each item item in the report is a collection of information pertaining
|
||||
# to the vg
|
||||
for r in _all['report']:
|
||||
# Get the lv data for this VG.
|
||||
if 'lv' in r:
|
||||
# Add them to result set
|
||||
for i in r['lv']:
|
||||
full_name = "%s/%s" % (i['vg_name'], i['lv_name'])
|
||||
c_lv_full_lookup[full_name] = i['lv_uuid']
|
||||
c_lvs[i['lv_uuid']] = i
|
||||
|
||||
# Add in the segment data
|
||||
if 'seg' in r:
|
||||
for s in r['seg']:
|
||||
r = c_lvs[s['lv_uuid']]
|
||||
r.setdefault('seg_pe_ranges', []).append(s['seg_pe_ranges'])
|
||||
r.setdefault('segtype', []).append(s['segtype'])
|
||||
|
||||
return DataStore._parse_lvs_common(c_lvs, c_lv_full_lookup)
|
||||
|
||||
@staticmethod
|
||||
def _make_list(l):
|
||||
if not isinstance(l, list):
|
||||
@@ -278,13 +384,22 @@ class DataStore(object):
|
||||
log_debug("lvmdb - refresh entry")
|
||||
|
||||
# Grab everything first then parse it
|
||||
_raw_pvs = cmdhandler.pv_retrieve_with_segs()
|
||||
_raw_vgs = cmdhandler.vg_retrieve(None)
|
||||
_raw_lvs = cmdhandler.lv_retrieve_with_segments()
|
||||
if self.json:
|
||||
# Do a single lvm retrieve for everything in json
|
||||
a = cmdhandler.lvm_full_report_json()
|
||||
|
||||
_pvs, _pvs_lookup, _pvs_in_vgs = self._parse_pvs(_raw_pvs)
|
||||
_vgs, _vgs_lookup = self._parse_vgs(_raw_vgs)
|
||||
_lvs, _lvs_in_vgs, _lvs_hidden, _lvs_lookup = self._parse_lvs(_raw_lvs)
|
||||
_pvs, _pvs_lookup, _pvs_in_vgs = self._parse_pvs_json(a)
|
||||
_vgs, _vgs_lookup = self._parse_vgs_json(a)
|
||||
_lvs, _lvs_in_vgs, _lvs_hidden, _lvs_lookup = self._parse_lvs_json(a)
|
||||
|
||||
else:
|
||||
_raw_pvs = cmdhandler.pv_retrieve_with_segs()
|
||||
_raw_vgs = cmdhandler.vg_retrieve(None)
|
||||
_raw_lvs = cmdhandler.lv_retrieve_with_segments()
|
||||
|
||||
_pvs, _pvs_lookup, _pvs_in_vgs = self._parse_pvs(_raw_pvs)
|
||||
_vgs, _vgs_lookup = self._parse_vgs(_raw_vgs)
|
||||
_lvs, _lvs_in_vgs, _lvs_hidden, _lvs_lookup = self._parse_lvs(_raw_lvs)
|
||||
|
||||
# Set all
|
||||
self.pvs = _pvs
|
||||
@@ -318,6 +433,12 @@ class DataStore(object):
|
||||
rc.append(self.pvs[self.pv_path_to_uuid[s]])
|
||||
return rc
|
||||
|
||||
def pv_missing(self, pv_uuid):
|
||||
if pv_uuid in self.pvs:
|
||||
if self.pvs[pv_uuid]['pv_missing'] == '':
|
||||
return False
|
||||
return True
|
||||
|
||||
def fetch_vgs(self, vg_name):
|
||||
if not vg_name:
|
||||
return self.vgs.values()
|
||||
@@ -348,7 +469,7 @@ class DataStore(object):
|
||||
|
||||
def pv_pe_segments(self, pv_uuid):
|
||||
pv = self.pvs[pv_uuid]
|
||||
return list(zip(pv['pv_seg_start'], pv['pvseg_size']))
|
||||
return list(zip(pv['pvseg_start'], pv['pvseg_size']))
|
||||
|
||||
def pv_contained_lv(self, pv_device):
|
||||
rc = []
|
||||
@@ -389,12 +510,21 @@ class DataStore(object):
|
||||
if __name__ == "__main__":
|
||||
pp = prettyprint.PrettyPrinter(indent=4)
|
||||
|
||||
ds = DataStore()
|
||||
use_json = False
|
||||
|
||||
if len(sys.argv) != 1:
|
||||
print(len(sys.argv))
|
||||
use_json = True
|
||||
|
||||
ds = DataStore(use_json)
|
||||
ds.refresh()
|
||||
|
||||
print("PVS")
|
||||
for v in ds.pvs.values():
|
||||
pp.pprint(v)
|
||||
print('PV missing is %s' % ds.pv_missing(v['pv_uuid']))
|
||||
|
||||
print("VGS")
|
||||
for v in ds.vgs.values():
|
||||
pp.pprint(v)
|
||||
|
||||
|
@@ -10,7 +10,7 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
import lvmdbusd
|
||||
from lvmdbusd import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(lvmdbusd.main())
|
||||
sys.exit(main())
|
||||
|
@@ -16,18 +16,19 @@ from . import cmdhandler
|
||||
import time
|
||||
import signal
|
||||
import dbus
|
||||
import dbus.mainloop.glib
|
||||
from . import lvmdb
|
||||
# noinspection PyUnresolvedReferences
|
||||
from gi.repository import GLib
|
||||
from .fetch import load
|
||||
from .manager import Manager
|
||||
from .background import background_reaper
|
||||
import traceback
|
||||
import queue
|
||||
from . import udevwatch
|
||||
from .utils import log_debug
|
||||
from .utils import log_debug, log_error
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
from .refresh import handle_external_event, event_complete
|
||||
|
||||
|
||||
@@ -62,6 +63,7 @@ def _discard_pending_refreshes():
|
||||
|
||||
def process_request():
|
||||
while cfg.run.value != 0:
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
req = cfg.worker_q.get(True, 5)
|
||||
|
||||
@@ -83,7 +85,7 @@ def process_request():
|
||||
log_debug(
|
||||
"Inspect method %s for too many refreshes" %
|
||||
(str(req.method)))
|
||||
log_debug("Complete ")
|
||||
log_debug("Method complete ")
|
||||
except queue.Empty:
|
||||
pass
|
||||
except Exception:
|
||||
@@ -92,29 +94,44 @@ def process_request():
|
||||
|
||||
|
||||
def main():
|
||||
start = time.time()
|
||||
# Add simple command line handling
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--udev", action='store_true',
|
||||
help="Use udev for updating state", default=False,
|
||||
dest='use_udev')
|
||||
parser.add_argument("--debug", action='store_true',
|
||||
help="Dump debug messages", default=False,
|
||||
dest='debug')
|
||||
parser.add_argument(
|
||||
"--udev", action='store_true',
|
||||
help="Use udev for updating state",
|
||||
default=False,
|
||||
dest='use_udev')
|
||||
parser.add_argument(
|
||||
"--debug", action='store_true',
|
||||
help="Dump debug messages", default=False,
|
||||
dest='debug')
|
||||
parser.add_argument(
|
||||
"--nojson", action='store_false',
|
||||
help="Do not use LVM JSON output (disables lvmshell)", default=True,
|
||||
dest='use_json')
|
||||
parser.add_argument(
|
||||
"--lvmshell", action='store_true',
|
||||
help="Use the lvm shell, not fork & exec lvm",
|
||||
default=False,
|
||||
dest='use_lvm_shell')
|
||||
|
||||
use_session = os.getenv('LVMDBUSD_USE_SESSION', False)
|
||||
|
||||
# Ensure that we get consistent output for parsing stdout/stderr
|
||||
os.environ["LC_ALL"] = "C"
|
||||
|
||||
args = parser.parse_args()
|
||||
cfg.args = parser.parse_args()
|
||||
|
||||
cfg.DEBUG = args.debug
|
||||
if cfg.args.use_lvm_shell and not cfg.args.use_json:
|
||||
log_error("You cannot specify --lvmshell and --nojson")
|
||||
sys.exit(1)
|
||||
|
||||
cmdhandler.set_execution(cfg.args.use_lvm_shell)
|
||||
|
||||
# List of threads that we start up
|
||||
thread_list = []
|
||||
|
||||
start = time.time()
|
||||
|
||||
# Install signal handlers
|
||||
for s in [signal.SIGHUP, signal.SIGINT]:
|
||||
try:
|
||||
@@ -137,39 +154,38 @@ def main():
|
||||
|
||||
cfg.load = load
|
||||
|
||||
cfg.db = lvmdb.DataStore()
|
||||
cfg.db = lvmdb.DataStore(cfg.args.use_json)
|
||||
|
||||
# Start up thread to monitor pv moves
|
||||
thread_list.append(
|
||||
threading.Thread(target=background_reaper, name="pv_move_reaper"))
|
||||
|
||||
# Using a thread to process requests.
|
||||
# Using a thread to process requests, we cannot hang the dbus library
|
||||
# thread that is handling the dbus interface
|
||||
thread_list.append(threading.Thread(target=process_request))
|
||||
|
||||
cfg.load(refresh=False, emit_signal=False)
|
||||
cfg.load(refresh=False, emit_signal=False, need_main_thread=False)
|
||||
cfg.loop = GLib.MainLoop()
|
||||
|
||||
for process in thread_list:
|
||||
process.damon = True
|
||||
process.start()
|
||||
|
||||
# Add udev watching
|
||||
if cfg.args.use_udev:
|
||||
log_debug('Utilizing udev to trigger updates')
|
||||
|
||||
# In all cases we are going to monitor for udev until we get an
|
||||
# ExternalEvent. In the case where we get an external event and the user
|
||||
# didn't specify --udev we will stop monitoring udev
|
||||
udevwatch.add()
|
||||
|
||||
end = time.time()
|
||||
log_debug(
|
||||
'Service ready! total time= %.2f, lvm time= %.2f count= %d' %
|
||||
'Service ready! total time= %.4f, lvm time= %.4f count= %d' %
|
||||
(end - start, cmdhandler.total_time, cmdhandler.total_count),
|
||||
'bg_black', 'fg_light_green')
|
||||
|
||||
# Add udev watching
|
||||
if args.use_udev:
|
||||
log_debug('Utilizing udev to trigger updates')
|
||||
udevwatch.add()
|
||||
|
||||
try:
|
||||
if cfg.run.value != 0:
|
||||
cfg.loop.run()
|
||||
|
||||
if args.use_udev:
|
||||
udevwatch.remove()
|
||||
udevwatch.remove()
|
||||
|
||||
for process in thread_list:
|
||||
process.join()
|
||||
|
@@ -17,11 +17,12 @@ from . import cmdhandler
|
||||
from .fetch import load_pvs, load_vgs
|
||||
from .request import RequestEntry
|
||||
from .refresh import event_add
|
||||
from . import udevwatch
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
class Manager(AutomatedProperties):
|
||||
_Version_meta = ("t", MANAGER_INTERFACE)
|
||||
_Version_meta = ("s", MANAGER_INTERFACE)
|
||||
|
||||
def __init__(self, object_path):
|
||||
super(Manager, self).__init__(object_path)
|
||||
@@ -29,25 +30,22 @@ class Manager(AutomatedProperties):
|
||||
|
||||
@property
|
||||
def Version(self):
|
||||
return '1.0.0'
|
||||
return dbus.String('1.0.0')
|
||||
|
||||
@staticmethod
|
||||
def _pv_create(device, create_options):
|
||||
|
||||
# Check to see if we are already trying to create a PV for an existing
|
||||
# PV
|
||||
pv = cfg.om.get_object_path_by_lvm_id(
|
||||
device, device, None, False)
|
||||
pv = cfg.om.get_object_path_by_uuid_lvm_id(device, device)
|
||||
if pv:
|
||||
raise dbus.exceptions.DBusException(
|
||||
MANAGER_INTERFACE, "PV Already exists!")
|
||||
|
||||
created_pv = []
|
||||
rc, out, err = cmdhandler.pv_create(create_options, [device])
|
||||
if rc == 0:
|
||||
pvs = load_pvs([device], emit_signal=True)[0]
|
||||
for p in pvs:
|
||||
created_pv = p.dbus_object_path()
|
||||
cfg.load()
|
||||
created_pv = cfg.om.get_object_path_by_lvm_id(device)
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
MANAGER_INTERFACE,
|
||||
@@ -80,20 +78,14 @@ class Manager(AutomatedProperties):
|
||||
MANAGER_INTERFACE, 'object path = %s not found' % p)
|
||||
|
||||
rc, out, err = cmdhandler.vg_create(create_options, pv_devices, name)
|
||||
created_vg = "/"
|
||||
|
||||
if rc == 0:
|
||||
vgs = load_vgs([name], emit_signal=True)[0]
|
||||
for v in vgs:
|
||||
created_vg = v.dbus_object_path()
|
||||
|
||||
# Update the PVS
|
||||
load_pvs(refresh=True, emit_signal=True, cache_refresh=False)
|
||||
cfg.load()
|
||||
return cfg.om.get_object_path_by_lvm_id(name)
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
MANAGER_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
return created_vg
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=MANAGER_INTERFACE,
|
||||
@@ -114,6 +106,10 @@ class Manager(AutomatedProperties):
|
||||
|
||||
# This is a diagnostic and should not be run in normal operation, so
|
||||
# lets remove the log entries for refresh as it's implied.
|
||||
|
||||
# Run an internal diagnostic on the object manager look up tables
|
||||
lc = cfg.om.validate_lookups()
|
||||
|
||||
rc = cfg.load(log=False)
|
||||
|
||||
if rc != 0:
|
||||
@@ -121,7 +117,7 @@ class Manager(AutomatedProperties):
|
||||
'bg_black', 'fg_light_red')
|
||||
else:
|
||||
utils.log_debug('Manager.Refresh - exit %d' % (rc))
|
||||
return rc
|
||||
return rc + lc
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=MANAGER_INTERFACE,
|
||||
@@ -159,28 +155,34 @@ class Manager(AutomatedProperties):
|
||||
:param key: The lookup value
|
||||
:return: Return the object path. If object not found you will get '/'
|
||||
"""
|
||||
p = cfg.om.get_object_path_by_lvm_id(
|
||||
key, key, gen_new=False)
|
||||
p = cfg.om.get_object_path_by_uuid_lvm_id(key, key)
|
||||
if p:
|
||||
return p
|
||||
return '/'
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=MANAGER_INTERFACE,
|
||||
in_signature='b')
|
||||
in_signature='b', out_signature='b')
|
||||
def UseLvmShell(self, yes_no):
|
||||
"""
|
||||
Allow the client to enable/disable lvm shell, used for testing
|
||||
:param yes_no:
|
||||
:return: Nothing
|
||||
"""
|
||||
cmdhandler.set_execution(yes_no)
|
||||
return dbus.Boolean(cmdhandler.set_execution(yes_no))
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=MANAGER_INTERFACE,
|
||||
in_signature='s', out_signature='i')
|
||||
def ExternalEvent(self, command):
|
||||
|
||||
# If a user didn't explicitly specify udev, we will turn it off now.
|
||||
if not cfg.args.use_udev:
|
||||
if udevwatch.remove():
|
||||
utils.log_debug("ExternalEvent received, disabling "
|
||||
"udev monitoring")
|
||||
# We are dependent on external events now to stay current!
|
||||
cfg.ee = True
|
||||
event_add((command,))
|
||||
return dbus.Int32(0)
|
||||
|
||||
|
@@ -12,8 +12,9 @@ import threading
|
||||
import traceback
|
||||
import dbus
|
||||
import os
|
||||
import copy
|
||||
from . import cfg
|
||||
from .utils import log_debug
|
||||
from .utils import log_debug, pv_obj_path_generate, log_error
|
||||
from .automatedproperties import AutomatedProperties
|
||||
|
||||
|
||||
@@ -70,12 +71,37 @@ class ObjectManager(AutomatedProperties):
|
||||
log_debug(('SIGNAL: InterfacesRemoved(%s, %s)' %
|
||||
(str(object_path), str(interface_list))))
|
||||
|
||||
def validate_lookups(self):
|
||||
with self.rlock:
|
||||
tmp_lookups = copy.deepcopy(self._id_to_object_path)
|
||||
|
||||
# iterate over all we know, removing from the copy. If all is well
|
||||
# we will have zero items left over
|
||||
for path, md in self._objects.items():
|
||||
obj, lvm_id, uuid = md
|
||||
|
||||
if lvm_id:
|
||||
assert path == tmp_lookups[lvm_id]
|
||||
del tmp_lookups[lvm_id]
|
||||
|
||||
if uuid:
|
||||
assert path == tmp_lookups[uuid]
|
||||
del tmp_lookups[uuid]
|
||||
|
||||
rc = len(tmp_lookups)
|
||||
if rc:
|
||||
# Error condition
|
||||
log_error("_id_to_object_path has extraneous lookups!")
|
||||
for key, path in tmp_lookups.items():
|
||||
log_error("Key= %s, path= %s" % (key, path))
|
||||
return rc
|
||||
|
||||
def _lookup_add(self, obj, path, lvm_id, uuid):
|
||||
"""
|
||||
Store information about what we added to the caches so that we
|
||||
can remove it cleanly
|
||||
:param obj: The dbus object we are storing
|
||||
:param lvm_id: The user name for the asset
|
||||
:param lvm_id: The lvm id for the asset
|
||||
:param uuid: The uuid for the asset
|
||||
:return:
|
||||
"""
|
||||
@@ -85,7 +111,12 @@ class ObjectManager(AutomatedProperties):
|
||||
self._lookup_remove(path)
|
||||
|
||||
self._objects[path] = (obj, lvm_id, uuid)
|
||||
self._id_to_object_path[lvm_id] = path
|
||||
|
||||
# Make sure we have one or the other
|
||||
assert lvm_id or uuid
|
||||
|
||||
if lvm_id:
|
||||
self._id_to_object_path[lvm_id] = path
|
||||
|
||||
if uuid:
|
||||
self._id_to_object_path[uuid] = path
|
||||
@@ -94,8 +125,13 @@ class ObjectManager(AutomatedProperties):
|
||||
# Note: Only called internally, lock implied
|
||||
if obj_path in self._objects:
|
||||
(obj, lvm_id, uuid) = self._objects[obj_path]
|
||||
del self._id_to_object_path[lvm_id]
|
||||
del self._id_to_object_path[uuid]
|
||||
|
||||
if lvm_id in self._id_to_object_path:
|
||||
del self._id_to_object_path[lvm_id]
|
||||
|
||||
if uuid in self._id_to_object_path:
|
||||
del self._id_to_object_path[uuid]
|
||||
|
||||
del self._objects[obj_path]
|
||||
|
||||
def lookup_update(self, dbus_obj, new_uuid, new_lvm_id):
|
||||
@@ -124,8 +160,8 @@ class ObjectManager(AutomatedProperties):
|
||||
with self.rlock:
|
||||
path, props = dbus_object.emit_data()
|
||||
|
||||
# print 'Registering object path %s for %s' %
|
||||
# (path, dbus_object.lvm_id)
|
||||
# print('Registering object path %s for %s' %
|
||||
# (path, dbus_object.lvm_id))
|
||||
|
||||
# We want fast access to the object by a number of different ways
|
||||
# so we use multiple hashs with different keys
|
||||
@@ -173,7 +209,7 @@ class ObjectManager(AutomatedProperties):
|
||||
def get_object_by_uuid_lvm_id(self, uuid, lvm_id):
|
||||
with self.rlock:
|
||||
return self.get_object_by_path(
|
||||
self.get_object_path_by_lvm_id(uuid, lvm_id, None, False))
|
||||
self.get_object_path_by_uuid_lvm_id(uuid, lvm_id))
|
||||
|
||||
def get_object_by_lvm_id(self, lvm_id):
|
||||
"""
|
||||
@@ -185,6 +221,17 @@ class ObjectManager(AutomatedProperties):
|
||||
return self.get_object_by_path(self._id_to_object_path[lvm_id])
|
||||
return None
|
||||
|
||||
def get_object_path_by_lvm_id(self, lvm_id):
|
||||
"""
|
||||
Given an lvm identifier, return the object path for it
|
||||
:param lvm_id: The lvm identifier
|
||||
:return: Object path or '/' if not found
|
||||
"""
|
||||
with self.rlock:
|
||||
if lvm_id in self._id_to_object_path:
|
||||
return self._id_to_object_path[lvm_id]
|
||||
return '/'
|
||||
|
||||
def _uuid_verify(self, path, uuid, lvm_id):
|
||||
"""
|
||||
Ensure uuid is present for a successful lvm_id lookup
|
||||
@@ -195,78 +242,111 @@ class ObjectManager(AutomatedProperties):
|
||||
:return: None
|
||||
"""
|
||||
# This gets called when we found an object based on lvm_id, ensure
|
||||
# uuid is correct too, as they can change
|
||||
# uuid is correct too, as they can change. There is no durable
|
||||
# non-changeable name in lvm
|
||||
if lvm_id != uuid:
|
||||
if uuid not in self._id_to_object_path:
|
||||
if uuid and uuid not in self._id_to_object_path:
|
||||
obj = self.get_object_by_path(path)
|
||||
self._lookup_add(obj, path, lvm_id, uuid)
|
||||
|
||||
def _return_lookup(self, uuid, lvm_identifier):
|
||||
def _lvm_id_verify(self, path, uuid, lvm_id):
|
||||
"""
|
||||
We found an identifier, so lets return the path to the found object
|
||||
:param uuid: The lvm uuid
|
||||
:param lvm_identifier: The lvm_id used to find object
|
||||
:return:
|
||||
Ensure lvm_id is present for a successful uuid lookup
|
||||
NOTE: Internal call, assumes under object manager lock
|
||||
:param path: Path to object we looked up
|
||||
:param uuid: uuid used to find object
|
||||
:param lvm_id: lvm_id to verify
|
||||
:return: None
|
||||
"""
|
||||
path = self._id_to_object_path[lvm_identifier]
|
||||
self._uuid_verify(path, uuid, lvm_identifier)
|
||||
# This gets called when we found an object based on uuid, ensure
|
||||
# lvm_id is correct too, as they can change. There is no durable
|
||||
# non-changeable name in lvm
|
||||
if lvm_id != uuid:
|
||||
if lvm_id and lvm_id not in self._id_to_object_path:
|
||||
obj = self.get_object_by_path(path)
|
||||
self._lookup_add(obj, path, lvm_id, uuid)
|
||||
|
||||
def _id_lookup(self, the_id):
|
||||
path = None
|
||||
|
||||
if the_id:
|
||||
# The _id_to_object_path contains hash keys for everything, so
|
||||
# uuid and lvm_id
|
||||
if the_id in self._id_to_object_path:
|
||||
path = self._id_to_object_path[the_id]
|
||||
else:
|
||||
if "/" in the_id:
|
||||
if the_id.startswith('/'):
|
||||
# We could have a pv device path lookup that failed,
|
||||
# lets try canonical form and try again.
|
||||
canonical = os.path.realpath(the_id)
|
||||
if canonical in self._id_to_object_path:
|
||||
path = self._id_to_object_path[canonical]
|
||||
else:
|
||||
vg, lv = the_id.split("/", 1)
|
||||
int_lvm_id = vg + "/" + ("[%s]" % lv)
|
||||
if int_lvm_id in self._id_to_object_path:
|
||||
path = self._id_to_object_path[int_lvm_id]
|
||||
return path
|
||||
|
||||
def get_object_path_by_lvm_id(self, uuid, lvm_id, path_create=None,
|
||||
gen_new=True):
|
||||
def get_object_path_by_uuid_lvm_id(self, uuid, lvm_id, path_create=None):
|
||||
"""
|
||||
For a given lvm asset return the dbus object registered to it. If the
|
||||
object is not found and gen_new == True and path_create is a valid
|
||||
function we will create a new path, register it and return it.
|
||||
:param uuid: The uuid for the lvm object
|
||||
:param lvm_id: The lvm name
|
||||
:param path_create: If true create an object path if not found
|
||||
:param gen_new: The function used to create the new path
|
||||
For a given lvm asset return the dbus object path registered for it.
|
||||
This method first looks up by uuid and then by lvm_id. You
|
||||
can search by just one by setting uuid == lvm_id (uuid or lvm_id).
|
||||
If the object is not found and path_create is a not None, the
|
||||
path_create function will be called to create a new object path and
|
||||
register it with the object manager for the specified uuid & lvm_id.
|
||||
Note: If path create is not None, uuid and lvm_id cannot be equal
|
||||
:param uuid: The uuid for the lvm object we are searching for
|
||||
:param lvm_id: The lvm name (eg. pv device path, vg name, lv full name)
|
||||
:param path_create: If not None, create the path using this function if
|
||||
we fail to find the object by uuid or lvm_id.
|
||||
:returns None if lvm asset not found and path_create == None otherwise
|
||||
a valid dbus object path
|
||||
"""
|
||||
with self.rlock:
|
||||
assert lvm_id
|
||||
assert uuid
|
||||
|
||||
if gen_new:
|
||||
assert path_create
|
||||
if path_create:
|
||||
assert uuid != lvm_id
|
||||
|
||||
path = None
|
||||
|
||||
if lvm_id in self._id_to_object_path:
|
||||
self._return_lookup(uuid, lvm_id)
|
||||
|
||||
if "/" in lvm_id:
|
||||
vg, lv = lvm_id.split("/", 1)
|
||||
int_lvm_id = vg + "/" + ("[%s]" % lv)
|
||||
if int_lvm_id in self._id_to_object_path:
|
||||
self._return_lookup(uuid, int_lvm_id)
|
||||
elif lvm_id.startswith('/'):
|
||||
# We could have a pv device path lookup that failed,
|
||||
# lets try canonical form and try again.
|
||||
canonical = os.path.realpath(lvm_id)
|
||||
if canonical in self._id_to_object_path:
|
||||
self._return_lookup(uuid, canonical)
|
||||
|
||||
if uuid and uuid in self._id_to_object_path:
|
||||
# If we get here it indicates that we found the object, but
|
||||
# the lvm_id lookup failed. In the case of a rename, the uuid
|
||||
# will be correct, but the lvm_id will be wrong and vise versa.
|
||||
# If the lvm_id does not equal the uuid, lets fix up the table
|
||||
# so that lookups will be handled correctly.
|
||||
path = self._id_to_object_path[uuid]
|
||||
|
||||
# In some cases we are looking up by one or the other, don't
|
||||
# update when they are the same.
|
||||
if uuid != lvm_id:
|
||||
obj = self.get_object_by_path(path)
|
||||
self._lookup_add(obj, path, lvm_id, uuid)
|
||||
# Check for Manager.LookUpByLvmId query, we cannot
|
||||
# check/verify/update the uuid and lvm_id lookups so don't!
|
||||
if uuid == lvm_id:
|
||||
path = self._id_lookup(lvm_id)
|
||||
else:
|
||||
if gen_new:
|
||||
path = path_create()
|
||||
self._lookup_add(None, path, lvm_id, uuid)
|
||||
# We have a uuid and a lvm_id we can do sanity checks to ensure
|
||||
# that they are consistent
|
||||
|
||||
# pprint('get_object_path_by_lvm_id(%s, %s, %s, %s: return %s' %
|
||||
# (uuid, lvm_id, str(path_create), str(gen_new), path))
|
||||
# If a PV is missing it's device path is '[unknown]' or some
|
||||
# other text derivation of unknown. When we find that a PV is
|
||||
# missing we will clear out the lvm_id as it's likely not unique
|
||||
# and thus not useful and potentially harmful for lookups.
|
||||
if path_create == pv_obj_path_generate and \
|
||||
cfg.db.pv_missing(uuid):
|
||||
lvm_id = None
|
||||
|
||||
# Lets check for the uuid first
|
||||
path = self._id_lookup(uuid)
|
||||
if path:
|
||||
# Verify the lvm_id is sane
|
||||
self._lvm_id_verify(path, uuid, lvm_id)
|
||||
else:
|
||||
# Unable to find by UUID, lets lookup by lvm_id
|
||||
path = self._id_lookup(lvm_id)
|
||||
if path:
|
||||
# Verify the uuid is sane
|
||||
self._uuid_verify(path, uuid, lvm_id)
|
||||
else:
|
||||
# We have exhausted all lookups, let's create if we can
|
||||
if path_create:
|
||||
path = path_create()
|
||||
self._lookup_add(None, path, lvm_id, uuid)
|
||||
|
||||
# print('get_object_path_by_lvm_id(%s, %s, %s, %s: return %s' %
|
||||
# (uuid, lvm_id, str(path_create), str(gen_new), path))
|
||||
|
||||
return path
|
||||
|
||||
|
@@ -18,7 +18,7 @@ from .utils import vg_obj_path_generate, n, pv_obj_path_generate, \
|
||||
from .loader import common
|
||||
from .request import RequestEntry
|
||||
from .state import State
|
||||
from .utils import round_size
|
||||
from .utils import round_size, mt_remove_dbus_objects
|
||||
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
@@ -55,9 +55,6 @@ class PvState(State):
|
||||
return self.lvm_path
|
||||
|
||||
def _lv_object_list(self, vg_name):
|
||||
|
||||
# Note we are returning "a(oa(tts))"
|
||||
|
||||
rc = []
|
||||
if vg_name:
|
||||
for lv in sorted(cfg.db.pv_contained_lv(self.lvm_id)):
|
||||
@@ -65,11 +62,11 @@ class PvState(State):
|
||||
full_name = "%s/%s" % (vg_name, lv_name)
|
||||
|
||||
path_create = lv_object_path_method(lv_name, meta)
|
||||
lv_path = cfg.om.get_object_path_by_lvm_id(
|
||||
lv_path = cfg.om.get_object_path_by_uuid_lvm_id(
|
||||
lv_uuid, full_name, path_create)
|
||||
|
||||
rc.append((lv_path, segs))
|
||||
return dbus.Array(rc, signature="(oa(tts))")
|
||||
return rc
|
||||
|
||||
# noinspection PyUnusedLocal,PyPep8Naming
|
||||
def __init__(self, lvm_path, Uuid, Name,
|
||||
@@ -83,7 +80,7 @@ class PvState(State):
|
||||
self.lv = self._lv_object_list(vg_name)
|
||||
|
||||
if vg_name:
|
||||
self.vg_path = cfg.om.get_object_path_by_lvm_id(
|
||||
self.vg_path = cfg.om.get_object_path_by_uuid_lvm_id(
|
||||
vg_uuid, vg_name, vg_obj_path_generate)
|
||||
else:
|
||||
self.vg_path = '/'
|
||||
@@ -93,8 +90,8 @@ class PvState(State):
|
||||
|
||||
def create_dbus_object(self, path):
|
||||
if not path:
|
||||
path = cfg.om.get_object_path_by_lvm_id(self.Uuid, self.Name,
|
||||
pv_obj_path_generate)
|
||||
path = cfg.om.get_object_path_by_uuid_lvm_id(self.Uuid, self.Name,
|
||||
pv_obj_path_generate)
|
||||
return Pv(path, self)
|
||||
|
||||
# noinspection PyMethodMayBeStatic
|
||||
@@ -143,7 +140,7 @@ class Pv(AutomatedProperties):
|
||||
if dbo:
|
||||
rc, out, err = cmdhandler.pv_remove(pv_name, remove_options)
|
||||
if rc == 0:
|
||||
cfg.om.remove_object(dbo, True)
|
||||
mt_remove_dbus_objects((dbo,))
|
||||
else:
|
||||
# Need to work on error handling, need consistent
|
||||
raise dbus.exceptions.DBusException(
|
||||
@@ -177,7 +174,7 @@ class Pv(AutomatedProperties):
|
||||
rc, out, err = cmdhandler.pv_resize(pv_name, new_size_bytes,
|
||||
resize_options)
|
||||
if rc == 0:
|
||||
dbo.refresh()
|
||||
cfg.load()
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
PV_INTERFACE,
|
||||
@@ -241,26 +238,20 @@ class Pv(AutomatedProperties):
|
||||
@property
|
||||
def PeSegments(self):
|
||||
if len(self.state.pe_segments):
|
||||
return self.state.pe_segments
|
||||
return dbus.Array(self.state.pe_segments, signature='(tt)')
|
||||
return dbus.Array([], '(tt)')
|
||||
|
||||
@property
|
||||
def Exportable(self):
|
||||
if self.state.attr[1] == 'x':
|
||||
return True
|
||||
return False
|
||||
return dbus.Boolean(self.state.attr[1] == 'x')
|
||||
|
||||
@property
|
||||
def Allocatable(self):
|
||||
if self.state.attr[0] == 'a':
|
||||
return True
|
||||
return False
|
||||
return dbus.Boolean(self.state.attr[0] == 'a')
|
||||
|
||||
@property
|
||||
def Missing(self):
|
||||
if self.state.attr[2] == 'm':
|
||||
return True
|
||||
return False
|
||||
return dbus.Boolean(self.state.attr[2] == 'm')
|
||||
|
||||
def object_path(self):
|
||||
return self._object_path
|
||||
@@ -275,8 +266,8 @@ class Pv(AutomatedProperties):
|
||||
|
||||
@property
|
||||
def Lv(self):
|
||||
return self.state.lv
|
||||
return dbus.Array(self.state.lv, signature="(oa(tts))")
|
||||
|
||||
@property
|
||||
def Vg(self):
|
||||
return self.state.vg_path
|
||||
return dbus.ObjectPath(self.state.vg_path)
|
||||
|
@@ -13,12 +13,12 @@ from gi.repository import GLib
|
||||
from .job import Job
|
||||
from . import cfg
|
||||
import traceback
|
||||
from .utils import log_error
|
||||
from .utils import log_error, mt_async_result
|
||||
|
||||
|
||||
class RequestEntry(object):
|
||||
def __init__(self, tmo, method, arguments, cb, cb_error,
|
||||
return_tuple=True):
|
||||
return_tuple=True, job_state=None):
|
||||
self.tmo = tmo
|
||||
self.method = method
|
||||
self.arguments = arguments
|
||||
@@ -29,10 +29,11 @@ class RequestEntry(object):
|
||||
self.lock = threading.RLock()
|
||||
self.done = False
|
||||
self._result = None
|
||||
self._job = False
|
||||
self._job = None
|
||||
self._rc = 0
|
||||
self._rc_error = None
|
||||
self._return_tuple = return_tuple
|
||||
self._job_state = job_state
|
||||
|
||||
if self.tmo < 0:
|
||||
# Client is willing to block forever
|
||||
@@ -53,12 +54,12 @@ class RequestEntry(object):
|
||||
r.timer_expired()
|
||||
|
||||
def _return_job(self):
|
||||
self._job = Job(self)
|
||||
self._job = Job(self, self._job_state)
|
||||
cfg.om.register_object(self._job, True)
|
||||
if self._return_tuple:
|
||||
self.cb(('/', self._job.dbus_object_path()))
|
||||
mt_async_result(self.cb, ('/', self._job.dbus_object_path()))
|
||||
else:
|
||||
self.cb(self._job.dbus_object_path())
|
||||
mt_async_result(self.cb, self._job.dbus_object_path())
|
||||
|
||||
def run_cmd(self):
|
||||
try:
|
||||
@@ -109,9 +110,9 @@ class RequestEntry(object):
|
||||
if error_rc == 0:
|
||||
if self.cb:
|
||||
if self._return_tuple:
|
||||
self.cb((result, '/'))
|
||||
mt_async_result(self.cb, (result, '/'))
|
||||
else:
|
||||
self.cb(result)
|
||||
mt_async_result(self.cb, result)
|
||||
else:
|
||||
if self.cb_error:
|
||||
if not error_exception:
|
||||
@@ -122,7 +123,7 @@ class RequestEntry(object):
|
||||
else:
|
||||
error_exception = Exception(error_msg)
|
||||
|
||||
self.cb_error(error_exception)
|
||||
mt_async_result(self.cb_error, error_exception)
|
||||
else:
|
||||
# We have a job and it's complete, indicate that it's done.
|
||||
# TODO: We need to signal the job is done too.
|
||||
@@ -130,7 +131,7 @@ class RequestEntry(object):
|
||||
self._job = None
|
||||
|
||||
def register_error(self, error_rc, error_message, error_exception):
|
||||
self._reg_ending(None, error_rc, error_message, error_exception)
|
||||
self._reg_ending('/', error_rc, error_message, error_exception)
|
||||
|
||||
def register_result(self, result):
|
||||
self._reg_ending(result)
|
||||
|
@@ -8,10 +8,12 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import pyudev
|
||||
import threading
|
||||
from .refresh import event_add
|
||||
from . import cfg
|
||||
|
||||
observer = None
|
||||
observer_lock = threading.RLock()
|
||||
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
@@ -40,15 +42,20 @@ def filter_event(action, device):
|
||||
|
||||
|
||||
def add():
|
||||
global observer
|
||||
context = pyudev.Context()
|
||||
monitor = pyudev.Monitor.from_netlink(context)
|
||||
monitor.filter_by('block')
|
||||
observer = pyudev.MonitorObserver(monitor, filter_event)
|
||||
observer.start()
|
||||
with observer_lock:
|
||||
global observer
|
||||
context = pyudev.Context()
|
||||
monitor = pyudev.Monitor.from_netlink(context)
|
||||
monitor.filter_by('block')
|
||||
observer = pyudev.MonitorObserver(monitor, filter_event)
|
||||
observer.start()
|
||||
|
||||
|
||||
def remove():
|
||||
global observer
|
||||
observer.stop()
|
||||
observer = None
|
||||
with observer_lock:
|
||||
global observer
|
||||
if observer:
|
||||
observer.stop()
|
||||
observer = None
|
||||
return True
|
||||
return False
|
||||
|
@@ -13,15 +13,13 @@ import inspect
|
||||
import ctypes
|
||||
import os
|
||||
import string
|
||||
import datetime
|
||||
|
||||
import dbus
|
||||
import dbus.service
|
||||
import dbus.mainloop.glib
|
||||
from lvmdbusd import cfg
|
||||
from gi.repository import GLib
|
||||
import threading
|
||||
|
||||
try:
|
||||
from . import cfg
|
||||
except SystemError:
|
||||
import cfg
|
||||
|
||||
STDOUT_TTY = os.isatty(sys.stdout.fileno())
|
||||
|
||||
@@ -149,17 +147,27 @@ def add_properties(xml, interface, props):
|
||||
:param props: Output from get_properties
|
||||
:return: updated XML string
|
||||
"""
|
||||
root = Et.fromstring(xml)
|
||||
|
||||
if props:
|
||||
root = Et.fromstring(xml)
|
||||
interface_element = None
|
||||
|
||||
# Check to see if interface is present
|
||||
for c in root:
|
||||
# print c.attrib['name']
|
||||
if c.attrib['name'] == interface:
|
||||
for p in props:
|
||||
temp = '<property type="%s" name="%s" access="%s"/>\n' % \
|
||||
(p['p_t'], p['p_name'], p['p_access'])
|
||||
c.append(Et.fromstring(temp))
|
||||
interface_element = c
|
||||
break
|
||||
|
||||
# Interface is not present, lets create it so we have something to
|
||||
# attach the properties too
|
||||
if interface_element is None:
|
||||
interface_element = Et.Element("interface", name=interface)
|
||||
root.append(interface_element)
|
||||
|
||||
# Add the properties
|
||||
for p in props:
|
||||
temp = '<property type="%s" name="%s" access="%s"/>\n' % \
|
||||
(p['p_t'], p['p_name'], p['p_access'])
|
||||
interface_element.append(Et.fromstring(temp))
|
||||
|
||||
return Et.tostring(root, encoding='utf8')
|
||||
return xml
|
||||
@@ -235,7 +243,7 @@ def parse_tags(tags):
|
||||
if len(tags):
|
||||
if ',' in tags:
|
||||
return tags.split(',')
|
||||
return sorted([tags])
|
||||
return dbus.Array(sorted([tags]), signature='s')
|
||||
return dbus.Array([], signature='s')
|
||||
|
||||
|
||||
@@ -243,7 +251,13 @@ def _common_log(msg, *attributes):
|
||||
cfg.stdout_lock.acquire()
|
||||
tid = ctypes.CDLL('libc.so.6').syscall(186)
|
||||
|
||||
msg = "%d:%d - %s" % (os.getpid(), tid, msg)
|
||||
if STDOUT_TTY:
|
||||
msg = "%s: %d:%d - %s" % \
|
||||
(datetime.datetime.now().strftime("%b %d %H:%M:%S.%f"),
|
||||
os.getpid(), tid, msg)
|
||||
|
||||
else:
|
||||
msg = "%d:%d - %s" % (os.getpid(), tid, msg)
|
||||
|
||||
if STDOUT_TTY and attributes:
|
||||
print(color(msg, *attributes))
|
||||
@@ -258,7 +272,7 @@ def _common_log(msg, *attributes):
|
||||
# @param msg Message to output to stdout
|
||||
# @return None
|
||||
def log_debug(msg, *attributes):
|
||||
if cfg.DEBUG:
|
||||
if cfg.args and cfg.args.debug:
|
||||
_common_log(msg, *attributes)
|
||||
|
||||
|
||||
@@ -482,3 +496,64 @@ def validate_tag(interface, tag):
|
||||
raise dbus.exceptions.DBusException(
|
||||
interface, 'tag (%s) contains invalid character, allowable set(%s)'
|
||||
% (tag, _ALLOWABLE_TAG_CH))
|
||||
|
||||
|
||||
# The methods below which start with mt_* are used to execute the desired code
|
||||
# on the the main thread of execution to alleviate any issues the dbus-python
|
||||
# library with regards to multi-threaded access. Essentially, we are trying to
|
||||
# ensure all dbus library interaction is done from the same thread!
|
||||
|
||||
|
||||
def _async_result(call_back, results):
|
||||
log_debug('Results = %s' % str(results))
|
||||
call_back(results)
|
||||
|
||||
# Return result in main thread
|
||||
def mt_async_result(call_back, results):
|
||||
GLib.idle_add(_async_result, call_back, results)
|
||||
|
||||
|
||||
# Run the supplied function and arguments on the main thread and wait for them
|
||||
# to complete while allowing the ability to get the return value too.
|
||||
#
|
||||
# Example:
|
||||
# result = MThreadRunner(foo, arg1, arg2).done()
|
||||
#
|
||||
class MThreadRunner(object):
|
||||
|
||||
@staticmethod
|
||||
def runner(obj):
|
||||
obj._run()
|
||||
with obj.cond:
|
||||
obj.function_complete = True
|
||||
obj.cond.notify_all()
|
||||
|
||||
def __init__(self, function, *args):
|
||||
self.f = function
|
||||
self.rc = None
|
||||
self.args = args
|
||||
self.function_complete = False
|
||||
self.cond = threading.Condition(threading.Lock())
|
||||
|
||||
def done(self):
|
||||
GLib.idle_add(MThreadRunner.runner, self)
|
||||
with self.cond:
|
||||
if not self.function_complete:
|
||||
self.cond.wait()
|
||||
return self.rc
|
||||
|
||||
def _run(self):
|
||||
if len(self.args):
|
||||
self.rc = self.f(*self.args)
|
||||
else:
|
||||
self.rc = self.f()
|
||||
|
||||
|
||||
def _remove_objects(dbus_objects_rm):
|
||||
for o in dbus_objects_rm:
|
||||
cfg.om.remove_object(o, emit_signal=True)
|
||||
|
||||
|
||||
# Remove dbus objects from main thread
|
||||
def mt_remove_dbus_objects(objs):
|
||||
MThreadRunner(_remove_objects, objs).done()
|
||||
|
@@ -19,7 +19,8 @@ from .request import RequestEntry
|
||||
from .loader import common
|
||||
from .state import State
|
||||
from . import background
|
||||
from .utils import round_size
|
||||
from .utils import round_size, mt_remove_dbus_objects
|
||||
from .job import JobState
|
||||
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
@@ -66,7 +67,7 @@ class VgState(State):
|
||||
|
||||
gen = utils.lv_object_path_method(lv_name, meta)
|
||||
|
||||
lv_path = cfg.om.get_object_path_by_lvm_id(
|
||||
lv_path = cfg.om.get_object_path_by_uuid_lvm_id(
|
||||
lv_uuid, full_name, gen)
|
||||
rc.append(lv_path)
|
||||
return dbus.Array(rc, signature='o')
|
||||
@@ -75,9 +76,9 @@ class VgState(State):
|
||||
rc = []
|
||||
for p in cfg.db.pvs_in_vg(self.Uuid):
|
||||
(pv_name, pv_uuid) = p
|
||||
rc.append(cfg.om.get_object_path_by_lvm_id(
|
||||
rc.append(cfg.om.get_object_path_by_uuid_lvm_id(
|
||||
pv_uuid, pv_name, pv_obj_path_generate))
|
||||
return dbus.Array(rc, signature='o')
|
||||
return rc
|
||||
|
||||
def __init__(self, Uuid, Name, Fmt,
|
||||
SizeBytes, FreeBytes, SysId, ExtentSizeBytes,
|
||||
@@ -90,7 +91,7 @@ class VgState(State):
|
||||
|
||||
def create_dbus_object(self, path):
|
||||
if not path:
|
||||
path = cfg.om.get_object_path_by_lvm_id(
|
||||
path = cfg.om.get_object_path_by_uuid_lvm_id(
|
||||
self.Uuid, self.Name, vg_obj_path_generate)
|
||||
return Vg(path, self)
|
||||
|
||||
@@ -144,13 +145,8 @@ class Vg(AutomatedProperties):
|
||||
|
||||
@staticmethod
|
||||
def fetch_new_lv(vg_name, lv_name):
|
||||
full_name = "%s/%s" % (vg_name, lv_name)
|
||||
|
||||
cfg.load()
|
||||
l = cfg.om.get_object_by_lvm_id(full_name)
|
||||
created_lv = l.dbus_object_path()
|
||||
|
||||
return created_lv
|
||||
return cfg.om.get_object_path_by_lvm_id("%s/%s" % (vg_name, lv_name))
|
||||
|
||||
@staticmethod
|
||||
def _rename(uuid, vg_name, new_name, rename_options):
|
||||
@@ -195,14 +191,7 @@ class Vg(AutomatedProperties):
|
||||
rc, out, err = cmdhandler.vg_remove(vg_name, remove_options)
|
||||
|
||||
if rc == 0:
|
||||
# Remove the VG
|
||||
cfg.om.remove_object(dbo, True)
|
||||
|
||||
# If an LV has hidden LVs, things can get quite involved,
|
||||
# especially if it's the last thin pool to get removed, so
|
||||
# lets refresh all
|
||||
cfg.load()
|
||||
|
||||
else:
|
||||
# Need to work on error handling, need consistent
|
||||
raise dbus.exceptions.DBusException(
|
||||
@@ -357,12 +346,20 @@ class Vg(AutomatedProperties):
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='o(tt)a(ott)ia{sv}',
|
||||
out_signature='o')
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Move(self, pv_src_obj, pv_source_range, pv_dests_and_ranges,
|
||||
tmo, move_options):
|
||||
return background.move(
|
||||
VG_INTERFACE, None, pv_src_obj, pv_source_range,
|
||||
pv_dests_and_ranges, move_options, tmo)
|
||||
tmo, move_options, cb, cbe):
|
||||
|
||||
job_state = JobState()
|
||||
|
||||
r = RequestEntry(
|
||||
tmo, background.move,
|
||||
(VG_INTERFACE, None, pv_src_obj, pv_source_range,
|
||||
pv_dests_and_ranges, move_options, job_state), cb, cbe, False,
|
||||
job_state)
|
||||
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _lv_create(uuid, vg_name, name, size_bytes, pv_dests_and_ranges,
|
||||
@@ -601,9 +598,7 @@ class Vg(AutomatedProperties):
|
||||
rc, out, err = create_method(
|
||||
md.lv_full_name(), data.lv_full_name(), create_options)
|
||||
if rc == 0:
|
||||
cfg.om.remove_object(md, emit_signal=True)
|
||||
cfg.om.remove_object(data, emit_signal=True)
|
||||
|
||||
mt_remove_dbus_objects((md, data))
|
||||
cache_pool_lv = Vg.fetch_new_lv(vg_name, new_name)
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
@@ -836,9 +831,7 @@ class Vg(AutomatedProperties):
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
def _attribute(self, pos, ch):
|
||||
if self.state.attr[pos] == ch:
|
||||
return True
|
||||
return False
|
||||
return dbus.Boolean(self.state.attr[pos] == ch)
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
@@ -904,11 +897,11 @@ class Vg(AutomatedProperties):
|
||||
|
||||
@property
|
||||
def Pvs(self):
|
||||
return self.state.Pvs
|
||||
return dbus.Array(self.state.Pvs, signature='o')
|
||||
|
||||
@property
|
||||
def Lvs(self):
|
||||
return self.state.Lvs
|
||||
return dbus.Array(self.state.Lvs, signature='o')
|
||||
|
||||
@property
|
||||
def lvm_id(self):
|
||||
|
@@ -55,24 +55,32 @@ int main(int argc, char **argv)
|
||||
if (!strcmp(cmd, "dump")) {
|
||||
reply = daemon_send_simple(h, "dump",
|
||||
"token = %s", "skip",
|
||||
"pid = " FMTd64, (int64_t)getpid(),
|
||||
"cmd = %s", "lvmetactl",
|
||||
NULL);
|
||||
printf("%s\n", reply.buffer.mem);
|
||||
|
||||
} else if (!strcmp(cmd, "pv_list")) {
|
||||
reply = daemon_send_simple(h, "pv_list",
|
||||
"token = %s", "skip",
|
||||
"pid = " FMTd64, (int64_t)getpid(),
|
||||
"cmd = %s", "lvmetactl",
|
||||
NULL);
|
||||
printf("%s\n", reply.buffer.mem);
|
||||
|
||||
} else if (!strcmp(cmd, "vg_list")) {
|
||||
reply = daemon_send_simple(h, "vg_list",
|
||||
"token = %s", "skip",
|
||||
"pid = " FMTd64, (int64_t)getpid(),
|
||||
"cmd = %s", "lvmetactl",
|
||||
NULL);
|
||||
printf("%s\n", reply.buffer.mem);
|
||||
|
||||
} else if (!strcmp(cmd, "get_global_info")) {
|
||||
reply = daemon_send_simple(h, "get_global_info",
|
||||
"token = %s", "skip",
|
||||
"pid = " FMTd64, (int64_t)getpid(),
|
||||
"cmd = %s", "lvmetactl",
|
||||
NULL);
|
||||
printf("%s\n", reply.buffer.mem);
|
||||
|
||||
@@ -86,6 +94,8 @@ int main(int argc, char **argv)
|
||||
reply = daemon_send_simple(h, "set_global_info",
|
||||
"global_invalid = " FMTd64, (int64_t) val,
|
||||
"token = %s", "skip",
|
||||
"pid = " FMTd64, (int64_t)getpid(),
|
||||
"cmd = %s", "lvmetactl",
|
||||
NULL);
|
||||
print_reply(reply);
|
||||
|
||||
@@ -100,6 +110,8 @@ int main(int argc, char **argv)
|
||||
"global_disable = " FMTd64, (int64_t) val,
|
||||
"disable_reason = %s", LVMETAD_DISABLE_REASON_DIRECT,
|
||||
"token = %s", "skip",
|
||||
"pid = " FMTd64, (int64_t)getpid(),
|
||||
"cmd = %s", "lvmetactl",
|
||||
NULL);
|
||||
print_reply(reply);
|
||||
|
||||
@@ -123,18 +135,24 @@ int main(int argc, char **argv)
|
||||
"name = %s", name,
|
||||
"version = " FMTd64, (int64_t) ver,
|
||||
"token = %s", "skip",
|
||||
"pid = " FMTd64, (int64_t)getpid(),
|
||||
"cmd = %s", "lvmetactl",
|
||||
NULL);
|
||||
} else if (uuid) {
|
||||
reply = daemon_send_simple(h, "set_vg_info",
|
||||
"uuid = %s", uuid,
|
||||
"version = " FMTd64, (int64_t) ver,
|
||||
"token = %s", "skip",
|
||||
"pid = " FMTd64, (int64_t)getpid(),
|
||||
"cmd = %s", "lvmetactl",
|
||||
NULL);
|
||||
} else if (name) {
|
||||
reply = daemon_send_simple(h, "set_vg_info",
|
||||
"name = %s", name,
|
||||
"version = " FMTd64, (int64_t) ver,
|
||||
"token = %s", "skip",
|
||||
"pid = " FMTd64, (int64_t)getpid(),
|
||||
"cmd = %s", "lvmetactl",
|
||||
NULL);
|
||||
} else {
|
||||
printf("name or uuid required\n");
|
||||
@@ -153,6 +171,8 @@ int main(int argc, char **argv)
|
||||
reply = daemon_send_simple(h, "vg_lookup",
|
||||
"name = %s", name,
|
||||
"token = %s", "skip",
|
||||
"pid = " FMTd64, (int64_t)getpid(),
|
||||
"cmd = %s", "lvmetactl",
|
||||
NULL);
|
||||
printf("%s\n", reply.buffer.mem);
|
||||
|
||||
@@ -166,6 +186,8 @@ int main(int argc, char **argv)
|
||||
reply = daemon_send_simple(h, "vg_lookup",
|
||||
"uuid = %s", uuid,
|
||||
"token = %s", "skip",
|
||||
"pid = " FMTd64, (int64_t)getpid(),
|
||||
"cmd = %s", "lvmetactl",
|
||||
NULL);
|
||||
printf("%s\n", reply.buffer.mem);
|
||||
|
||||
@@ -182,6 +204,8 @@ int main(int argc, char **argv)
|
||||
reply = daemon_send_simple(h, "vg_lookup",
|
||||
"uuid = %s", uuid,
|
||||
"token = %s", "skip",
|
||||
"pid = " FMTd64, (int64_t)getpid(),
|
||||
"cmd = %s", "lvmetactl",
|
||||
NULL);
|
||||
/* printf("%s\n", reply.buffer.mem); */
|
||||
|
||||
@@ -208,6 +232,8 @@ int main(int argc, char **argv)
|
||||
reply = daemon_send_simple(h, "pv_lookup",
|
||||
"uuid = %s", uuid,
|
||||
"token = %s", "skip",
|
||||
"pid = " FMTd64, (int64_t)getpid(),
|
||||
"cmd = %s", "lvmetactl",
|
||||
NULL);
|
||||
printf("%s\n", reply.buffer.mem);
|
||||
|
||||
|
@@ -19,9 +19,12 @@
|
||||
|
||||
#define LVMETAD_SOCKET DEFAULT_RUN_DIR "/lvmetad.socket"
|
||||
|
||||
#define LVMETAD_TOKEN_UPDATE_IN_PROGRESS "update in progress"
|
||||
|
||||
#define LVMETAD_DISABLE_REASON_DIRECT "DIRECT"
|
||||
#define LVMETAD_DISABLE_REASON_LVM1 "LVM1"
|
||||
#define LVMETAD_DISABLE_REASON_DUPLICATES "DUPLICATES"
|
||||
#define LVMETAD_DISABLE_REASON_VGRESTORE "VGRESTORE"
|
||||
|
||||
struct volume_group;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -75,7 +75,10 @@ int scan(daemon_handle h, char *fn) {
|
||||
}
|
||||
|
||||
char uuid[64];
|
||||
id_write_format(dev->pvid, uuid, 64);
|
||||
if (!id_write_format(dev->pvid, uuid, 64)) {
|
||||
fprintf(stderr, "[C] Failed to format PV UUID for %s", dev_name(dev));
|
||||
return;
|
||||
}
|
||||
fprintf(stderr, "[C] found PV: %s\n", uuid);
|
||||
struct lvmcache_info *info = (struct lvmcache_info *) label->info;
|
||||
struct physical_volume pv = { 0, };
|
||||
|
@@ -77,7 +77,7 @@ static void save_client_info(char *line)
|
||||
uint32_t client_id = 0;
|
||||
char name[MAX_NAME+1] = { 0 };
|
||||
|
||||
sscanf(line, "info=client pid=%u fd=%d pi=%d id=%u name=%s",
|
||||
(void) sscanf(line, "info=client pid=%u fd=%d pi=%d id=%u name=%s",
|
||||
&pid, &fd, &pi, &client_id, name);
|
||||
|
||||
clients[num_clients].client_id = client_id;
|
||||
@@ -110,7 +110,7 @@ static void format_info_ls(char *line)
|
||||
char lock_args[MAX_ARGS+1] = { 0 };
|
||||
char lock_type[MAX_NAME+1] = { 0 };
|
||||
|
||||
sscanf(line, "info=ls ls_name=%s vg_name=%s vg_uuid=%s vg_sysid=%s vg_args=%s lm_type=%s",
|
||||
(void) sscanf(line, "info=ls ls_name=%s vg_name=%s vg_uuid=%s vg_sysid=%s vg_args=%s lm_type=%s",
|
||||
ls_name, vg_name, vg_uuid, vg_sysid, lock_args, lock_type);
|
||||
|
||||
if (!first_ls)
|
||||
@@ -131,7 +131,7 @@ static void format_info_ls_action(char *line)
|
||||
uint32_t pid = 0;
|
||||
char cl_name[MAX_NAME+1] = { 0 };
|
||||
|
||||
sscanf(line, "info=ls_action client_id=%u %s %s op=%s",
|
||||
(void) sscanf(line, "info=ls_action client_id=%u %s %s op=%s",
|
||||
&client_id, flags, version, op);
|
||||
|
||||
find_client_info(client_id, &pid, cl_name);
|
||||
@@ -147,7 +147,7 @@ static void format_info_r(char *line, char *r_name_out, char *r_type_out)
|
||||
char sh_count[MAX_NAME+1] = { 0 };
|
||||
uint32_t ver = 0;
|
||||
|
||||
sscanf(line, "info=r name=%s type=%s mode=%s %s version=%u",
|
||||
(void) sscanf(line, "info=r name=%s type=%s mode=%s %s version=%u",
|
||||
r_name, r_type, mode, sh_count, &ver);
|
||||
|
||||
strcpy(r_name_out, r_name);
|
||||
@@ -185,7 +185,7 @@ static void format_info_lk(char *line, char *r_name, char *r_type)
|
||||
return;
|
||||
}
|
||||
|
||||
sscanf(line, "info=lk mode=%s version=%u %s client_id=%u",
|
||||
(void) sscanf(line, "info=lk mode=%s version=%u %s client_id=%u",
|
||||
mode, &ver, flags, &client_id);
|
||||
|
||||
find_client_info(client_id, &pid, cl_name);
|
||||
@@ -221,7 +221,7 @@ static void format_info_r_action(char *line, char *r_name, char *r_type)
|
||||
return;
|
||||
}
|
||||
|
||||
sscanf(line, "info=r_action client_id=%u %s %s op=%s rt=%s mode=%s %s %s %s",
|
||||
(void) sscanf(line, "info=r_action client_id=%u %s %s op=%s rt=%s mode=%s %s %s %s",
|
||||
&client_id, flags, version, op, rt, mode, lm, result, lm_rv);
|
||||
|
||||
find_client_info(client_id, &pid, cl_name);
|
||||
|
@@ -1013,6 +1013,7 @@ static daemon_reply send_lvmetad(const char *id, ...)
|
||||
daemon_reply reply;
|
||||
va_list ap;
|
||||
int retries = 0;
|
||||
int err;
|
||||
|
||||
va_start(ap, id);
|
||||
|
||||
@@ -1022,20 +1023,28 @@ static daemon_reply send_lvmetad(const char *id, ...)
|
||||
*/
|
||||
pthread_mutex_lock(&lvmetad_mutex);
|
||||
retry:
|
||||
if (!lvmetad_connected) {
|
||||
lvmetad_handle = lvmetad_open(NULL);
|
||||
if (lvmetad_handle.error || lvmetad_handle.socket_fd < 0) {
|
||||
err = lvmetad_handle.error ?: lvmetad_handle.socket_fd;
|
||||
pthread_mutex_unlock(&lvmetad_mutex);
|
||||
log_error("lvmetad_open reconnect error %d", err);
|
||||
memset(&reply, 0, sizeof(reply));
|
||||
reply.error = err;
|
||||
va_end(ap);
|
||||
return reply;
|
||||
} else {
|
||||
log_debug("lvmetad reconnected");
|
||||
lvmetad_connected = 1;
|
||||
}
|
||||
}
|
||||
|
||||
reply = daemon_send_simple_v(lvmetad_handle, id, ap);
|
||||
|
||||
/* lvmetad may have been restarted */
|
||||
if ((reply.error == ECONNRESET) && (retries < 2)) {
|
||||
daemon_close(lvmetad_handle);
|
||||
lvmetad_connected = 0;
|
||||
|
||||
lvmetad_handle = lvmetad_open(NULL);
|
||||
if (lvmetad_handle.error || lvmetad_handle.socket_fd < 0) {
|
||||
log_error("lvmetad_open reconnect error %d", lvmetad_handle.error);
|
||||
} else {
|
||||
log_debug("lvmetad reconnected");
|
||||
lvmetad_connected = 1;
|
||||
}
|
||||
retries++;
|
||||
goto retry;
|
||||
}
|
||||
@@ -1240,10 +1249,6 @@ static int res_lock(struct lockspace *ls, struct resource *r, struct action *act
|
||||
rv = -EREMOVED;
|
||||
}
|
||||
|
||||
if (!lvmetad_connected && inval_meta)
|
||||
log_debug("S %s R %s res_lock no lvmetad connection to invalidate",
|
||||
ls->name, r->name);
|
||||
|
||||
/*
|
||||
* r is vglk: tell lvmetad to set the vg invalid
|
||||
* flag, and provide the new r_version. If lvmetad finds
|
||||
@@ -1259,7 +1264,7 @@ static int res_lock(struct lockspace *ls, struct resource *r, struct action *act
|
||||
* caches, and tell lvmetad to set global invalid to 0.
|
||||
*/
|
||||
|
||||
if (lvmetad_connected && inval_meta && (r->type == LD_RT_VG)) {
|
||||
if (inval_meta && (r->type == LD_RT_VG)) {
|
||||
daemon_reply reply;
|
||||
char *uuid;
|
||||
|
||||
@@ -1283,7 +1288,7 @@ static int res_lock(struct lockspace *ls, struct resource *r, struct action *act
|
||||
daemon_reply_destroy(reply);
|
||||
}
|
||||
|
||||
if (lvmetad_connected && inval_meta && (r->type == LD_RT_GL)) {
|
||||
if (inval_meta && (r->type == LD_RT_GL)) {
|
||||
daemon_reply reply;
|
||||
|
||||
log_debug("S %s R %s res_lock set lvmetad global invalid",
|
||||
@@ -1649,6 +1654,9 @@ static int res_able(struct lockspace *ls, struct resource *r,
|
||||
}
|
||||
|
||||
rv = lm_able_gl_sanlock(ls, act->op == LD_OP_ENABLE);
|
||||
|
||||
if (!rv && (act->op == LD_OP_ENABLE))
|
||||
gl_vg_removed = 0;
|
||||
out:
|
||||
return rv;
|
||||
}
|
||||
@@ -2622,8 +2630,10 @@ out_act:
|
||||
|
||||
if (free_vg && ls->sanlock_gl_enabled && act_op_free) {
|
||||
pthread_mutex_lock(&lockspaces_mutex);
|
||||
if (other_sanlock_vgs_exist(ls))
|
||||
if (other_sanlock_vgs_exist(ls)) {
|
||||
act_op_free->flags |= LD_AF_WARN_GL_REMOVED;
|
||||
gl_vg_removed = 1;
|
||||
}
|
||||
pthread_mutex_unlock(&lockspaces_mutex);
|
||||
}
|
||||
|
||||
@@ -3663,7 +3673,7 @@ static int client_send_result(struct client *cl, struct action *act)
|
||||
if (act->flags & LD_AF_DUP_GL_LS)
|
||||
strcat(result_flags, "DUP_GL_LS,");
|
||||
|
||||
if (act->flags & LD_AF_WARN_GL_REMOVED)
|
||||
if ((act->flags & LD_AF_WARN_GL_REMOVED) || gl_vg_removed)
|
||||
strcat(result_flags, "WARN_GL_REMOVED,");
|
||||
|
||||
if (act->op == LD_OP_INIT) {
|
||||
@@ -4421,7 +4431,7 @@ static void client_recv_action(struct client *cl)
|
||||
return;
|
||||
}
|
||||
|
||||
req.cft = dm_config_from_string(req.buffer.mem);
|
||||
req.cft = config_tree_from_string_without_dup_node_check(req.buffer.mem);
|
||||
if (!req.cft) {
|
||||
log_error("client recv %u config_from_string error", cl->id);
|
||||
buffer_destroy(&req.buffer);
|
||||
|
@@ -320,6 +320,7 @@ static inline int list_empty(const struct list_head *head)
|
||||
EXTERN int gl_type_static;
|
||||
EXTERN int gl_use_dlm;
|
||||
EXTERN int gl_use_sanlock;
|
||||
EXTERN int gl_vg_removed;
|
||||
EXTERN char gl_lsname_dlm[MAX_NAME+1];
|
||||
EXTERN char gl_lsname_sanlock[MAX_NAME+1];
|
||||
EXTERN int global_dlm_lockspace_exists;
|
||||
|
@@ -276,8 +276,8 @@ static int read_host_id_file(void)
|
||||
*sep = '\0';
|
||||
memset(key_str, 0, sizeof(key_str));
|
||||
memset(val_str, 0, sizeof(val_str));
|
||||
sscanf(key, "%s", key_str);
|
||||
sscanf(val, "%s", val_str);
|
||||
(void) sscanf(key, "%s", key_str);
|
||||
(void) sscanf(val, "%s", val_str);
|
||||
|
||||
if (!strcmp(key_str, "host_id")) {
|
||||
host_id = atoi(val_str);
|
||||
|
@@ -31,7 +31,7 @@ const char *polling_op(enum poll_type type)
|
||||
|
||||
static int add_to_cmd_arr(const char ***cmdargv, const char *str, unsigned *ind)
|
||||
{
|
||||
const char **newargv = *cmdargv;
|
||||
const char **newargv;
|
||||
|
||||
if (*ind && !(*ind % MIN_ARGV_SIZE)) {
|
||||
newargv = dm_realloc(*cmdargv, (*ind / MIN_ARGV_SIZE + 1) * MIN_ARGV_SIZE * sizeof(char *));
|
||||
|
@@ -11,7 +11,7 @@ Every bio that is mapped by the target is referred to the policy.
|
||||
The policy can return a simple HIT or MISS or issue a migration.
|
||||
|
||||
Currently there's no way for the policy to issue background work,
|
||||
e.g. to start writing back dirty blocks that are going to be evicte
|
||||
e.g. to start writing back dirty blocks that are going to be evicted
|
||||
soon.
|
||||
|
||||
Because we map bios, rather than requests it's easy for the policy
|
||||
@@ -25,53 +25,77 @@ trying to see when the io scheduler has let the ios run.
|
||||
Overview of supplied cache replacement policies
|
||||
===============================================
|
||||
|
||||
multiqueue
|
||||
----------
|
||||
multiqueue (mq)
|
||||
---------------
|
||||
|
||||
This policy is the default.
|
||||
This policy is now an alias for smq (see below).
|
||||
|
||||
The multiqueue policy has three sets of 16 queues: one set for entries
|
||||
waiting for the cache and another two for those in the cache (a set for
|
||||
clean entries and a set for dirty entries).
|
||||
The following tunables are accepted, but have no effect:
|
||||
|
||||
Cache entries in the queues are aged based on logical time. Entry into
|
||||
the cache is based on variable thresholds and queue selection is based
|
||||
on hit count on entry. The policy aims to take different cache miss
|
||||
costs into account and to adjust to varying load patterns automatically.
|
||||
|
||||
Message and constructor argument pairs are:
|
||||
'sequential_threshold <#nr_sequential_ios>'
|
||||
'random_threshold <#nr_random_ios>'
|
||||
'read_promote_adjustment <value>'
|
||||
'write_promote_adjustment <value>'
|
||||
'discard_promote_adjustment <value>'
|
||||
|
||||
The sequential threshold indicates the number of contiguous I/Os
|
||||
required before a stream is treated as sequential. Once a stream is
|
||||
considered sequential it will bypass the cache. The random threshold
|
||||
is the number of intervening non-contiguous I/Os that must be seen
|
||||
before the stream is treated as random again.
|
||||
Stochastic multiqueue (smq)
|
||||
---------------------------
|
||||
|
||||
The sequential and random thresholds default to 512 and 4 respectively.
|
||||
This policy is the default.
|
||||
|
||||
Large, sequential I/Os are probably better left on the origin device
|
||||
since spindles tend to have good sequential I/O bandwidth. The
|
||||
io_tracker counts contiguous I/Os to try to spot when the I/O is in one
|
||||
of these sequential modes. But there are use-cases for wanting to
|
||||
promote sequential blocks to the cache (e.g. fast application startup).
|
||||
If sequential threshold is set to 0 the sequential I/O detection is
|
||||
disabled and sequential I/O will no longer implicitly bypass the cache.
|
||||
Setting the random threshold to 0 does _not_ disable the random I/O
|
||||
stream detection.
|
||||
The stochastic multi-queue (smq) policy addresses some of the problems
|
||||
with the multiqueue (mq) policy.
|
||||
|
||||
Internally the mq policy determines a promotion threshold. If the hit
|
||||
count of a block not in the cache goes above this threshold it gets
|
||||
promoted to the cache. The read, write and discard promote adjustment
|
||||
tunables allow you to tweak the promotion threshold by adding a small
|
||||
value based on the io type. They default to 4, 8 and 1 respectively.
|
||||
If you're trying to quickly warm a new cache device you may wish to
|
||||
reduce these to encourage promotion. Remember to switch them back to
|
||||
their defaults after the cache fills though.
|
||||
The smq policy (vs mq) offers the promise of less memory utilization,
|
||||
improved performance and increased adaptability in the face of changing
|
||||
workloads. smq also does not have any cumbersome tuning knobs.
|
||||
|
||||
Users may switch from "mq" to "smq" simply by appropriately reloading a
|
||||
DM table that is using the cache target. Doing so will cause all of the
|
||||
mq policy's hints to be dropped. Also, performance of the cache may
|
||||
degrade slightly until smq recalculates the origin device's hotspots
|
||||
that should be cached.
|
||||
|
||||
Memory usage:
|
||||
The mq policy used a lot of memory; 88 bytes per cache block on a 64
|
||||
bit machine.
|
||||
|
||||
smq uses 28bit indexes to implement it's data structures rather than
|
||||
pointers. It avoids storing an explicit hit count for each block. It
|
||||
has a 'hotspot' queue, rather than a pre-cache, which uses a quarter of
|
||||
the entries (each hotspot block covers a larger area than a single
|
||||
cache block).
|
||||
|
||||
All this means smq uses ~25bytes per cache block. Still a lot of
|
||||
memory, but a substantial improvement nontheless.
|
||||
|
||||
Level balancing:
|
||||
mq placed entries in different levels of the multiqueue structures
|
||||
based on their hit count (~ln(hit count)). This meant the bottom
|
||||
levels generally had the most entries, and the top ones had very
|
||||
few. Having unbalanced levels like this reduced the efficacy of the
|
||||
multiqueue.
|
||||
|
||||
smq does not maintain a hit count, instead it swaps hit entries with
|
||||
the least recently used entry from the level above. The overall
|
||||
ordering being a side effect of this stochastic process. With this
|
||||
scheme we can decide how many entries occupy each multiqueue level,
|
||||
resulting in better promotion/demotion decisions.
|
||||
|
||||
Adaptability:
|
||||
The mq policy maintained a hit count for each cache block. For a
|
||||
different block to get promoted to the cache it's hit count has to
|
||||
exceed the lowest currently in the cache. This meant it could take a
|
||||
long time for the cache to adapt between varying IO patterns.
|
||||
|
||||
smq doesn't maintain hit counts, so a lot of this problem just goes
|
||||
away. In addition it tracks performance of the hotspot queue, which
|
||||
is used to decide which blocks to promote. If the hotspot queue is
|
||||
performing badly then it starts moving entries more quickly between
|
||||
levels. This lets it adapt to new IO patterns very quickly.
|
||||
|
||||
Performance:
|
||||
Testing smq shows substantially better performance than mq.
|
||||
|
||||
cleaner
|
||||
-------
|
||||
|
@@ -221,6 +221,7 @@ Status
|
||||
<#read hits> <#read misses> <#write hits> <#write misses>
|
||||
<#demotions> <#promotions> <#dirty> <#features> <features>*
|
||||
<#core args> <core args>* <policy name> <#policy args> <policy args>*
|
||||
<cache metadata mode>
|
||||
|
||||
metadata block size : Fixed block size for each metadata block in
|
||||
sectors
|
||||
@@ -251,8 +252,18 @@ core args : Key/value pairs for tuning the core
|
||||
e.g. migration_threshold
|
||||
policy name : Name of the policy
|
||||
#policy args : Number of policy arguments to follow (must be even)
|
||||
policy args : Key/value pairs
|
||||
e.g. sequential_threshold
|
||||
policy args : Key/value pairs e.g. sequential_threshold
|
||||
cache metadata mode : ro if read-only, rw if read-write
|
||||
In serious cases where even a read-only mode is deemed unsafe
|
||||
no further I/O will be permitted and the status will just
|
||||
contain the string 'Fail'. The userspace recovery tools
|
||||
should then be used.
|
||||
needs_check : 'needs_check' if set, '-' if not set
|
||||
A metadata operation has failed, resulting in the needs_check
|
||||
flag being set in the metadata's superblock. The metadata
|
||||
device must be deactivated and checked/repaired before the
|
||||
cache can be made fully operational again. '-' indicates
|
||||
needs_check is not set.
|
||||
|
||||
Messages
|
||||
--------
|
||||
|
@@ -8,6 +8,7 @@ Parameters:
|
||||
<device> <offset> <delay> [<write_device> <write_offset> <write_delay>]
|
||||
|
||||
With separate write parameters, the first set is only used for reads.
|
||||
Offsets are specified in sectors.
|
||||
Delays are specified in milliseconds.
|
||||
|
||||
Example scripts
|
||||
|
@@ -209,6 +209,37 @@ include:
|
||||
"repair" - Initiate a repair of the array.
|
||||
"reshape"- Currently unsupported (-EINVAL).
|
||||
|
||||
|
||||
Discard Support
|
||||
---------------
|
||||
The implementation of discard support among hardware vendors varies.
|
||||
When a block is discarded, some storage devices will return zeroes when
|
||||
the block is read. These devices set the 'discard_zeroes_data'
|
||||
attribute. Other devices will return random data. Confusingly, some
|
||||
devices that advertise 'discard_zeroes_data' will not reliably return
|
||||
zeroes when discarded blocks are read! Since RAID 4/5/6 uses blocks
|
||||
from a number of devices to calculate parity blocks and (for performance
|
||||
reasons) relies on 'discard_zeroes_data' being reliable, it is important
|
||||
that the devices be consistent. Blocks may be discarded in the middle
|
||||
of a RAID 4/5/6 stripe and if subsequent read results are not
|
||||
consistent, the parity blocks may be calculated differently at any time;
|
||||
making the parity blocks useless for redundancy. It is important to
|
||||
understand how your hardware behaves with discards if you are going to
|
||||
enable discards with RAID 4/5/6.
|
||||
|
||||
Since the behavior of storage devices is unreliable in this respect,
|
||||
even when reporting 'discard_zeroes_data', by default RAID 4/5/6
|
||||
discard support is disabled -- this ensures data integrity at the
|
||||
expense of losing some performance.
|
||||
|
||||
Storage devices that properly support 'discard_zeroes_data' are
|
||||
increasingly whitelisted in the kernel and can thus be trusted.
|
||||
|
||||
For trusted devices, the following dm-raid module parameter can be set
|
||||
to safely enable discard support for RAID 4/5/6:
|
||||
'devices_handle_discards_safely'
|
||||
|
||||
|
||||
Version History
|
||||
---------------
|
||||
1.0.0 Initial version. Support for RAID 4/5/6
|
||||
@@ -224,3 +255,5 @@ Version History
|
||||
New status (STATUSTYPE_INFO) fields: sync_action and mismatch_cnt.
|
||||
1.5.1 Add ability to restore transiently failed devices on resume.
|
||||
1.5.2 'mismatch_cnt' is zero unless [last_]sync_action is "check".
|
||||
1.6.0 Add discard support (and devices_handle_discard_safely module param).
|
||||
1.7.0 Add support for MD RAID0 mappings.
|
||||
|
@@ -41,9 +41,13 @@ useless and be disabled, returning errors. So it is important to monitor
|
||||
the amount of free space and expand the <COW device> before it fills up.
|
||||
|
||||
<persistent?> is P (Persistent) or N (Not persistent - will not survive
|
||||
after reboot).
|
||||
The difference is that for transient snapshots less metadata must be
|
||||
saved on disk - they can be kept in memory by the kernel.
|
||||
after reboot). O (Overflow) can be added as a persistent store option
|
||||
to allow userspace to advertise its support for seeing "Overflow" in the
|
||||
snapshot status. So supported store types are "P", "PO" and "N".
|
||||
|
||||
The difference between persistent and transient is with transient
|
||||
snapshots less metadata must be saved on disk - they can be kept in
|
||||
memory by the kernel.
|
||||
|
||||
|
||||
* snapshot-merge <origin> <COW device> <persistent> <chunksize>
|
||||
|
@@ -13,9 +13,14 @@ the range specified.
|
||||
The I/O statistics counters for each step-sized area of a region are
|
||||
in the same format as /sys/block/*/stat or /proc/diskstats (see:
|
||||
Documentation/iostats.txt). But two extra counters (12 and 13) are
|
||||
provided: total time spent reading and writing in milliseconds. All
|
||||
these counters may be accessed by sending the @stats_print message to
|
||||
the appropriate DM device via dmsetup.
|
||||
provided: total time spent reading and writing. When the histogram
|
||||
argument is used, the 14th parameter is reported that represents the
|
||||
histogram of latencies. All these counters may be accessed by sending
|
||||
the @stats_print message to the appropriate DM device via dmsetup.
|
||||
|
||||
The reported times are in milliseconds and the granularity depends on
|
||||
the kernel ticks. When the option precise_timestamps is used, the
|
||||
reported times are in nanoseconds.
|
||||
|
||||
Each region has a corresponding unique identifier, which we call a
|
||||
region_id, that is assigned when the region is created. The region_id
|
||||
@@ -33,7 +38,9 @@ memory is used by reading
|
||||
Messages
|
||||
========
|
||||
|
||||
@stats_create <range> <step> [<program_id> [<aux_data>]]
|
||||
@stats_create <range> <step>
|
||||
[<number_of_optional_arguments> <optional_arguments>...]
|
||||
[<program_id> [<aux_data>]]
|
||||
|
||||
Create a new region and return the region_id.
|
||||
|
||||
@@ -48,6 +55,29 @@ Messages
|
||||
"/<number_of_areas>" - the range is subdivided into the specified
|
||||
number of areas.
|
||||
|
||||
<number_of_optional_arguments>
|
||||
The number of optional arguments
|
||||
|
||||
<optional_arguments>
|
||||
The following optional arguments are supported
|
||||
precise_timestamps - use precise timer with nanosecond resolution
|
||||
instead of the "jiffies" variable. When this argument is
|
||||
used, the resulting times are in nanoseconds instead of
|
||||
milliseconds. Precise timestamps are a little bit slower
|
||||
to obtain than jiffies-based timestamps.
|
||||
histogram:n1,n2,n3,n4,... - collect histogram of latencies. The
|
||||
numbers n1, n2, etc are times that represent the boundaries
|
||||
of the histogram. If precise_timestamps is not used, the
|
||||
times are in milliseconds, otherwise they are in
|
||||
nanoseconds. For each range, the kernel will report the
|
||||
number of requests that completed within this range. For
|
||||
example, if we use "histogram:10,20,30", the kernel will
|
||||
report four numbers a:b:c:d. a is the number of requests
|
||||
that took 0-10 ms to complete, b is the number of requests
|
||||
that took 10-20 ms to complete, c is the number of requests
|
||||
that took 20-30 ms to complete and d is the number of
|
||||
requests that took more than 30 ms to complete.
|
||||
|
||||
<program_id>
|
||||
An optional parameter. A name that uniquely identifies
|
||||
the userspace owner of the range. This groups ranges together
|
||||
@@ -55,6 +85,9 @@ Messages
|
||||
created and ignore those created by others.
|
||||
The kernel returns this string back in the output of
|
||||
@stats_list message, but it doesn't use it for anything else.
|
||||
If we omit the number of optional arguments, program id must not
|
||||
be a number, otherwise it would be interpreted as the number of
|
||||
optional arguments.
|
||||
|
||||
<aux_data>
|
||||
An optional parameter. A word that provides auxiliary data
|
||||
@@ -88,6 +121,10 @@ Messages
|
||||
|
||||
Output format:
|
||||
<region_id>: <start_sector>+<length> <step> <program_id> <aux_data>
|
||||
precise_timestamps histogram:n1,n2,n3,...
|
||||
|
||||
The strings "precise_timestamps" and "histogram" are printed only
|
||||
if they were specified when creating the region.
|
||||
|
||||
@stats_print <region_id> [<starting_line> <number_of_lines>]
|
||||
|
||||
@@ -168,7 +205,7 @@ statistics on them:
|
||||
|
||||
dmsetup message vol 0 @stats_create - /100
|
||||
|
||||
Set the auxillary data string to "foo bar baz" (the escape for each
|
||||
Set the auxiliary data string to "foo bar baz" (the escape for each
|
||||
space must also be escaped, otherwise the shell will consume them):
|
||||
|
||||
dmsetup message vol 0 @stats_set_aux 0 foo\\ bar\\ baz
|
||||
|
@@ -296,7 +296,7 @@ ii) Status
|
||||
underlying device. When this is enabled when loading the table,
|
||||
it can get disabled if the underlying device doesn't support it.
|
||||
|
||||
ro|rw
|
||||
ro|rw|out_of_data_space
|
||||
If the pool encounters certain types of device failures it will
|
||||
drop into a read-only metadata mode in which no changes to
|
||||
the pool metadata (like allocating new blocks) are permitted.
|
||||
@@ -314,6 +314,13 @@ ii) Status
|
||||
module parameter can be used to change this timeout -- it
|
||||
defaults to 60 seconds but may be disabled using a value of 0.
|
||||
|
||||
needs_check
|
||||
A metadata operation has failed, resulting in the needs_check
|
||||
flag being set in the metadata's superblock. The metadata
|
||||
device must be deactivated and checked/repaired before the
|
||||
thin-pool can be made fully operational again. '-' indicates
|
||||
needs_check is not set.
|
||||
|
||||
iii) Messages
|
||||
|
||||
create_thin <dev id>
|
||||
|
@@ -18,11 +18,11 @@ Construction Parameters
|
||||
|
||||
0 is the original format used in the Chromium OS.
|
||||
The salt is appended when hashing, digests are stored continuously and
|
||||
the rest of the block is padded with zeros.
|
||||
the rest of the block is padded with zeroes.
|
||||
|
||||
1 is the current format that should be used for new devices.
|
||||
The salt is prepended when hashing and each digest is
|
||||
padded with zeros to the power of two.
|
||||
padded with zeroes to the power of two.
|
||||
|
||||
<dev>
|
||||
This is the device containing data, the integrity of which needs to be
|
||||
@@ -79,6 +79,37 @@ restart_on_corruption
|
||||
not compatible with ignore_corruption and requires user space support to
|
||||
avoid restart loops.
|
||||
|
||||
ignore_zero_blocks
|
||||
Do not verify blocks that are expected to contain zeroes and always return
|
||||
zeroes instead. This may be useful if the partition contains unused blocks
|
||||
that are not guaranteed to contain zeroes.
|
||||
|
||||
use_fec_from_device <fec_dev>
|
||||
Use forward error correction (FEC) to recover from corruption if hash
|
||||
verification fails. Use encoding data from the specified device. This
|
||||
may be the same device where data and hash blocks reside, in which case
|
||||
fec_start must be outside data and hash areas.
|
||||
|
||||
If the encoding data covers additional metadata, it must be accessible
|
||||
on the hash device after the hash blocks.
|
||||
|
||||
Note: block sizes for data and hash devices must match. Also, if the
|
||||
verity <dev> is encrypted the <fec_dev> should be too.
|
||||
|
||||
fec_roots <num>
|
||||
Number of generator roots. This equals to the number of parity bytes in
|
||||
the encoding data. For example, in RS(M, N) encoding, the number of roots
|
||||
is M-N.
|
||||
|
||||
fec_blocks <num>
|
||||
The number of encoding data blocks on the FEC device. The block size for
|
||||
the FEC device is <data_block_size>.
|
||||
|
||||
fec_start <offset>
|
||||
This is the offset, in <data_block_size> blocks, from the start of the
|
||||
FEC device to the beginning of the encoding data.
|
||||
|
||||
|
||||
Theory of operation
|
||||
===================
|
||||
|
||||
@@ -98,6 +129,11 @@ per-block basis. This allows for a lightweight hash computation on first read
|
||||
into the page cache. Block hashes are stored linearly, aligned to the nearest
|
||||
block size.
|
||||
|
||||
If forward error correction (FEC) support is enabled any recovery of
|
||||
corrupted data will be verified using the cryptographic hash of the
|
||||
corresponding data. This is why combining error correction with
|
||||
integrity checking is essential.
|
||||
|
||||
Hash Tree
|
||||
---------
|
||||
|
||||
|
@@ -50,6 +50,7 @@
|
||||
@top_srcdir@/lib/misc/lvm-file.h
|
||||
@top_srcdir@/lib/misc/lvm-flock.h
|
||||
@top_srcdir@/lib/misc/lvm-globals.h
|
||||
@top_srcdir@/lib/misc/lvm-maths.h
|
||||
@top_srcdir@/lib/misc/lvm-percent.h
|
||||
@top_srcdir@/lib/misc/lvm-signal.h
|
||||
@top_srcdir@/lib/misc/lvm-string.h
|
||||
|
@@ -264,9 +264,15 @@
|
||||
/* Define to 1 if you have the <limits.h> header file. */
|
||||
#undef HAVE_LIMITS_H
|
||||
|
||||
/* Define to 1 if you have the <linux/fiemap.h> header file. */
|
||||
#undef HAVE_LINUX_FIEMAP_H
|
||||
|
||||
/* Define to 1 if you have the <linux/fs.h> header file. */
|
||||
#undef HAVE_LINUX_FS_H
|
||||
|
||||
/* Define to 1 if you have the <linux/magic.h> header file. */
|
||||
#undef HAVE_LINUX_MAGIC_H
|
||||
|
||||
/* Define to 1 if you have the <locale.h> header file. */
|
||||
#undef HAVE_LOCALE_H
|
||||
|
||||
|
@@ -76,6 +76,7 @@ SOURCES =\
|
||||
filters/filter-partitioned.c \
|
||||
filters/filter-type.c \
|
||||
filters/filter-usable.c \
|
||||
filters/filter-internal.c \
|
||||
format_text/archive.c \
|
||||
format_text/archiver.c \
|
||||
format_text/export.c \
|
||||
@@ -111,6 +112,7 @@ SOURCES =\
|
||||
misc/lvm-file.c \
|
||||
misc/lvm-flock.c \
|
||||
misc/lvm-globals.c \
|
||||
misc/lvm-maths.c \
|
||||
misc/lvm-signal.c \
|
||||
misc/lvm-string.c \
|
||||
misc/lvm-wrappers.c \
|
||||
|
@@ -253,8 +253,9 @@ int lv_status(struct cmd_context *cmd, const struct lv_segment *lv_seg,
|
||||
int lv_cache_status(const struct logical_volume *cache_lv,
|
||||
struct lv_status_cache **status)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int lv_check_not_in_use(const struct logical_volume *lv)
|
||||
int lv_check_not_in_use(const struct logical_volume *lv, int error_if_used)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -287,18 +288,6 @@ int lv_raid_message(const struct logical_volume *lv, const char *msg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int lv_cache_block_info(struct logical_volume *lv,
|
||||
uint32_t *chunk_size, uint64_t *dirty_count,
|
||||
uint64_t *used_count, uint64_t *total_count)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int lv_cache_policy_info(struct logical_volume *lv,
|
||||
const char **policy_name, int *policy_argc,
|
||||
const char ***policy_argv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int lv_thin_pool_percent(const struct logical_volume *lv, int metadata,
|
||||
dm_percent_t *percent)
|
||||
{
|
||||
@@ -381,6 +370,11 @@ void activation_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
int raid4_is_supported(struct cmd_context *cmd, const struct segment_type *segtype)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lv_is_active(const struct logical_volume *lv)
|
||||
{
|
||||
return 0;
|
||||
@@ -632,25 +626,33 @@ int module_present(struct cmd_context *cmd, const char *target_name)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int target_present(struct cmd_context *cmd, const char *target_name,
|
||||
int use_modprobe)
|
||||
int target_present_version(struct cmd_context *cmd, const char *target_name,
|
||||
int use_modprobe,
|
||||
uint32_t *maj, uint32_t *min, uint32_t *patchlevel)
|
||||
{
|
||||
uint32_t maj, min, patchlevel;
|
||||
|
||||
if (!activation())
|
||||
if (!activation()) {
|
||||
log_error(INTERNAL_ERROR "Target present version called when activation is disabled.");
|
||||
return 0;
|
||||
|
||||
}
|
||||
#ifdef MODPROBE_CMD
|
||||
if (use_modprobe) {
|
||||
if (target_version(target_name, &maj, &min, &patchlevel))
|
||||
if (target_version(target_name, maj, min, patchlevel))
|
||||
return 1;
|
||||
|
||||
if (!module_present(cmd, target_name))
|
||||
return_0;
|
||||
}
|
||||
#endif
|
||||
return target_version(target_name, maj, min, patchlevel);
|
||||
}
|
||||
|
||||
return target_version(target_name, &maj, &min, &patchlevel);
|
||||
int target_present(struct cmd_context *cmd, const char *target_name,
|
||||
int use_modprobe)
|
||||
{
|
||||
uint32_t maj, min, patchlevel;
|
||||
|
||||
return target_present_version(cmd, target_name, use_modprobe,
|
||||
&maj, &min, &patchlevel);
|
||||
}
|
||||
|
||||
static int _lv_info(struct cmd_context *cmd, const struct logical_volume *lv,
|
||||
@@ -683,8 +685,36 @@ static int _lv_info(struct cmd_context *cmd, const struct logical_volume *lv,
|
||||
use_layer = 1;
|
||||
}
|
||||
|
||||
if (seg_status)
|
||||
if (seg_status) {
|
||||
/* TODO: for now it's mess with seg_status */
|
||||
seg_status->seg = seg;
|
||||
if (lv_is_merging_cow(lv)) {
|
||||
if (lv_has_target_type(cmd->mem, origin_from_cow(lv), NULL, TARGET_NAME_SNAPSHOT_MERGE)) {
|
||||
/*
|
||||
* When the snapshot-merge has not yet started, query COW LVs as is.
|
||||
* When merge is in progress, query merging origin LV instead.
|
||||
* COW volume is already mapped as error target in this case.
|
||||
*/
|
||||
lv = origin_from_cow(lv);
|
||||
seg_status->seg = first_seg(lv);
|
||||
log_debug_activation("Snapshot merge is in progress, querying status of %s instead.",
|
||||
display_lvname(lv));
|
||||
}
|
||||
} else if (!use_layer && lv_is_origin(lv) && !lv_is_external_origin(lv)) {
|
||||
/*
|
||||
* Query status for 'layered' (-real) device most of the time,
|
||||
* only when snapshot merge started, query its progress.
|
||||
* TODO: single LV may need couple status to be exposed at once....
|
||||
* but this needs more logical background
|
||||
*/
|
||||
if (!lv_is_merging_origin(lv) ||
|
||||
!lv_has_target_type(cmd->mem, origin_from_cow(lv), NULL, TARGET_NAME_SNAPSHOT_MERGE))
|
||||
use_layer = 1;
|
||||
} else if (lv_is_cow(lv)) {
|
||||
/* Hadle fictional lvm2 snapshot and query snapshotX volume */
|
||||
seg_status->seg = find_snapshot(lv);
|
||||
}
|
||||
}
|
||||
|
||||
if (!dev_manager_info(cmd, lv,
|
||||
(use_layer) ? lv_layer(lv) : NULL,
|
||||
@@ -1095,19 +1125,29 @@ int lv_cache_status(const struct logical_volume *cache_lv,
|
||||
struct dev_manager *dm;
|
||||
struct lv_segment *cache_seg;
|
||||
|
||||
if (lv_is_cache_pool(cache_lv) && !dm_list_empty(&cache_lv->segs_using_this_lv)) {
|
||||
if (!(cache_seg = get_only_segment_using_this_lv(cache_lv)))
|
||||
return_0;
|
||||
if (lv_is_cache_pool(cache_lv)) {
|
||||
if (dm_list_empty(&cache_lv->segs_using_this_lv) ||
|
||||
!(cache_seg = get_only_segment_using_this_lv(cache_lv))) {
|
||||
log_error(INTERNAL_ERROR "Cannot check status for unused cache pool %s.",
|
||||
display_lvname(cache_lv));
|
||||
return 0;
|
||||
}
|
||||
cache_lv = cache_seg->lv;
|
||||
}
|
||||
|
||||
if (lv_is_pending_delete(cache_lv))
|
||||
if (lv_is_pending_delete(cache_lv)) {
|
||||
log_error("Cannot check status for deleted cache volume %s.",
|
||||
display_lvname(cache_lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!lv_info(cache_lv->vg->cmd, cache_lv, 0, NULL, 0, 0))
|
||||
if (!lv_info(cache_lv->vg->cmd, cache_lv, 0, NULL, 0, 0)) {
|
||||
log_error("Cannot check status for locally inactive cache volume %s.",
|
||||
display_lvname(cache_lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_debug_activation("Checking cache status for LV %s.",
|
||||
log_debug_activation("Checking status for cache volume %s.",
|
||||
display_lvname(cache_lv));
|
||||
|
||||
if (!(dm = dev_manager_create(cache_lv->vg->cmd, cache_lv->vg->name, 1)))
|
||||
@@ -1194,7 +1234,7 @@ int lv_thin_pool_transaction_id(const struct logical_volume *lv,
|
||||
if (!(dm = dev_manager_create(lv->vg->cmd, lv->vg->name, 1)))
|
||||
return_0;
|
||||
|
||||
if (!(r = dev_manager_thin_pool_status(dm, lv, &status, 1)))
|
||||
if (!(r = dev_manager_thin_pool_status(dm, lv, &status, 0)))
|
||||
stack;
|
||||
else
|
||||
*transaction_id = status->transaction_id;
|
||||
@@ -1231,8 +1271,10 @@ static int _lv_active(struct cmd_context *cmd, const struct logical_volume *lv)
|
||||
struct lvinfo info;
|
||||
|
||||
if (!lv_info(cmd, lv, 0, &info, 0, 0)) {
|
||||
stack;
|
||||
return -1;
|
||||
log_debug("Cannot determine activation status of %s%s.",
|
||||
display_lvname(lv),
|
||||
activation() ? "" : " (no device driver)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return info.exists;
|
||||
@@ -1357,7 +1399,7 @@ int lvs_in_vg_opened(const struct volume_group *vg)
|
||||
if (lv_is_visible(lvl->lv))
|
||||
count += (_lv_open_count(vg->cmd, lvl->lv) > 0);
|
||||
|
||||
log_debug_activation("Counted %d open LVs in VG %s", count, vg->name);
|
||||
log_debug_activation("Counted %d open LVs in VG %s.", count, vg->name);
|
||||
|
||||
return count;
|
||||
}
|
||||
@@ -1419,9 +1461,9 @@ static int _lv_is_active(const struct logical_volume *lv,
|
||||
* Old users of this function will never be affected by this,
|
||||
* since they are only concerned about active vs. not active.
|
||||
* New users of this function who specifically ask for 'exclusive'
|
||||
* will be given an error message.
|
||||
* will be given a warning message.
|
||||
*/
|
||||
log_error("Unable to determine exclusivity of %s.", display_lvname(lv));
|
||||
log_warn("WARNING: Unable to determine exclusivity of %s.", display_lvname(lv));
|
||||
|
||||
e = 0;
|
||||
|
||||
@@ -1452,6 +1494,26 @@ out:
|
||||
return r || l;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if "raid4" @segtype is supported by kernel.
|
||||
*
|
||||
* if segment type is not raid4, return 1.
|
||||
*/
|
||||
int raid4_is_supported(struct cmd_context *cmd, const struct segment_type *segtype)
|
||||
{
|
||||
unsigned attrs;
|
||||
|
||||
if (segtype_is_raid4(segtype) &&
|
||||
(!segtype->ops->target_present ||
|
||||
!segtype->ops->target_present(cmd, NULL, &attrs) ||
|
||||
!(attrs & RAID_FEATURE_RAID4))) {
|
||||
log_error("RAID module does not support RAID4.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lv_is_active(const struct logical_volume *lv)
|
||||
{
|
||||
return _lv_is_active(lv, NULL, NULL, NULL);
|
||||
@@ -1511,7 +1573,7 @@ static struct dm_event_handler *_create_dm_event_handler(struct cmd_context *cmd
|
||||
if (dm_event_handler_set_dmeventd_path(dmevh, find_config_tree_str(cmd, dmeventd_executable_CFG, NULL)))
|
||||
goto_bad;
|
||||
|
||||
if (dm_event_handler_set_dso(dmevh, dso))
|
||||
if (dso && dm_event_handler_set_dso(dmevh, dso))
|
||||
goto_bad;
|
||||
|
||||
if (dm_event_handler_set_uuid(dmevh, dmuuid))
|
||||
@@ -1555,6 +1617,39 @@ static char *_build_target_uuid(struct cmd_context *cmd, const struct logical_vo
|
||||
return build_dm_uuid(cmd->mem, lv, layer);
|
||||
}
|
||||
|
||||
static int _device_registered_with_dmeventd(struct cmd_context *cmd, const struct logical_volume *lv, int *pending, const char **dso)
|
||||
{
|
||||
char *uuid;
|
||||
enum dm_event_mask evmask = 0;
|
||||
struct dm_event_handler *dmevh;
|
||||
|
||||
*pending = 0;
|
||||
|
||||
if (!(uuid = _build_target_uuid(cmd, lv)))
|
||||
return_0;
|
||||
|
||||
if (!(dmevh = _create_dm_event_handler(cmd, uuid, NULL, 0, DM_EVENT_ALL_ERRORS)))
|
||||
return_0;
|
||||
|
||||
if (dm_event_get_registered_device(dmevh, 0)) {
|
||||
dm_event_handler_destroy(dmevh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
evmask = dm_event_handler_get_event_mask(dmevh);
|
||||
if (evmask & DM_EVENT_REGISTRATION_PENDING) {
|
||||
*pending = 1;
|
||||
evmask &= ~DM_EVENT_REGISTRATION_PENDING;
|
||||
}
|
||||
|
||||
if (dso && (*dso = dm_event_handler_get_dso(dmevh)) && !(*dso = dm_pool_strdup(cmd->mem, *dso)))
|
||||
log_error("Failed to duplicate dso name.");
|
||||
|
||||
dm_event_handler_destroy(dmevh);
|
||||
|
||||
return evmask;
|
||||
}
|
||||
|
||||
int target_registered_with_dmeventd(struct cmd_context *cmd, const char *dso,
|
||||
const struct logical_volume *lv, int *pending)
|
||||
{
|
||||
@@ -1613,7 +1708,7 @@ int target_register_events(struct cmd_context *cmd, const char *dso, const struc
|
||||
if (!r)
|
||||
return_0;
|
||||
|
||||
log_info("%s %s for events", set ? "Monitored" : "Unmonitored", uuid);
|
||||
log_very_verbose("%s %s for events", set ? "Monitored" : "Unmonitored", uuid);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -1637,6 +1732,8 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume
|
||||
uint32_t s;
|
||||
static const struct lv_activate_opts zlaopts = { 0 };
|
||||
struct lvinfo info;
|
||||
const char *dso = NULL;
|
||||
int new_unmonitor;
|
||||
|
||||
if (!laopts)
|
||||
laopts = &zlaopts;
|
||||
@@ -1651,6 +1748,23 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume
|
||||
if (monitor && !dmeventd_monitor_mode())
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Activation of unused cache-pool activates metadata device as
|
||||
* a public LV for clearing purpose.
|
||||
* FIXME:
|
||||
* As VG lock is held across whole operation unmonitored volume
|
||||
* is usually OK since dmeventd couldn't do anything.
|
||||
* However in case command would have crashed, such LV is
|
||||
* left unmonitored and may potentially require dmeventd.
|
||||
*/
|
||||
if ((lv_is_cache_pool_data(lv) || lv_is_cache_pool_metadata(lv)) &&
|
||||
!lv_is_used_cache_pool((find_pool_seg(first_seg(lv))->lv))) {
|
||||
log_debug_activation("Skipping %smonitor of %s.%s",
|
||||
(monitor) ? "" : "un", display_lvname(lv),
|
||||
(monitor) ? " Cache pool activation for clearing only." : "");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow to unmonitor thin pool via explicit pool unmonitor
|
||||
* or unmonitor before the last thin pool user deactivation
|
||||
@@ -1685,7 +1799,8 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume
|
||||
/*
|
||||
* In case this LV is a snapshot origin, we instead monitor
|
||||
* each of its respective snapshots. The origin itself may
|
||||
* also need to be monitored if it is a mirror, for example.
|
||||
* also need to be monitored if it is a mirror, for example,
|
||||
* so fall through to process it afterwards.
|
||||
*/
|
||||
if (!laopts->origin_only && lv_is_origin(lv))
|
||||
dm_list_iterate_safe(snh, snht, &lv->snapshot_segs)
|
||||
@@ -1744,42 +1859,58 @@ int monitor_dev_for_events(struct cmd_context *cmd, const struct logical_volume
|
||||
!seg->segtype->ops->target_monitored) /* doesn't support registration */
|
||||
continue;
|
||||
|
||||
monitored = seg->segtype->ops->target_monitored(seg, &pending);
|
||||
if (!monitor)
|
||||
/* When unmonitoring, obtain existing dso being used. */
|
||||
monitored = _device_registered_with_dmeventd(cmd, seg_is_snapshot(seg) ? seg->cow : seg->lv, &pending, &dso);
|
||||
else
|
||||
monitored = seg->segtype->ops->target_monitored(seg, &pending);
|
||||
|
||||
/* FIXME: We should really try again if pending */
|
||||
monitored = (pending) ? 0 : monitored;
|
||||
|
||||
monitor_fn = NULL;
|
||||
new_unmonitor = 0;
|
||||
|
||||
if (monitor) {
|
||||
if (monitored)
|
||||
log_verbose("%s already monitored.", display_lvname(lv));
|
||||
else if (seg->segtype->ops->target_monitor_events)
|
||||
else if (seg->segtype->ops->target_monitor_events) {
|
||||
log_verbose("Monitoring %s%s", display_lvname(lv), test_mode() ? " [Test mode: skipping this]" : "");
|
||||
monitor_fn = seg->segtype->ops->target_monitor_events;
|
||||
}
|
||||
} else {
|
||||
if (!monitored)
|
||||
log_verbose("%s already not monitored.", display_lvname(lv));
|
||||
else if (seg->segtype->ops->target_unmonitor_events)
|
||||
monitor_fn = seg->segtype->ops->target_unmonitor_events;
|
||||
else if (dso && *dso) {
|
||||
/*
|
||||
* Divert unmonitor away from code that depends on the new segment
|
||||
* type instead of the existing one if it's changing.
|
||||
*/
|
||||
log_verbose("Not monitoring %s with %s%s", display_lvname(lv), dso, test_mode() ? " [Test mode: skipping this]" : "");
|
||||
new_unmonitor = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do [un]monitor */
|
||||
if (!monitor_fn)
|
||||
continue;
|
||||
|
||||
log_verbose("%sonitoring %s%s", monitor ? "M" : "Not m", display_lvname(lv),
|
||||
test_mode() ? " [Test mode: skipping this]" : "");
|
||||
|
||||
/* FIXME Test mode should really continue a bit further. */
|
||||
if (test_mode())
|
||||
continue;
|
||||
|
||||
/* FIXME specify events */
|
||||
if (!monitor_fn(seg, 0)) {
|
||||
log_error("%s: %s segment monitoring function failed.",
|
||||
display_lvname(lv), seg->segtype->name);
|
||||
return 0;
|
||||
}
|
||||
if (new_unmonitor) {
|
||||
if (!target_register_events(cmd, dso, seg_is_snapshot(seg) ? seg->cow : lv, 0, 0, 10)) {
|
||||
log_error("%s: segment unmonitoring failed.",
|
||||
display_lvname(lv));
|
||||
|
||||
return 0;
|
||||
}
|
||||
} else if (monitor_fn) {
|
||||
/* FIXME specify events */
|
||||
if (!monitor_fn(seg, 0)) {
|
||||
log_error("%s: %s segment monitoring function failed.",
|
||||
display_lvname(lv), seg->segtype->name);
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
continue;
|
||||
|
||||
/* Check [un]monitor results */
|
||||
/* Try a couple times if pending, but not forever... */
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2012 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2016 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@@ -92,10 +92,14 @@ int library_version(char *version, size_t size);
|
||||
int lvm1_present(struct cmd_context *cmd);
|
||||
|
||||
int module_present(struct cmd_context *cmd, const char *target_name);
|
||||
int target_present_version(struct cmd_context *cmd, const char *target_name,
|
||||
int use_modprobe, uint32_t *maj,
|
||||
uint32_t *min, uint32_t *patchlevel);
|
||||
int target_present(struct cmd_context *cmd, const char *target_name,
|
||||
int use_modprobe);
|
||||
int target_version(const char *target_name, uint32_t *maj,
|
||||
uint32_t *min, uint32_t *patchlevel);
|
||||
int raid4_is_supported(struct cmd_context *cmd, const struct segment_type *segtype);
|
||||
int lvm_dm_prefix_check(int major, int minor, const char *prefix);
|
||||
int list_segment_modules(struct dm_pool *mem, const struct lv_segment *seg,
|
||||
struct dm_list *modules);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2015 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2016 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@@ -85,7 +85,8 @@ int read_only_lv(const struct logical_volume *lv, const struct lv_activate_opts
|
||||
static struct dm_task *_setup_task(const char *name, const char *uuid,
|
||||
uint32_t *event_nr, int task,
|
||||
uint32_t major, uint32_t minor,
|
||||
int with_open_count)
|
||||
int with_open_count,
|
||||
int with_flush)
|
||||
{
|
||||
struct dm_task *dmt;
|
||||
|
||||
@@ -110,6 +111,9 @@ static struct dm_task *_setup_task(const char *name, const char *uuid,
|
||||
if (!with_open_count && !dm_task_no_open_count(dmt))
|
||||
log_warn("WARNING: Failed to disable open_count.");
|
||||
|
||||
if (!with_flush && !dm_task_no_flush(dmt))
|
||||
log_warn("WARNING: Failed to set no_flush.");
|
||||
|
||||
return dmt;
|
||||
out:
|
||||
dm_task_destroy(dmt);
|
||||
@@ -123,6 +127,7 @@ static int _get_segment_status_from_target_params(const char *target_name,
|
||||
struct segment_type *segtype;
|
||||
|
||||
seg_status->type = SEG_STATUS_UNKNOWN;
|
||||
|
||||
/*
|
||||
* TODO: Add support for other segment types too!
|
||||
* The segment to report status for must be properly
|
||||
@@ -130,19 +135,27 @@ static int _get_segment_status_from_target_params(const char *target_name,
|
||||
* linear/striped, old snapshots and raids have proper
|
||||
* segment selected for status!
|
||||
*/
|
||||
if (strcmp(target_name, TARGET_NAME_CACHE) &&
|
||||
strcmp(target_name, TARGET_NAME_THIN_POOL) &&
|
||||
strcmp(target_name, TARGET_NAME_THIN))
|
||||
return 1;
|
||||
if (!strcmp(target_name, TARGET_NAME_SNAPSHOT_MERGE) &&
|
||||
lv_is_merging_origin(seg_status->seg->lv)) {
|
||||
/* Snapshot merge has started, check snapshot status */
|
||||
if (!(segtype = get_segtype_from_string(seg_status->seg->lv->vg->cmd, TARGET_NAME_SNAPSHOT)))
|
||||
return_0;
|
||||
} else {
|
||||
if (strcmp(target_name, TARGET_NAME_CACHE) &&
|
||||
strcmp(target_name, TARGET_NAME_SNAPSHOT) &&
|
||||
strcmp(target_name, TARGET_NAME_THIN_POOL) &&
|
||||
strcmp(target_name, TARGET_NAME_THIN))
|
||||
return 1; /* TODO: Do not know how to handle yet */
|
||||
|
||||
if (!(segtype = get_segtype_from_string(seg_status->seg->lv->vg->cmd, target_name)))
|
||||
return_0;
|
||||
if (!(segtype = get_segtype_from_string(seg_status->seg->lv->vg->cmd, target_name)))
|
||||
return_0;
|
||||
|
||||
if (segtype != seg_status->seg->segtype) {
|
||||
log_error(INTERNAL_ERROR "_get_segment_status_from_target_params: "
|
||||
"segment type %s found does not match expected segment type %s",
|
||||
segtype->name, seg_status->seg->segtype->name);
|
||||
return 0;
|
||||
if (segtype != seg_status->seg->segtype) {
|
||||
log_error(INTERNAL_ERROR "_get_segment_status_from_target_params: "
|
||||
"segment type %s found does not match expected segment type %s",
|
||||
segtype->name, seg_status->seg->segtype->name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (segtype_is_cache(segtype)) {
|
||||
@@ -192,6 +205,7 @@ static int _info_run(info_type_t type, const char *name, const char *dlid,
|
||||
uint64_t target_start, target_length;
|
||||
char *target_name, *target_params, *params_to_process = NULL;
|
||||
uint32_t extent_size;
|
||||
int with_flush = 1; /* TODO: arg for _info_run */
|
||||
|
||||
switch (type) {
|
||||
case INFO:
|
||||
@@ -199,6 +213,7 @@ static int _info_run(info_type_t type, const char *name, const char *dlid,
|
||||
break;
|
||||
case STATUS:
|
||||
dmtask = DM_DEVICE_STATUS;
|
||||
with_flush = 0;
|
||||
break;
|
||||
case MKNODES:
|
||||
dmtask = DM_DEVICE_MKNODES;
|
||||
@@ -209,7 +224,7 @@ static int _info_run(info_type_t type, const char *name, const char *dlid,
|
||||
}
|
||||
|
||||
if (!(dmt = _setup_task((type == MKNODES) ? name : NULL, dlid, 0, dmtask,
|
||||
major, minor, with_open_count)))
|
||||
major, minor, with_open_count, with_flush)))
|
||||
return_0;
|
||||
|
||||
if (!dm_task_run(dmt))
|
||||
@@ -338,7 +353,7 @@ static int _ignore_blocked_mirror_devices(struct device *dev,
|
||||
* dead, we have no choice but to look up the table too.
|
||||
*/
|
||||
if (!(dmt = _setup_task(NULL, NULL, NULL, DM_DEVICE_TABLE,
|
||||
MAJOR(dev->dev), MINOR(dev->dev), 0)))
|
||||
MAJOR(dev->dev), MINOR(dev->dev), 0, 1)))
|
||||
goto_out;
|
||||
|
||||
if (!dm_task_run(dmt))
|
||||
@@ -380,7 +395,7 @@ static int _device_is_suspended(int major, int minor)
|
||||
int r = 0;
|
||||
|
||||
if (!(dmt = _setup_task(NULL, NULL, NULL, DM_DEVICE_INFO,
|
||||
major, minor, 0)))
|
||||
major, minor, 0, 0)))
|
||||
return_0;
|
||||
|
||||
if (!dm_task_run(dmt) ||
|
||||
@@ -405,7 +420,7 @@ static int _ignore_suspended_snapshot_component(struct device *dev)
|
||||
int r = 0;
|
||||
|
||||
if (!(dmt = _setup_task(NULL, NULL, NULL, DM_DEVICE_TABLE,
|
||||
MAJOR(dev->dev), MINOR(dev->dev), 0)))
|
||||
MAJOR(dev->dev), MINOR(dev->dev), 0, 1)))
|
||||
return_0;
|
||||
|
||||
if (!dm_task_run(dmt)) {
|
||||
@@ -452,7 +467,7 @@ static int _ignore_unusable_thins(struct device *dev)
|
||||
return_0;
|
||||
|
||||
if (!(dmt = _setup_task(NULL, NULL, NULL, DM_DEVICE_TABLE,
|
||||
MAJOR(dev->dev), MINOR(dev->dev), 0)))
|
||||
MAJOR(dev->dev), MINOR(dev->dev), 0, 1)))
|
||||
goto_out;
|
||||
|
||||
if (!dm_task_run(dmt)) {
|
||||
@@ -468,12 +483,9 @@ static int _ignore_unusable_thins(struct device *dev)
|
||||
dm_task_destroy(dmt);
|
||||
|
||||
if (!(dmt = _setup_task(NULL, NULL, NULL, DM_DEVICE_STATUS,
|
||||
major, minor, 0)))
|
||||
major, minor, 0, 0)))
|
||||
goto_out;
|
||||
|
||||
if (!dm_task_no_flush(dmt))
|
||||
log_warn("Can't set no_flush.");
|
||||
|
||||
if (!dm_task_run(dmt)) {
|
||||
log_error("Failed to get state of mapped device.");
|
||||
goto out;
|
||||
@@ -528,13 +540,9 @@ int device_is_usable(struct device *dev, struct dev_usable_check_params check)
|
||||
int r = 0;
|
||||
|
||||
if (!(dmt = _setup_task(NULL, NULL, NULL, DM_DEVICE_STATUS,
|
||||
MAJOR(dev->dev), MINOR(dev->dev), 0)))
|
||||
MAJOR(dev->dev), MINOR(dev->dev), 0, 0)))
|
||||
return_0;
|
||||
|
||||
/* Non-blocking status read */
|
||||
if (!dm_task_no_flush(dmt))
|
||||
log_warn("WARNING: Can't set no_flush for dm status.");
|
||||
|
||||
if (!dm_task_run(dmt)) {
|
||||
log_error("Failed to get state of mapped device");
|
||||
goto out;
|
||||
@@ -797,7 +805,7 @@ int lv_has_target_type(struct dm_pool *mem, const struct logical_volume *lv,
|
||||
if (!(dlid = build_dm_uuid(mem, lv, layer)))
|
||||
return_0;
|
||||
|
||||
if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_STATUS, 0, 0, 0)))
|
||||
if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_STATUS, 0, 0, 0, 0)))
|
||||
goto_bad;
|
||||
|
||||
if (!dm_task_run(dmt))
|
||||
@@ -810,7 +818,7 @@ int lv_has_target_type(struct dm_pool *mem, const struct logical_volume *lv,
|
||||
if (info.inactive_table) {
|
||||
dm_task_destroy(dmt);
|
||||
|
||||
if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_STATUS, 0, 0, 0)))
|
||||
if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_STATUS, 0, 0, 0, 0)))
|
||||
goto_bad;
|
||||
|
||||
if (!dm_task_query_inactive_table(dmt))
|
||||
@@ -855,7 +863,7 @@ static int _thin_lv_has_device_id(struct dm_pool *mem, const struct logical_volu
|
||||
if (!(dlid = build_dm_uuid(mem, lv, layer)))
|
||||
return_0;
|
||||
|
||||
if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_TABLE, 0, 0, 0)))
|
||||
if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_TABLE, 0, 0, 0, 1)))
|
||||
goto_bad;
|
||||
|
||||
if (!dm_task_run(dmt))
|
||||
@@ -868,7 +876,7 @@ static int _thin_lv_has_device_id(struct dm_pool *mem, const struct logical_volu
|
||||
if (info.inactive_table) {
|
||||
dm_task_destroy(dmt);
|
||||
|
||||
if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_TABLE, 0, 0, 0)))
|
||||
if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_TABLE, 0, 0, 0, 1)))
|
||||
goto_bad;
|
||||
|
||||
if (!dm_task_query_inactive_table(dmt))
|
||||
@@ -981,14 +989,9 @@ static int _percent_run(struct dev_manager *dm, const char *name,
|
||||
return_0;
|
||||
|
||||
if (!(dmt = _setup_task(name, dlid, event_nr,
|
||||
wait ? DM_DEVICE_WAITEVENT : DM_DEVICE_STATUS, 0, 0, 0)))
|
||||
wait ? DM_DEVICE_WAITEVENT : DM_DEVICE_STATUS, 0, 0, 0, 0)))
|
||||
return_0;
|
||||
|
||||
/* No freeze on overfilled thin-pool, read existing slightly outdated data */
|
||||
if (segtype_is_thin(segtype) &&
|
||||
!dm_task_no_flush(dmt))
|
||||
log_warn("Can't set no_flush flag."); /* Non fatal */
|
||||
|
||||
if (!dm_task_run(dmt))
|
||||
goto_out;
|
||||
|
||||
@@ -1103,7 +1106,7 @@ int dev_manager_transient(struct dev_manager *dm, const struct logical_volume *l
|
||||
if (!(dlid = build_dm_uuid(dm->mem, lv, layer)))
|
||||
return_0;
|
||||
|
||||
if (!(dmt = _setup_task(0, dlid, NULL, DM_DEVICE_STATUS, 0, 0, 0)))
|
||||
if (!(dmt = _setup_task(0, dlid, NULL, DM_DEVICE_STATUS, 0, 0, 0, 0)))
|
||||
return_0;
|
||||
|
||||
if (!dm_task_run(dmt))
|
||||
@@ -1299,7 +1302,7 @@ int dev_manager_raid_status(struct dev_manager *dm,
|
||||
if (!(dlid = build_dm_uuid(dm->mem, lv, layer)))
|
||||
return_0;
|
||||
|
||||
if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_STATUS, 0, 0, 0)))
|
||||
if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_STATUS, 0, 0, 0, 0)))
|
||||
return_0;
|
||||
|
||||
if (!dm_task_run(dmt))
|
||||
@@ -1358,7 +1361,7 @@ int dev_manager_raid_message(struct dev_manager *dm,
|
||||
if (!(dlid = build_dm_uuid(dm->mem, lv, layer)))
|
||||
return_0;
|
||||
|
||||
if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_TARGET_MSG, 0, 0, 0)))
|
||||
if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_TARGET_MSG, 0, 0, 0, 1)))
|
||||
return_0;
|
||||
|
||||
if (!dm_task_set_message(dmt, msg))
|
||||
@@ -1393,7 +1396,7 @@ int dev_manager_cache_status(struct dev_manager *dm,
|
||||
if (!(*status = dm_pool_zalloc(dm->mem, sizeof(struct lv_status_cache))))
|
||||
return_0;
|
||||
|
||||
if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_STATUS, 0, 0, 0)))
|
||||
if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_STATUS, 0, 0, 0, 0)))
|
||||
return_0;
|
||||
|
||||
if (!dm_task_run(dmt))
|
||||
@@ -1429,8 +1432,9 @@ int dev_manager_cache_status(struct dev_manager *dm,
|
||||
c->total_blocks);
|
||||
(*status)->metadata_usage = dm_make_percent(c->metadata_used_blocks,
|
||||
c->metadata_total_blocks);
|
||||
(*status)->dirty_usage = dm_make_percent(c->dirty_blocks,
|
||||
c->used_blocks);
|
||||
(*status)->dirty_usage = (c->used_blocks) ?
|
||||
dm_make_percent(c->dirty_blocks,
|
||||
c->used_blocks) : DM_PERCENT_0;
|
||||
}
|
||||
r = 1;
|
||||
out:
|
||||
@@ -1442,7 +1446,7 @@ out:
|
||||
int dev_manager_thin_pool_status(struct dev_manager *dm,
|
||||
const struct logical_volume *lv,
|
||||
struct dm_status_thin_pool **status,
|
||||
int noflush)
|
||||
int flush)
|
||||
{
|
||||
const char *dlid;
|
||||
struct dm_task *dmt;
|
||||
@@ -1456,12 +1460,9 @@ int dev_manager_thin_pool_status(struct dev_manager *dm,
|
||||
if (!(dlid = build_dm_uuid(dm->mem, lv, lv_layer(lv))))
|
||||
return_0;
|
||||
|
||||
if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_STATUS, 0, 0, 0)))
|
||||
if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_STATUS, 0, 0, 0, flush)))
|
||||
return_0;
|
||||
|
||||
if (noflush && !dm_task_no_flush(dmt))
|
||||
log_warn("Can't set no_flush.");
|
||||
|
||||
if (!dm_task_run(dmt))
|
||||
goto_out;
|
||||
|
||||
@@ -1543,7 +1544,7 @@ int dev_manager_thin_device_id(struct dev_manager *dm,
|
||||
if (!(dlid = build_dm_uuid(dm->mem, lv, lv_layer(lv))))
|
||||
return_0;
|
||||
|
||||
if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_TABLE, 0, 0, 0)))
|
||||
if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_TABLE, 0, 0, 0, 1)))
|
||||
return_0;
|
||||
|
||||
if (!dm_task_run(dmt))
|
||||
@@ -1599,6 +1600,12 @@ static int _dev_manager_lv_rmnodes(const struct logical_volume *lv)
|
||||
return fs_del_lv(lv);
|
||||
}
|
||||
|
||||
static int _lv_has_mknode(const struct logical_volume *lv)
|
||||
{
|
||||
return (lv_is_visible(lv) &&
|
||||
(!lv_is_thin_pool(lv) || lv_is_new_thin_pool(lv)));
|
||||
}
|
||||
|
||||
int dev_manager_mknodes(const struct logical_volume *lv)
|
||||
{
|
||||
struct dm_info dminfo;
|
||||
@@ -1610,7 +1617,7 @@ int dev_manager_mknodes(const struct logical_volume *lv)
|
||||
|
||||
if ((r = _info_run(MKNODES, name, NULL, &dminfo, NULL, NULL, 0, 0, 0, 0))) {
|
||||
if (dminfo.exists) {
|
||||
if (lv_is_visible(lv))
|
||||
if (_lv_has_mknode(lv))
|
||||
r = _dev_manager_lv_mknodes(lv);
|
||||
} else
|
||||
r = _dev_manager_lv_rmnodes(lv);
|
||||
@@ -2154,6 +2161,7 @@ static int _add_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
dm_list_iterate(snh, &lv->snapshot_segs)
|
||||
if (!_add_lv_to_dtree(dm, dtree, dm_list_struct_base(snh, struct lv_segment, origin_list)->cow, 0))
|
||||
return_0;
|
||||
|
||||
if (dm->activation && !origin_only && lv_is_merging_origin(lv) &&
|
||||
!_add_lv_to_dtree(dm, dtree, find_snapshot(lv)->lv, 1))
|
||||
return_0;
|
||||
@@ -2206,7 +2214,7 @@ static int _add_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
(!dm->track_pending_delete || !lv_is_cache(lv)) &&
|
||||
!_add_lv_to_dtree(dm, dtree, seg_lv(seg, s), 0))
|
||||
return_0;
|
||||
if (seg_is_raid(seg) &&
|
||||
if (seg_is_raid_with_meta(seg) && seg->meta_areas && seg_metalv(seg, s) &&
|
||||
!_add_lv_to_dtree(dm, dtree, seg_metalv(seg, s), 0))
|
||||
return_0;
|
||||
}
|
||||
@@ -2242,8 +2250,8 @@ bad:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *_add_error_device(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
struct lv_segment *seg, int s)
|
||||
static char *_add_error_or_zero_device(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
struct lv_segment *seg, int s, int use_zero)
|
||||
{
|
||||
char *dlid, *name;
|
||||
char errid[32];
|
||||
@@ -2254,13 +2262,15 @@ static char *_add_error_device(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
uint64_t size = (uint64_t) seg->len * seg->lv->vg->extent_size;
|
||||
|
||||
dm_list_iterate_items(seg_i, &seg->lv->segments) {
|
||||
if (seg == seg_i)
|
||||
if (seg == seg_i) {
|
||||
segno = i;
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
if (segno < 0) {
|
||||
log_error("_add_error_device called with bad segment");
|
||||
log_error(INTERNAL_ERROR "_add_error_or_zero_device called with bad segment.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -2273,7 +2283,7 @@ static char *_add_error_device(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
seg->lv->name, errid)))
|
||||
return_NULL;
|
||||
|
||||
log_debug_activation("Getting device info for %s [%s]", name, dlid);
|
||||
log_debug_activation("Getting device info for %s [%s].", name, dlid);
|
||||
if (!_info(dm->cmd, dlid, 1, 0, &info, NULL, NULL)) {
|
||||
log_error("Failed to get info for %s [%s].", name, dlid);
|
||||
return 0;
|
||||
@@ -2283,14 +2293,19 @@ static char *_add_error_device(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
/* Create new node */
|
||||
if (!(node = dm_tree_add_new_dev(dtree, name, dlid, 0, 0, 0, 0, 0)))
|
||||
return_NULL;
|
||||
if (!dm_tree_node_add_error_target(node, size))
|
||||
return_NULL;
|
||||
|
||||
if (use_zero) {
|
||||
if (!dm_tree_node_add_zero_target(node, size))
|
||||
return_NULL;
|
||||
} else
|
||||
if (!dm_tree_node_add_error_target(node, size))
|
||||
return_NULL;
|
||||
} else {
|
||||
/* Already exists */
|
||||
if (!dm_tree_add_dev(dtree, info.major, info.minor)) {
|
||||
log_error("Failed to add device (%" PRIu32 ":%" PRIu32") to dtree",
|
||||
log_error("Failed to add device (%" PRIu32 ":%" PRIu32") to dtree.",
|
||||
info.major, info.minor);
|
||||
return_NULL;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2302,14 +2317,15 @@ static int _add_error_area(struct dev_manager *dm, struct dm_tree_node *node,
|
||||
{
|
||||
char *dlid;
|
||||
uint64_t extent_size = seg->lv->vg->extent_size;
|
||||
int use_zero = !strcmp(dm->cmd->stripe_filler, TARGET_NAME_ZERO) ? 1 : 0;
|
||||
|
||||
if (!strcmp(dm->cmd->stripe_filler, TARGET_NAME_ERROR)) {
|
||||
if (!strcmp(dm->cmd->stripe_filler, TARGET_NAME_ERROR) || use_zero) {
|
||||
/*
|
||||
* FIXME, the tree pointer is first field of dm_tree_node, but
|
||||
* we don't have the struct definition available.
|
||||
*/
|
||||
struct dm_tree **tree = (struct dm_tree **) node;
|
||||
if (!(dlid = _add_error_device(dm, *tree, seg, s)))
|
||||
if (!(dlid = _add_error_or_zero_device(dm, *tree, seg, s, use_zero)))
|
||||
return_0;
|
||||
if (!dm_tree_node_add_target_area(node, NULL, dlid, extent_size * seg_le(seg, s)))
|
||||
return_0;
|
||||
@@ -2378,9 +2394,13 @@ int add_areas_line(struct dev_manager *dm, struct lv_segment *seg,
|
||||
return_0;
|
||||
continue;
|
||||
}
|
||||
if (!(dlid = build_dm_uuid(dm->mem, seg_metalv(seg, s), NULL)))
|
||||
return_0;
|
||||
if (!dm_tree_node_add_target_area(node, NULL, dlid, extent_size * seg_metale(seg, s)))
|
||||
|
||||
if (seg->meta_areas && seg_metalv(seg, s)) {
|
||||
if (!(dlid = build_dm_uuid(dm->mem, seg_metalv(seg, s), NULL)))
|
||||
return_0;
|
||||
if (!dm_tree_node_add_target_area(node, NULL, dlid, extent_size * seg_metale(seg, s)))
|
||||
return_0;
|
||||
} else if (!dm_tree_node_add_null_area(node, 0))
|
||||
return_0;
|
||||
|
||||
if (!(dlid = build_dm_uuid(dm->mem, seg_lv(seg, s), NULL)))
|
||||
@@ -2708,7 +2728,7 @@ static int _add_segment_to_dtree(struct dev_manager *dm,
|
||||
!_add_new_lv_to_dtree(dm, dtree, seg_lv(seg, s),
|
||||
laopts, NULL))
|
||||
return_0;
|
||||
if (seg_is_raid(seg) &&
|
||||
if (seg_is_raid_with_meta(seg) && seg->meta_areas && seg_metalv(seg, s) &&
|
||||
!_add_new_lv_to_dtree(dm, dtree, seg_metalv(seg, s),
|
||||
laopts, NULL))
|
||||
return_0;
|
||||
@@ -2813,7 +2833,7 @@ static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
if (!(name = dm_build_dm_name(dm->mem, lv->vg->name, lv->name, layer)))
|
||||
return_0;
|
||||
|
||||
/* Even unused thin-pool still needs to get layered UUID -suffix */
|
||||
/* Even unused thin-pool still needs to get layered UUID -suffix */
|
||||
if (!layer && lv_is_new_thin_pool(lv))
|
||||
layer = lv_layer(lv);
|
||||
|
||||
@@ -2988,7 +3008,7 @@ static int _create_lv_symlinks(struct dev_manager *dm, struct dm_tree_node *root
|
||||
r = 0;
|
||||
continue;
|
||||
}
|
||||
if (lv_is_visible(lvlayer->lv)) {
|
||||
if (_lv_has_mknode(lvlayer->lv)) {
|
||||
if (!_dev_manager_lv_mknodes(lvlayer->lv))
|
||||
r = 0;
|
||||
continue;
|
||||
@@ -3104,6 +3124,7 @@ static int _tree_action(struct dev_manager *dm, const struct logical_volume *lv,
|
||||
/* Some targets may build bigger tree for activation */
|
||||
dm->activation = ((action == PRELOAD) || (action == ACTIVATE));
|
||||
dm->suspend = (action == SUSPEND_WITH_LOCKFS) || (action == SUSPEND);
|
||||
|
||||
if (!(dtree = _create_partial_dtree(dm, lv, laopts->origin_only)))
|
||||
return_0;
|
||||
|
||||
|
@@ -69,7 +69,7 @@ int dev_manager_cache_status(struct dev_manager *dm,
|
||||
int dev_manager_thin_pool_status(struct dev_manager *dm,
|
||||
const struct logical_volume *lv,
|
||||
struct dm_status_thin_pool **status,
|
||||
int noflush);
|
||||
int flush);
|
||||
int dev_manager_thin_pool_percent(struct dev_manager *dm,
|
||||
const struct logical_volume *lv,
|
||||
int metadata, dm_percent_t *percent);
|
||||
|
838
lib/cache/lvmcache.c
vendored
838
lib/cache/lvmcache.c
vendored
File diff suppressed because it is too large
Load Diff
16
lib/cache/lvmcache.h
vendored
16
lib/cache/lvmcache.h
vendored
@@ -102,7 +102,7 @@ int lvmcache_vginfo_holders_dec_and_test_for_zero(struct lvmcache_vginfo *vginfo
|
||||
struct lvmcache_vginfo *lvmcache_vginfo_from_vgname(const char *vgname,
|
||||
const char *vgid);
|
||||
struct lvmcache_vginfo *lvmcache_vginfo_from_vgid(const char *vgid);
|
||||
struct lvmcache_info *lvmcache_info_from_pvid(const char *pvid, int valid_only);
|
||||
struct lvmcache_info *lvmcache_info_from_pvid(const char *pvid, struct device *dev, int valid_only);
|
||||
const char *lvmcache_vgname_from_vgid(struct dm_pool *mem, const char *vgid);
|
||||
const char *lvmcache_vgid_from_vgname(struct cmd_context *cmd, const char *vgname);
|
||||
struct device *lvmcache_device_from_pvid(struct cmd_context *cmd, const struct id *pvid,
|
||||
@@ -111,6 +111,7 @@ const char *lvmcache_pvid_from_devname(struct cmd_context *cmd,
|
||||
const char *devname);
|
||||
char *lvmcache_vgname_from_pvid(struct cmd_context *cmd, const char *pvid);
|
||||
const char *lvmcache_vgname_from_info(struct lvmcache_info *info);
|
||||
const struct format_type *lvmcache_fmt_from_info(struct lvmcache_info *info);
|
||||
int lvmcache_vgs_locked(void);
|
||||
int lvmcache_vgname_is_locked(const char *vgname);
|
||||
|
||||
@@ -193,12 +194,11 @@ unsigned lvmcache_mda_count(struct lvmcache_info *info);
|
||||
int lvmcache_vgid_is_cached(const char *vgid);
|
||||
uint64_t lvmcache_smallest_mda_size(struct lvmcache_info *info);
|
||||
|
||||
void lvmcache_replace_dev(struct cmd_context *cmd, struct physical_volume *pv,
|
||||
struct device *dev);
|
||||
|
||||
int lvmcache_found_duplicate_pvs(void);
|
||||
|
||||
void lvmcache_set_preferred_duplicates(const char *vgid);
|
||||
int lvmcache_get_unused_duplicate_devs(struct cmd_context *cmd, struct dm_list *head);
|
||||
|
||||
int vg_has_duplicate_pvs(struct volume_group *vg);
|
||||
|
||||
int lvmcache_contains_lock_type_sanlock(struct cmd_context *cmd);
|
||||
|
||||
@@ -209,4 +209,10 @@ int lvmcache_vg_is_foreign(struct cmd_context *cmd, const char *vgname, const ch
|
||||
|
||||
void lvmcache_lock_ordering(int enable);
|
||||
|
||||
int lvmcache_dev_is_unchosen_duplicate(struct device *dev);
|
||||
|
||||
void lvmcache_remove_unchosen_duplicate(struct device *dev);
|
||||
|
||||
int lvmcache_pvid_in_unchosen_duplicates(const char *pvid);
|
||||
|
||||
#endif
|
||||
|
1275
lib/cache/lvmetad.c
vendored
1275
lib/cache/lvmetad.c
vendored
File diff suppressed because it is too large
Load Diff
38
lib/cache/lvmetad.h
vendored
38
lib/cache/lvmetad.h
vendored
@@ -78,14 +78,16 @@ void lvmetad_release_token(void);
|
||||
* lvmetad_vg_commit. The request is validated immediately and lvmetad_vg_commit
|
||||
* only constitutes a pointer update.
|
||||
*/
|
||||
int lvmetad_vg_update(struct volume_group *vg);
|
||||
int lvmetad_vg_update_pending(struct volume_group *vg);
|
||||
int lvmetad_vg_update_finish(struct volume_group *vg);
|
||||
|
||||
/*
|
||||
* Inform lvmetad that a VG has been removed. This is not entirely safe, but is
|
||||
* only needed during vgremove, which does not wipe PV labels and therefore
|
||||
* cannot mark the PVs as gone.
|
||||
*/
|
||||
int lvmetad_vg_remove(struct volume_group *vg);
|
||||
int lvmetad_vg_remove_pending(struct volume_group *vg);
|
||||
int lvmetad_vg_remove_finish(struct volume_group *vg);
|
||||
|
||||
/*
|
||||
* Notify lvmetad that a PV has been found. It is not an error if the PV is
|
||||
@@ -95,15 +97,17 @@ int lvmetad_vg_remove(struct volume_group *vg);
|
||||
* number on the cached and on the discovered PV match but the metadata content
|
||||
* does not.
|
||||
*/
|
||||
int lvmetad_pv_found(const struct id *pvid, struct device *dev,
|
||||
int lvmetad_pv_found(struct cmd_context *cmd, const struct id *pvid, struct device *dev,
|
||||
const struct format_type *fmt, uint64_t label_sector,
|
||||
struct volume_group *vg, activation_handler handler);
|
||||
struct volume_group *vg,
|
||||
struct dm_list *found_vgnames,
|
||||
struct dm_list *changed_vgnames);
|
||||
|
||||
/*
|
||||
* Inform the daemon that the device no longer exists.
|
||||
*/
|
||||
int lvmetad_pv_gone(dev_t devno, const char *pv_name, activation_handler handler);
|
||||
int lvmetad_pv_gone_by_dev(struct device *dev, activation_handler handler);
|
||||
int lvmetad_pv_gone(dev_t devno, const char *pv_name);
|
||||
int lvmetad_pv_gone_by_dev(struct device *dev);
|
||||
|
||||
/*
|
||||
* Request a list of all PVs available to lvmetad. If requested, this will also
|
||||
@@ -142,10 +146,10 @@ struct volume_group *lvmetad_vg_lookup(struct cmd_context *cmd,
|
||||
* Scan a single device and update lvmetad with the result(s).
|
||||
*/
|
||||
int lvmetad_pvscan_single(struct cmd_context *cmd, struct device *dev,
|
||||
activation_handler handler, int ignore_obsolete);
|
||||
struct dm_list *found_vgnames,
|
||||
struct dm_list *changed_vgnames);
|
||||
|
||||
int lvmetad_pvscan_all_devs(struct cmd_context *cmd, activation_handler handler, int do_wait);
|
||||
int lvmetad_pvscan_foreign_vgs(struct cmd_context *cmd, activation_handler handler);
|
||||
int lvmetad_pvscan_all_devs(struct cmd_context *cmd, int do_wait);
|
||||
|
||||
int lvmetad_vg_clear_outdated_pvs(struct volume_group *vg);
|
||||
void lvmetad_validate_global_cache(struct cmd_context *cmd, int force);
|
||||
@@ -169,19 +173,21 @@ void lvmetad_clear_disabled(struct cmd_context *cmd);
|
||||
# define lvmetad_set_token(a) do { } while (0)
|
||||
# define lvmetad_release_token() do { } while (0)
|
||||
# define lvmetad_vg_update(vg) (1)
|
||||
# define lvmetad_vg_remove(vg) (1)
|
||||
# define lvmetad_pv_found(pvid, dev, fmt, label_sector, vg, handler) (1)
|
||||
# define lvmetad_pv_gone(devno, pv_name, handler) (1)
|
||||
# define lvmetad_pv_gone_by_dev(dev, handler) (1)
|
||||
# define lvmetad_vg_update_pending(vg) (1)
|
||||
# define lvmetad_vg_update_finish(vg) (1)
|
||||
# define lvmetad_vg_remove_pending(vg) (1)
|
||||
# define lvmetad_vg_remove_finish(vg) (1)
|
||||
# define lvmetad_pv_found(cmd, pvid, dev, fmt, label_sector, vg, found_vgnames, changed_vgnames) (1)
|
||||
# define lvmetad_pv_gone(devno, pv_name) (1)
|
||||
# define lvmetad_pv_gone_by_dev(dev) (1)
|
||||
# define lvmetad_pv_list_to_lvmcache(cmd) (1)
|
||||
# define lvmetad_pv_lookup(cmd, pvid, found) (0)
|
||||
# define lvmetad_pv_lookup_by_dev(cmd, dev, found) (0)
|
||||
# define lvmetad_vg_list_to_lvmcache(cmd) (1)
|
||||
# define lvmetad_get_vgnameids(cmd, vgnameids) do { } while (0)
|
||||
# define lvmetad_vg_lookup(cmd, vgname, vgid) (NULL)
|
||||
# define lvmetad_pvscan_single(cmd, dev, handler, ignore_obsolete) (0)
|
||||
# define lvmetad_pvscan_all_devs(cmd, handler, do_wait) (0)
|
||||
# define lvmetad_pvscan_foreign_vgs(cmd, handler) (0)
|
||||
# define lvmetad_pvscan_single(cmd, dev, found_vgnames, changed_vgnames) (0)
|
||||
# define lvmetad_pvscan_all_devs(cmd, do_wait) (0)
|
||||
# define lvmetad_vg_clear_outdated_pvs(vg) do { } while (0)
|
||||
# define lvmetad_validate_global_cache(cmd, force) do { } while (0)
|
||||
# define lvmetad_vg_is_foreign(cmd, vgname, vgid) (0)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2013-2014 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2013-2016 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@@ -26,6 +26,8 @@
|
||||
#include "defaults.h"
|
||||
|
||||
static const char _cache_module[] = "cache";
|
||||
#define CACHE_POLICY_WHEN_MISSING "mq"
|
||||
#define CACHE_MODE_WHEN_MISSING CACHE_MODE_WRITETHROUGH
|
||||
|
||||
/* TODO: using static field here, maybe should be a part of segment_type */
|
||||
static unsigned _feature_mask;
|
||||
@@ -41,24 +43,21 @@ static unsigned _feature_mask;
|
||||
*
|
||||
* Needs both segments cache and cache_pool to be loaded.
|
||||
*/
|
||||
static int _fix_missing_defaults(struct lv_segment *cpool_seg)
|
||||
static void _fix_missing_defaults(struct lv_segment *cpool_seg)
|
||||
{
|
||||
if (!cpool_seg->policy_name) {
|
||||
cpool_seg->policy_name = "mq";
|
||||
log_verbose("Cache is missing cache policy, using %s.",
|
||||
cpool_seg->policy_name = CACHE_POLICY_WHEN_MISSING;
|
||||
log_verbose("Cache pool %s is missing cache policy, using %s.",
|
||||
display_lvname(cpool_seg->lv),
|
||||
cpool_seg->policy_name);
|
||||
}
|
||||
|
||||
if (!cache_mode_is_set(cpool_seg)) {
|
||||
if (!cache_set_mode(cpool_seg, "writethrough")) {
|
||||
log_error(INTERNAL_ERROR "Failed to writethrough cache mode.");
|
||||
return 0;
|
||||
}
|
||||
log_verbose("Cache is missing cache mode, using %s.",
|
||||
if (cpool_seg->cache_mode == CACHE_MODE_UNDEFINED) {
|
||||
cpool_seg->cache_mode = CACHE_MODE_WHEN_MISSING;
|
||||
log_verbose("Cache pool %s is missing cache mode, using %s.",
|
||||
display_lvname(cpool_seg->lv),
|
||||
get_cache_mode_name(cpool_seg));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _cache_pool_text_import(struct lv_segment *seg,
|
||||
@@ -97,7 +96,7 @@ static int _cache_pool_text_import(struct lv_segment *seg,
|
||||
if (dm_config_has_node(sn, "cache_mode")) {
|
||||
if (!(str = dm_config_find_str(sn, "cache_mode", NULL)))
|
||||
return SEG_LOG_ERROR("cache_mode must be a string in");
|
||||
if (!cache_set_mode(seg, str))
|
||||
if (!set_cache_mode(&seg->cache_mode, str))
|
||||
return SEG_LOG_ERROR("Unknown cache_mode in");
|
||||
}
|
||||
|
||||
@@ -141,9 +140,9 @@ static int _cache_pool_text_import(struct lv_segment *seg,
|
||||
if (!attach_pool_metadata_lv(seg, meta_lv))
|
||||
return_0;
|
||||
|
||||
if (!dm_list_empty(&seg->lv->segs_using_this_lv) &&
|
||||
!_fix_missing_defaults(seg))
|
||||
return_0;
|
||||
/* when cache pool is used, we require policy and mode to be defined */
|
||||
if (!dm_list_empty(&seg->lv->segs_using_this_lv))
|
||||
_fix_missing_defaults(seg);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -170,7 +169,7 @@ static int _cache_pool_text_export(const struct lv_segment *seg,
|
||||
* but not worth to break backward compatibility, by shifting
|
||||
* content to cache segment
|
||||
*/
|
||||
if (cache_mode_is_set(seg)) {
|
||||
if (seg->cache_mode != CACHE_MODE_UNDEFINED) {
|
||||
if (!(cache_mode = get_cache_mode_name(seg)))
|
||||
return_0;
|
||||
outf(f, "cache_mode = \"%s\"", cache_mode);
|
||||
@@ -211,8 +210,11 @@ static int _target_present(struct cmd_context *cmd,
|
||||
unsigned cache_alias;
|
||||
const char feature[12];
|
||||
const char module[12]; /* check dm-%s */
|
||||
const char *aliasing;
|
||||
} _features[] = {
|
||||
{ 1, 9, 0, CACHE_FEATURE_POLICY_MQ, "policy_smq", "cache-smq" },
|
||||
/* Assumption: cache >=1.9 always aliases MQ policy */
|
||||
{ 1, 9, CACHE_FEATURE_POLICY_SMQ, CACHE_FEATURE_POLICY_MQ, "policy_smq", "cache-smq",
|
||||
" and aliases cache-mq" },
|
||||
{ 1, 8, CACHE_FEATURE_POLICY_SMQ, 0, "policy_smq", "cache-smq" },
|
||||
{ 1, 3, CACHE_FEATURE_POLICY_MQ, 0, "policy_mq", "cache-mq" },
|
||||
};
|
||||
@@ -232,10 +234,8 @@ static int _target_present(struct cmd_context *cmd,
|
||||
if (!_cache_checked) {
|
||||
_cache_checked = 1;
|
||||
|
||||
if (!(_cache_present = target_present(cmd, TARGET_NAME_CACHE, 1)))
|
||||
return 0;
|
||||
|
||||
if (!target_version(TARGET_NAME_CACHE, &maj, &min, &patchlevel))
|
||||
if (!(_cache_present = target_present_version(cmd, TARGET_NAME_CACHE, 1,
|
||||
&maj, &min, &patchlevel)))
|
||||
return_0;
|
||||
|
||||
if ((maj < 1) ||
|
||||
@@ -247,16 +247,17 @@ static int _target_present(struct cmd_context *cmd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < DM_ARRAY_SIZE(_features); ++i) {
|
||||
if (_attrs & _features[i].cache_feature)
|
||||
continue; /* already present */
|
||||
if (((maj > _features[i].maj) ||
|
||||
(maj == _features[i].maj && min >= _features[i].min)) &&
|
||||
(!_features[i].module[0] ||
|
||||
(_attrs & _features[i].cache_feature) || /* already present */
|
||||
module_present(cmd, _features[i].module)))
|
||||
_attrs |= _features[i].cache_feature |
|
||||
_features[i].cache_alias;
|
||||
else
|
||||
module_present(cmd, _features[i].module)) {
|
||||
log_debug_activation("Cache policy %s is available%s.",
|
||||
_features[i].module,
|
||||
_features[i].aliasing ? : "");
|
||||
_attrs |= (_features[i].cache_feature | _features[i].cache_alias);
|
||||
} else if (!_features[i].cache_alias)
|
||||
log_very_verbose("Target %s does not support %s.",
|
||||
_cache_module, _features[i].feature);
|
||||
}
|
||||
@@ -356,9 +357,9 @@ static int _cache_text_import(struct lv_segment *seg,
|
||||
if (!attach_pool_lv(seg, pool_lv, NULL, NULL, NULL))
|
||||
return_0;
|
||||
|
||||
if (!dm_list_empty(&pool_lv->segments) &&
|
||||
!_fix_missing_defaults(first_seg(pool_lv)))
|
||||
return_0;
|
||||
/* load order is unknown, could be cache origin or pool LV, so check for both */
|
||||
if (!dm_list_empty(&pool_lv->segments))
|
||||
_fix_missing_defaults(first_seg(pool_lv));
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -397,6 +398,7 @@ static int _cache_add_target_line(struct dev_manager *dm,
|
||||
{
|
||||
struct lv_segment *cache_pool_seg;
|
||||
char *metadata_uuid, *data_uuid, *origin_uuid;
|
||||
uint64_t feature_flags = 0;
|
||||
|
||||
if (!seg->pool_lv || !seg_is_cache(seg)) {
|
||||
log_error(INTERNAL_ERROR "Passed segment is not cache.");
|
||||
@@ -404,6 +406,25 @@ static int _cache_add_target_line(struct dev_manager *dm,
|
||||
}
|
||||
|
||||
cache_pool_seg = first_seg(seg->pool_lv);
|
||||
if (seg->cleaner_policy)
|
||||
/* With cleaner policy always pass writethrough */
|
||||
feature_flags |= DM_CACHE_FEATURE_WRITETHROUGH;
|
||||
else
|
||||
switch (cache_pool_seg->cache_mode) {
|
||||
default:
|
||||
log_error(INTERNAL_ERROR "LV %s has unknown cache mode %d.",
|
||||
display_lvname(seg->lv), cache_pool_seg->cache_mode);
|
||||
/* Fall through */
|
||||
case CACHE_MODE_WRITETHROUGH:
|
||||
feature_flags |= DM_CACHE_FEATURE_WRITETHROUGH;
|
||||
break;
|
||||
case CACHE_MODE_WRITEBACK:
|
||||
feature_flags |= DM_CACHE_FEATURE_WRITEBACK;
|
||||
break;
|
||||
case CACHE_MODE_PASSTHROUGH:
|
||||
feature_flags |= DM_CACHE_FEATURE_PASSTHROUGH;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(metadata_uuid = build_dm_uuid(mem, cache_pool_seg->metadata_lv, NULL)))
|
||||
return_0;
|
||||
@@ -415,7 +436,7 @@ static int _cache_add_target_line(struct dev_manager *dm,
|
||||
return_0;
|
||||
|
||||
if (!dm_tree_node_add_cache_target(node, len,
|
||||
cache_pool_seg->feature_flags,
|
||||
feature_flags,
|
||||
metadata_uuid,
|
||||
data_uuid,
|
||||
origin_uuid,
|
||||
|
@@ -365,7 +365,7 @@ static void _init_logging(struct cmd_context *cmd)
|
||||
/* Tell device-mapper about our logging */
|
||||
#ifdef DEVMAPPER_SUPPORT
|
||||
if (!dm_log_is_non_default())
|
||||
dm_log_with_errno_init(print_log);
|
||||
dm_log_with_errno_init(print_log_libdm);
|
||||
#endif
|
||||
reset_log_duplicated();
|
||||
reset_lvm_errno(1);
|
||||
@@ -640,8 +640,8 @@ static int _process_config(struct cmd_context *cmd)
|
||||
if (!strcmp(cmd->stripe_filler, "/dev/ioerror") &&
|
||||
stat(cmd->stripe_filler, &st))
|
||||
cmd->stripe_filler = "error";
|
||||
|
||||
if (strcmp(cmd->stripe_filler, "error")) {
|
||||
else if (strcmp(cmd->stripe_filler, "error") &&
|
||||
strcmp(cmd->stripe_filler, "zero")) {
|
||||
if (stat(cmd->stripe_filler, &st)) {
|
||||
log_warn("WARNING: activation/missing_stripe_filler = \"%s\" "
|
||||
"is invalid,", cmd->stripe_filler);
|
||||
@@ -950,6 +950,9 @@ static void _destroy_config(struct cmd_context *cmd)
|
||||
* they will get loaded again automatically.
|
||||
*/
|
||||
dm_list_iterate_items_safe(profile, tmp_profile, &cmd->profile_params->profiles) {
|
||||
if (cmd->is_interactive && (profile == cmd->profile_params->shell_profile))
|
||||
continue;
|
||||
|
||||
config_destroy(profile->cft);
|
||||
profile->cft = NULL;
|
||||
dm_list_move(&cmd->profile_params->profiles_to_load, &profile->list);
|
||||
@@ -1053,7 +1056,7 @@ static int _init_dev_cache(struct cmd_context *cmd)
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define MAX_FILTERS 8
|
||||
#define MAX_FILTERS 9
|
||||
|
||||
static struct dev_filter *_init_lvmetad_filter_chain(struct cmd_context *cmd)
|
||||
{
|
||||
@@ -1078,6 +1081,13 @@ static struct dev_filter *_init_lvmetad_filter_chain(struct cmd_context *cmd)
|
||||
nr_filt++;
|
||||
}
|
||||
|
||||
/* internal filter used by command processing. */
|
||||
if (!(filters[nr_filt] = internal_filter_create())) {
|
||||
log_error("Failed to create internal device filter");
|
||||
goto bad;
|
||||
}
|
||||
nr_filt++;
|
||||
|
||||
/* global regex filter. Optional. */
|
||||
if ((cn = find_config_tree_node(cmd, devices_global_filter_CFG, NULL))) {
|
||||
if (!(filters[nr_filt] = regex_filter_create(cn->v))) {
|
||||
@@ -1159,7 +1169,7 @@ bad:
|
||||
* If lvmetad is used, there are three filter chains:
|
||||
*
|
||||
* - cmd->lvmetad_filter - the lvmetad filter chain used when scanning devs for lvmetad update:
|
||||
* sysfs filter -> global regex filter -> type filter ->
|
||||
* sysfs filter -> internal filter -> global regex filter -> type filter ->
|
||||
* usable device filter(FILTER_MODE_PRE_LVMETAD) ->
|
||||
* mpath component filter -> partitioned filter ->
|
||||
* md component filter -> fw raid filter
|
||||
@@ -1173,7 +1183,7 @@ bad:
|
||||
* If lvmetad is not used, there's just one filter chain:
|
||||
*
|
||||
* - cmd->filter == cmd->full_filter:
|
||||
* persistent filter -> sysfs filter -> global regex filter ->
|
||||
* persistent filter -> sysfs filter -> internal filter -> global regex filter ->
|
||||
* regex_filter -> type filter -> usable device filter(FILTER_MODE_NO_LVMETAD) ->
|
||||
* mpath component filter -> partitioned filter -> md component filter -> fw raid filter
|
||||
*
|
||||
@@ -1642,37 +1652,6 @@ static void _init_globals(struct cmd_context *cmd)
|
||||
init_mirror_in_sync(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Close and reopen stream on file descriptor fd.
|
||||
*/
|
||||
static int _reopen_stream(FILE *stream, int fd, const char *mode, const char *name, FILE **new_stream)
|
||||
{
|
||||
int fd_copy, new_fd;
|
||||
|
||||
if ((fd_copy = dup(fd)) < 0) {
|
||||
log_sys_error("dup", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fclose(stream))
|
||||
log_sys_error("fclose", name);
|
||||
|
||||
if ((new_fd = dup2(fd_copy, fd)) < 0)
|
||||
log_sys_error("dup2", name);
|
||||
else if (new_fd != fd)
|
||||
log_error("dup2(%d, %d) returned %d", fd_copy, fd, new_fd);
|
||||
|
||||
if (close(fd_copy) < 0)
|
||||
log_sys_error("close", name);
|
||||
|
||||
if (!(*new_stream = fdopen(fd, mode))) {
|
||||
log_sys_error("fdopen", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* init_connections();
|
||||
* _init_lvmetad();
|
||||
@@ -1776,6 +1755,66 @@ bad:
|
||||
return 0;
|
||||
}
|
||||
|
||||
void destroy_config_context(struct cmd_context *cmd)
|
||||
{
|
||||
_destroy_config(cmd);
|
||||
|
||||
if (cmd->mem)
|
||||
dm_pool_destroy(cmd->mem);
|
||||
if (cmd->libmem)
|
||||
dm_pool_destroy(cmd->libmem);
|
||||
|
||||
dm_free(cmd);
|
||||
}
|
||||
|
||||
/*
|
||||
* A "config context" is a very light weight toolcontext that
|
||||
* is only used for reading config settings from lvm.conf.
|
||||
*/
|
||||
struct cmd_context *create_config_context(void)
|
||||
{
|
||||
struct cmd_context *cmd;
|
||||
|
||||
if (!(cmd = dm_zalloc(sizeof(*cmd))))
|
||||
goto_out;
|
||||
|
||||
strcpy(cmd->system_dir, DEFAULT_SYS_DIR);
|
||||
|
||||
if (!_get_env_vars(cmd))
|
||||
goto_out;
|
||||
|
||||
if (!(cmd->libmem = dm_pool_create("library", 4 * 1024)))
|
||||
goto_out;
|
||||
|
||||
dm_list_init(&cmd->config_files);
|
||||
dm_list_init(&cmd->tags);
|
||||
|
||||
if (!_init_lvm_conf(cmd))
|
||||
goto_out;
|
||||
|
||||
if (!_init_hostname(cmd))
|
||||
goto_out;
|
||||
|
||||
if (!_init_tags(cmd, cmd->cft))
|
||||
goto_out;
|
||||
|
||||
/* Load lvmlocal.conf */
|
||||
if (*cmd->system_dir && !_load_config_file(cmd, "", 1))
|
||||
goto_out;
|
||||
|
||||
if (!_init_tag_configs(cmd))
|
||||
goto_out;
|
||||
|
||||
if (!(cmd->cft = _merge_config_files(cmd, cmd->cft)))
|
||||
goto_out;
|
||||
|
||||
return cmd;
|
||||
out:
|
||||
if (cmd)
|
||||
destroy_config_context(cmd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Entry point */
|
||||
struct cmd_context *create_toolcontext(unsigned is_long_lived,
|
||||
const char *system_dir,
|
||||
@@ -1785,7 +1824,6 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
|
||||
unsigned set_filters)
|
||||
{
|
||||
struct cmd_context *cmd;
|
||||
FILE *new_stream;
|
||||
int flags;
|
||||
|
||||
#ifdef M_MMAP_MAX
|
||||
@@ -1835,9 +1873,8 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
|
||||
if (is_valid_fd(STDIN_FILENO) &&
|
||||
((flags = fcntl(STDIN_FILENO, F_GETFL)) > 0) &&
|
||||
(flags & O_ACCMODE) != O_WRONLY) {
|
||||
if (!_reopen_stream(stdin, STDIN_FILENO, "r", "stdin", &new_stream))
|
||||
if (!reopen_standard_stream(&stdin, "r"))
|
||||
goto_out;
|
||||
stdin = new_stream;
|
||||
if (setvbuf(stdin, cmd->linebuffer, _IOLBF, linebuffer_size)) {
|
||||
log_sys_error("setvbuf", "");
|
||||
goto out;
|
||||
@@ -1847,9 +1884,8 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
|
||||
if (is_valid_fd(STDOUT_FILENO) &&
|
||||
((flags = fcntl(STDOUT_FILENO, F_GETFL)) > 0) &&
|
||||
(flags & O_ACCMODE) != O_RDONLY) {
|
||||
if (!_reopen_stream(stdout, STDOUT_FILENO, "w", "stdout", &new_stream))
|
||||
if (!reopen_standard_stream(&stdout, "w"))
|
||||
goto_out;
|
||||
stdout = new_stream;
|
||||
if (setvbuf(stdout, cmd->linebuffer + linebuffer_size,
|
||||
_IOLBF, linebuffer_size)) {
|
||||
log_sys_error("setvbuf", "");
|
||||
@@ -1861,7 +1897,6 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
|
||||
/* Without buffering, must not use stdin/stdout */
|
||||
init_silent(1);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Environment variable LVM_SYSTEM_DIR overrides this below.
|
||||
*/
|
||||
@@ -1934,6 +1969,8 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
|
||||
if (!init_lvmcache_orphans(cmd))
|
||||
goto_out;
|
||||
|
||||
dm_list_init(&cmd->unused_duplicate_devs);
|
||||
|
||||
if (!_init_segtypes(cmd))
|
||||
goto_out;
|
||||
|
||||
@@ -1945,7 +1982,7 @@ struct cmd_context *create_toolcontext(unsigned is_long_lived,
|
||||
_init_globals(cmd);
|
||||
|
||||
if (set_connections && !init_connections(cmd))
|
||||
return_0;
|
||||
goto_out;
|
||||
|
||||
if (set_filters && !init_filters(cmd, 1))
|
||||
goto_out;
|
||||
@@ -2175,7 +2212,6 @@ int refresh_toolcontext(struct cmd_context *cmd)
|
||||
void destroy_toolcontext(struct cmd_context *cmd)
|
||||
{
|
||||
struct dm_config_tree *cft_cmdline;
|
||||
FILE *new_stream;
|
||||
int flags;
|
||||
|
||||
if (cmd->dump_filter && cmd->filter && cmd->filter->dump &&
|
||||
@@ -2211,20 +2247,18 @@ void destroy_toolcontext(struct cmd_context *cmd)
|
||||
if (is_valid_fd(STDIN_FILENO) &&
|
||||
((flags = fcntl(STDIN_FILENO, F_GETFL)) > 0) &&
|
||||
(flags & O_ACCMODE) != O_WRONLY) {
|
||||
if (_reopen_stream(stdin, STDIN_FILENO, "r", "stdin", &new_stream)) {
|
||||
stdin = new_stream;
|
||||
if (reopen_standard_stream(&stdin, "r"))
|
||||
setlinebuf(stdin);
|
||||
} else
|
||||
else
|
||||
cmd->linebuffer = NULL; /* Leave buffer in place (deliberate leak) */
|
||||
}
|
||||
|
||||
if (is_valid_fd(STDOUT_FILENO) &&
|
||||
((flags = fcntl(STDOUT_FILENO, F_GETFL)) > 0) &&
|
||||
(flags & O_ACCMODE) != O_RDONLY) {
|
||||
if (_reopen_stream(stdout, STDOUT_FILENO, "w", "stdout", &new_stream)) {
|
||||
stdout = new_stream;
|
||||
if (reopen_standard_stream(&stdout, "w"))
|
||||
setlinebuf(stdout);
|
||||
} else
|
||||
else
|
||||
cmd->linebuffer = NULL; /* Leave buffer in place (deliberate leak) */
|
||||
}
|
||||
|
||||
|
@@ -66,6 +66,15 @@ struct cmd_context_initialized_parts {
|
||||
unsigned connections:1;
|
||||
};
|
||||
|
||||
struct cmd_report {
|
||||
int log_only;
|
||||
dm_report_group_type_t report_group_type;
|
||||
struct dm_report_group *report_group;
|
||||
struct dm_report *log_rh;
|
||||
const char *log_name;
|
||||
log_report_t saved_log_report_state;
|
||||
};
|
||||
|
||||
/* FIXME Split into tool & library contexts */
|
||||
/* command-instance-related variables needed by library */
|
||||
struct cmd_context {
|
||||
@@ -79,11 +88,19 @@ struct cmd_context {
|
||||
* Command line and arguments.
|
||||
*/
|
||||
const char *cmd_line;
|
||||
const char *name; /* needed before cmd->command is set */
|
||||
struct command *command;
|
||||
char **argv;
|
||||
struct arg_values *arg_values;
|
||||
struct arg_values *opt_arg_values;
|
||||
struct dm_list arg_value_groups;
|
||||
|
||||
/*
|
||||
* Position args remaining after command name
|
||||
* and --options are removed from original argc/argv.
|
||||
*/
|
||||
int position_argc;
|
||||
char **position_argv;
|
||||
|
||||
/*
|
||||
* Format handlers.
|
||||
*/
|
||||
@@ -113,6 +130,7 @@ struct cmd_context {
|
||||
* Switches.
|
||||
*/
|
||||
unsigned is_long_lived:1; /* optimises persistent_filter handling */
|
||||
unsigned is_interactive:1;
|
||||
unsigned check_pv_dev_sizes:1;
|
||||
unsigned handles_missing_pvs:1;
|
||||
unsigned handles_unknown_segments:1;
|
||||
@@ -184,6 +202,11 @@ struct cmd_context {
|
||||
char dev_dir[PATH_MAX];
|
||||
char proc_dir[PATH_MAX];
|
||||
|
||||
/*
|
||||
* Reporting.
|
||||
*/
|
||||
struct cmd_report cmd_report;
|
||||
|
||||
/*
|
||||
* Buffers.
|
||||
*/
|
||||
@@ -197,6 +220,7 @@ struct cmd_context {
|
||||
const char *report_list_item_separator;
|
||||
const char *time_format;
|
||||
unsigned rand_seed;
|
||||
struct dm_list unused_duplicate_devs; /* save preferences between lvmcache instances */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -218,6 +242,14 @@ int init_lvmcache_orphans(struct cmd_context *cmd);
|
||||
int init_filters(struct cmd_context *cmd, unsigned load_persistent_cache);
|
||||
int init_connections(struct cmd_context *cmd);
|
||||
|
||||
/*
|
||||
* A config context is a very light weight cmd struct that
|
||||
* is only used for reading config settings from lvm.conf,
|
||||
* which are at cmd->cft.
|
||||
*/
|
||||
struct cmd_context *create_config_context(void);
|
||||
void destroy_config_context(struct cmd_context *cmd);
|
||||
|
||||
struct format_type *get_format_by_name(struct cmd_context *cmd, const char *format);
|
||||
|
||||
const char *system_id_from_string(struct cmd_context *cmd, const char *str);
|
||||
|
@@ -385,6 +385,13 @@ int override_config_tree_from_string(struct cmd_context *cmd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cmd->is_interactive &&
|
||||
!config_force_check(cmd, CONFIG_STRING, cft_new)) {
|
||||
log_error("Ignoring invalid configuration string.");
|
||||
dm_config_destroy(cft_new);
|
||||
return_0;
|
||||
}
|
||||
|
||||
if (!(cs = dm_pool_zalloc(cft_new->mem, sizeof(struct config_source)))) {
|
||||
log_error("Failed to allocate config source.");
|
||||
dm_config_destroy(cft_new);
|
||||
@@ -489,7 +496,7 @@ int override_config_tree_from_profile(struct cmd_context *cmd,
|
||||
int config_file_read_fd(struct dm_config_tree *cft, struct device *dev,
|
||||
off_t offset, size_t size, off_t offset2, size_t size2,
|
||||
checksum_fn_t checksum_fn, uint32_t checksum,
|
||||
int checksum_only)
|
||||
int checksum_only, int no_dup_node_check)
|
||||
{
|
||||
char *fb, *fe;
|
||||
int r = 0;
|
||||
@@ -540,8 +547,13 @@ int config_file_read_fd(struct dm_config_tree *cft, struct device *dev,
|
||||
|
||||
if (!checksum_only) {
|
||||
fe = fb + size + size2;
|
||||
if (!dm_config_parse(cft, fb, fe))
|
||||
goto_out;
|
||||
if (no_dup_node_check) {
|
||||
if (!dm_config_parse_without_dup_node_check(cft, fb, fe))
|
||||
goto_out;
|
||||
} else {
|
||||
if (!dm_config_parse(cft, fb, fe))
|
||||
goto_out;
|
||||
}
|
||||
}
|
||||
|
||||
r = 1;
|
||||
@@ -589,7 +601,7 @@ int config_file_read(struct dm_config_tree *cft)
|
||||
}
|
||||
|
||||
r = config_file_read_fd(cft, cf->dev, 0, (size_t) info.st_size, 0, 0,
|
||||
(checksum_fn_t) NULL, 0, 0);
|
||||
(checksum_fn_t) NULL, 0, 0, 0);
|
||||
|
||||
if (!cf->keep_open) {
|
||||
if (!dev_close(cf->dev))
|
||||
@@ -984,6 +996,20 @@ static int _config_def_check_node_is_profilable(struct cft_check_handle *handle,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _config_def_check_node_is_allowed(struct cft_check_handle *handle,
|
||||
const char *rp, struct dm_config_node *cn,
|
||||
const cfg_def_item_t *def)
|
||||
{
|
||||
if (handle->disallowed_flags & def->flags) {
|
||||
log_warn_suppress(handle->suppress_messages,
|
||||
"Configuration %s \"%s\" is not allowed here.",
|
||||
cn->v ? "option" : "section", rp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _config_def_check_node(struct cft_check_handle *handle,
|
||||
const char *vp, char *pvp, char *rp, char *prp,
|
||||
size_t buf_size, struct dm_config_node *cn)
|
||||
@@ -1034,6 +1060,9 @@ static int _config_def_check_node(struct cft_check_handle *handle,
|
||||
!_config_def_check_node_is_profilable(handle, rp, cn, def))
|
||||
return_0;
|
||||
|
||||
if (!_config_def_check_node_is_allowed(handle, rp, cn, def))
|
||||
return_0;
|
||||
|
||||
handle->status[def->id] |= CFG_VALID;
|
||||
return 1;
|
||||
}
|
||||
@@ -2113,7 +2142,7 @@ bad:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _check_profile(struct cmd_context *cmd, struct profile *profile)
|
||||
int config_force_check(struct cmd_context *cmd, config_source_t source, struct dm_config_tree *cft)
|
||||
{
|
||||
struct cft_check_handle *handle;
|
||||
int r;
|
||||
@@ -2124,13 +2153,19 @@ static int _check_profile(struct cmd_context *cmd, struct profile *profile)
|
||||
}
|
||||
|
||||
handle->cmd = cmd;
|
||||
handle->cft = profile->cft;
|
||||
handle->source = profile->source;
|
||||
/* the check is compulsory - allow only profilable items in a profile config! */
|
||||
handle->cft = cft;
|
||||
handle->source = source;
|
||||
handle->force_check = 1;
|
||||
/* provide warning messages only if config/checks=1 */
|
||||
handle->suppress_messages = !find_config_tree_bool(cmd, config_checks_CFG, NULL);
|
||||
|
||||
/*
|
||||
* Some settings can't be changed if we're running commands interactively
|
||||
* within lvm shell so check for them in case we're in this interactive mode.
|
||||
*/
|
||||
if (cmd->is_interactive)
|
||||
handle->disallowed_flags |= CFG_DISALLOW_INTERACTIVE;
|
||||
|
||||
r = config_def_check(handle);
|
||||
|
||||
dm_pool_free(cmd->libmem, handle);
|
||||
@@ -2252,7 +2287,7 @@ int load_profile(struct cmd_context *cmd, struct profile *profile) {
|
||||
* messages to be suppressed, but the check itself is always done
|
||||
* for profiles!
|
||||
*/
|
||||
if (!_check_profile(cmd, profile)) {
|
||||
if (!config_force_check(cmd, profile->source, profile->cft)) {
|
||||
log_error("Ignoring invalid %s %s.",
|
||||
_config_source_names[profile->source], profile->name);
|
||||
config_destroy(profile->cft);
|
||||
@@ -2431,3 +2466,25 @@ int get_default_allocation_cache_pool_chunk_size_CFG(struct cmd_context *cmd, st
|
||||
{
|
||||
return DEFAULT_CACHE_POOL_CHUNK_SIZE * 2;
|
||||
}
|
||||
|
||||
uint64_t get_default_allocation_cache_pool_max_chunks_CFG(struct cmd_context *cmd, struct profile *profile)
|
||||
{
|
||||
static int _warn_max_chunks = 0;
|
||||
/*
|
||||
* TODO: In future may depend on the cache target version,
|
||||
* newer targets may scale better.
|
||||
*/
|
||||
uint64_t default_max_chunks = DEFAULT_CACHE_POOL_MAX_CHUNKS;
|
||||
uint64_t max_chunks = find_config_tree_int(cmd, allocation_cache_pool_max_chunks_CFG, profile);
|
||||
|
||||
if (!max_chunks)
|
||||
max_chunks = default_max_chunks;
|
||||
else if (max_chunks > default_max_chunks)
|
||||
/* Still warn the user when the value is tweaked above recommended level */
|
||||
/* Maybe drop to log_verbose... */
|
||||
log_warn_suppress(_warn_max_chunks++, "WARNING: Configured cache_pool_max_chunks value "
|
||||
FMTu64 " is higher then recommended " FMTu64 ".",
|
||||
max_chunks, default_max_chunks);
|
||||
|
||||
return max_chunks;
|
||||
}
|
||||
|
@@ -48,6 +48,7 @@ struct profile_params {
|
||||
struct profile *global_metadata_profile; /* profile (as given by --metadataprofile cmd arg) that overrides any other VG/LV-based profile */
|
||||
struct dm_list profiles_to_load; /* list of profiles which are only added, but still need to be loaded for any use */
|
||||
struct dm_list profiles; /* list of profiles which are loaded already and which are ready for use */
|
||||
struct profile *shell_profile; /* master profile used in interactive/shell mode */
|
||||
};
|
||||
|
||||
#define CFG_PATH_MAX_LEN 128
|
||||
@@ -98,31 +99,34 @@ typedef union {
|
||||
|
||||
|
||||
/* whether the configuration item name is variable */
|
||||
#define CFG_NAME_VARIABLE 0x001
|
||||
#define CFG_NAME_VARIABLE 0x0001
|
||||
/* whether empty value is allowed */
|
||||
#define CFG_ALLOW_EMPTY 0x002
|
||||
#define CFG_ALLOW_EMPTY 0x0002
|
||||
/* whether the configuration item is for advanced use only */
|
||||
#define CFG_ADVANCED 0x004
|
||||
#define CFG_ADVANCED 0x0004
|
||||
/* whether the configuration item is not officially supported */
|
||||
#define CFG_UNSUPPORTED 0x008
|
||||
#define CFG_UNSUPPORTED 0x0008
|
||||
/* whether the configuration item is customizable by a profile */
|
||||
#define CFG_PROFILABLE 0x010
|
||||
/* whether the configuration item is customizable by a profile */
|
||||
/* and whether it can be attached to VG/LV metadata at the same time
|
||||
#define CFG_PROFILABLE 0x0010
|
||||
/* whether the configuration item is customizable by a profile
|
||||
* and whether it can be attached to VG/LV metadata at the same time
|
||||
* The CFG_PROFILABLE_METADATA flag incorporates CFG_PROFILABLE flag!!! */
|
||||
#define CFG_PROFILABLE_METADATA 0x030
|
||||
#define CFG_PROFILABLE_METADATA 0x0030
|
||||
/* whether the default value is undefned */
|
||||
#define CFG_DEFAULT_UNDEFINED 0x040
|
||||
#define CFG_DEFAULT_UNDEFINED 0x0040
|
||||
/* whether the default value is commented out on output */
|
||||
#define CFG_DEFAULT_COMMENTED 0x080
|
||||
#define CFG_DEFAULT_COMMENTED 0x0080
|
||||
/* whether the default value is calculated during run time */
|
||||
#define CFG_DEFAULT_RUN_TIME 0x100
|
||||
#define CFG_DEFAULT_RUN_TIME 0x0100
|
||||
/* whether the configuration setting is disabled (and hence defaults always used) */
|
||||
#define CFG_DISABLED 0x200
|
||||
#define CFG_DISABLED 0x0200
|
||||
/* whether to print integers in octal form (prefixed by "0") */
|
||||
#define CFG_FORMAT_INT_OCTAL 0x400
|
||||
#define CFG_FORMAT_INT_OCTAL 0x0400
|
||||
/* whether to disable checks for the whole config section subtree */
|
||||
#define CFG_SECTION_NO_CHECK 0x800
|
||||
#define CFG_SECTION_NO_CHECK 0x0800
|
||||
/* whether to disallow a possibility to override configuration
|
||||
* setting for commands run interactively (e.g. in lvm shell) */
|
||||
#define CFG_DISALLOW_INTERACTIVE 0x1000
|
||||
|
||||
/* configuration definition item structure */
|
||||
typedef struct cfg_def_item {
|
||||
@@ -212,11 +216,15 @@ struct cft_check_handle {
|
||||
unsigned check_diff:1; /* check if the value used differs from default one */
|
||||
unsigned ignoreadvanced:1; /* do not include advnced configs */
|
||||
unsigned ignoreunsupported:1; /* do not include unsupported configs */
|
||||
uint16_t disallowed_flags; /* set of disallowed flags */
|
||||
uint8_t status[CFG_COUNT]; /* flags for each configuration item - the result of the check */
|
||||
};
|
||||
|
||||
int config_def_get_path(char *buf, size_t buf_size, int id);
|
||||
/* Checks config using given handle - the handle may be reused. */
|
||||
int config_def_check(struct cft_check_handle *handle);
|
||||
/* Forces config check and automatically creates a new handle inside with defaults and discards the handle after the check. */
|
||||
int config_force_check(struct cmd_context *cmd, config_source_t source, struct dm_config_tree *cft);
|
||||
|
||||
int override_config_tree_from_string(struct cmd_context *cmd, const char *config_settings);
|
||||
int override_config_tree_from_profile(struct cmd_context *cmd, struct profile *profile);
|
||||
@@ -231,7 +239,7 @@ struct dm_config_tree *config_open(config_source_t source, const char *filename,
|
||||
int config_file_read_fd(struct dm_config_tree *cft, struct device *dev,
|
||||
off_t offset, size_t size, off_t offset2, size_t size2,
|
||||
checksum_fn_t checksum_fn, uint32_t checksum,
|
||||
int skip_parse);
|
||||
int skip_parse, int no_dup_node_check);
|
||||
int config_file_read(struct dm_config_tree *cft);
|
||||
struct dm_config_tree *config_file_open_and_read(const char *config_file, config_source_t source,
|
||||
struct cmd_context *cmd);
|
||||
@@ -299,5 +307,6 @@ int get_default_allocation_cache_pool_chunk_size_CFG(struct cmd_context *cmd, st
|
||||
#define get_default_unconfigured_allocation_cache_pool_chunk_size_CFG NULL
|
||||
const char *get_default_allocation_cache_policy_CFG(struct cmd_context *cmd, struct profile *profile);
|
||||
#define get_default_unconfigured_allocation_cache_policy_CFG NULL
|
||||
uint64_t get_default_allocation_cache_pool_max_chunks_CFG(struct cmd_context *cmd, struct profile *profile);
|
||||
|
||||
#endif
|
||||
|
@@ -23,6 +23,11 @@
|
||||
* - define a configuration array of one or more types:
|
||||
* cfg_array(id, name, parent, flags, types, default_value, since_version, unconfigured_default_value, deprecated_since_version, deprecation_comment, comment)
|
||||
*
|
||||
* - define a configuration setting where the default value is evaluated in runtime
|
||||
* cfg_runtime(id, name, parent, flags, type, since_version, deprecated_since_version, deprecation_comment, comment)
|
||||
* (for each cfg_runtime, you need to define 'get_default_<name>(struct cmd_context *cmd, struct profile *profile)' function
|
||||
* to get the default value in runtime - usually, these functions are placed in config.[ch] file)
|
||||
*
|
||||
*
|
||||
* If default value can't be assigned statically because it depends on some
|
||||
* run-time checks or if it depends on other settings already defined,
|
||||
@@ -52,6 +57,7 @@
|
||||
* CFG_DISABLED - configuration is disabled (defaults always used)
|
||||
* CFG_FORMAT_INT_OCTAL - print integer number in octal form (also prefixed by "0")
|
||||
* CFG_SECTION_NO_CHECK - do not check content of the section at all - use with care!!!
|
||||
* CFG_DISALLOW_INTERACTIVE - disallow configuration node for use in interactive environment (e.g. cmds run in lvm shell)
|
||||
*
|
||||
* type: Allowed type for the value of simple configuation setting, one of:
|
||||
* CFG_TYPE_BOOL
|
||||
@@ -124,7 +130,7 @@ cfg_section(devices_CFG_SECTION, "devices", root_CFG_SECTION, 0, vsn(1, 0, 0), 0
|
||||
cfg_section(allocation_CFG_SECTION, "allocation", root_CFG_SECTION, CFG_PROFILABLE, vsn(2, 2, 77), 0, NULL,
|
||||
"How LVM selects space and applies properties to LVs.\n")
|
||||
|
||||
cfg_section(log_CFG_SECTION, "log", root_CFG_SECTION, 0, vsn(1, 0, 0), 0, NULL,
|
||||
cfg_section(log_CFG_SECTION, "log", root_CFG_SECTION, CFG_PROFILABLE, vsn(1, 0, 0), 0, NULL,
|
||||
"How LVM log information is reported.\n")
|
||||
|
||||
cfg_section(backup_CFG_SECTION, "backup", root_CFG_SECTION, 0, vsn(1, 0, 0), 0, NULL,
|
||||
@@ -166,7 +172,7 @@ cfg(config_checks_CFG, "checks", config_CFG_SECTION, 0, CFG_TYPE_BOOL, 1, vsn(2,
|
||||
cfg(config_abort_on_errors_CFG, "abort_on_errors", config_CFG_SECTION, 0, CFG_TYPE_BOOL, 0, vsn(2,2,99), NULL, 0, NULL,
|
||||
"Abort the LVM process if a configuration mismatch is found.\n")
|
||||
|
||||
cfg_runtime(config_profile_dir_CFG, "profile_dir", config_CFG_SECTION, 0, CFG_TYPE_STRING, vsn(2, 2, 99), 0, NULL,
|
||||
cfg_runtime(config_profile_dir_CFG, "profile_dir", config_CFG_SECTION, CFG_DISALLOW_INTERACTIVE, CFG_TYPE_STRING, vsn(2, 2, 99), 0, NULL,
|
||||
"Directory where LVM looks for configuration profiles.\n")
|
||||
|
||||
cfg(devices_dir_CFG, "dir", devices_CFG_SECTION, CFG_ADVANCED, CFG_TYPE_STRING, DEFAULT_DEV_DIR, vsn(1, 0, 0), NULL, 0, NULL,
|
||||
@@ -395,6 +401,18 @@ cfg(devices_issue_discards_CFG, "issue_discards", devices_CFG_SECTION, 0, CFG_TY
|
||||
"generally do. If enabled, discards will only be issued if both the\n"
|
||||
"storage and kernel provide support.\n")
|
||||
|
||||
cfg(devices_allow_changes_with_duplicate_pvs_CFG, "allow_changes_with_duplicate_pvs", devices_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_ALLOW_CHANGES_WITH_DUPLICATE_PVS, vsn(2, 2, 153), NULL, 0, NULL,
|
||||
"Allow VG modification while a PV appears on multiple devices.\n"
|
||||
"When a PV appears on multiple devices, LVM attempts to choose the\n"
|
||||
"best device to use for the PV. If the devices represent the same\n"
|
||||
"underlying storage, the choice has minimal consequence. If the\n"
|
||||
"devices represent different underlying storage, the wrong choice\n"
|
||||
"can result in data loss if the VG is modified. Disabling this\n"
|
||||
"setting is the safest option because it prevents modifying a VG\n"
|
||||
"or activating LVs in it while a PV appears on multiple devices.\n"
|
||||
"Enabling this setting allows the VG to be used as usual even with\n"
|
||||
"uncertain devices.\n")
|
||||
|
||||
cfg_array(allocation_cling_tag_list_CFG, "cling_tag_list", allocation_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(2, 2, 77), NULL, 0, NULL,
|
||||
"Advise LVM which PVs to use when searching for new space.\n"
|
||||
"When searching for free space to extend an LV, the 'cling' allocation\n"
|
||||
@@ -449,6 +467,12 @@ cfg(allocation_mirror_logs_require_separate_pvs_CFG, "mirror_logs_require_separa
|
||||
"Mirror logs and images will always use different PVs.\n"
|
||||
"The default setting changed in version 2.02.85.\n")
|
||||
|
||||
cfg(allocation_raid_stripe_all_devices_CFG, "raid_stripe_all_devices", allocation_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_ALLOCATION_STRIPE_ALL_DEVICES, vsn(2, 2, 162), NULL, 0, NULL,
|
||||
"Stripe across all PVs when RAID stripes are not specified.\n"
|
||||
"If enabled, all PVs in the VG or on the command line are used for raid0/4/5/6/10\n"
|
||||
"when the command does not specify the number of stripes to use.\n"
|
||||
"This was the default behaviour until release 2.02.162.\n")
|
||||
|
||||
cfg(allocation_cache_pool_metadata_require_separate_pvs_CFG, "cache_pool_metadata_require_separate_pvs", allocation_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_CACHE_POOL_METADATA_REQUIRE_SEPARATE_PVS, vsn(2, 2, 106), NULL, 0, NULL,
|
||||
"Cache pool metadata and data will always use different PVs.\n")
|
||||
|
||||
@@ -492,6 +516,11 @@ cfg_runtime(allocation_cache_pool_chunk_size_CFG, "cache_pool_chunk_size", alloc
|
||||
"on the smaller end of the spectrum. Supported values range from\n"
|
||||
"32KiB to 1GiB in multiples of 32.\n")
|
||||
|
||||
cfg(allocation_cache_pool_max_chunks_CFG, "cache_pool_max_chunks", allocation_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_UNDEFINED, CFG_TYPE_INT, 0, vsn(2, 2, 165), NULL, 0, NULL,
|
||||
"The maximum number of chunks in a cache pool.\n"
|
||||
"For cache target v1.9 the recommended maximumm is 1000000 chunks.\n"
|
||||
"Using cache pool with more chunks may degrade cache performance.\n")
|
||||
|
||||
cfg(allocation_thin_pool_metadata_require_separate_pvs_CFG, "thin_pool_metadata_require_separate_pvs", allocation_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_THIN_POOL_METADATA_REQUIRE_SEPARATE_PVS, vsn(2, 2, 89), NULL, 0, NULL,
|
||||
"Thin pool metdata and data will always use different PVs.\n")
|
||||
|
||||
@@ -535,6 +564,47 @@ cfg_runtime(allocation_thin_pool_chunk_size_CFG, "thin_pool_chunk_size", allocat
|
||||
cfg(allocation_physical_extent_size_CFG, "physical_extent_size", allocation_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_EXTENT_SIZE, vsn(2, 2, 112), NULL, 0, NULL,
|
||||
"Default physical extent size in KiB to use for new VGs.\n")
|
||||
|
||||
cfg(log_report_command_log_CFG, "report_command_log", log_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED | CFG_DISALLOW_INTERACTIVE, CFG_TYPE_BOOL, DEFAULT_COMMAND_LOG_REPORT, vsn(2, 2, 158), NULL, 0, NULL,
|
||||
"Enable or disable LVM log reporting.\n"
|
||||
"If enabled, LVM will collect a log of operations, messages,\n"
|
||||
"per-object return codes with object identification and associated\n"
|
||||
"error numbers (errnos) during LVM command processing. Then the\n"
|
||||
"log is either reported solely or in addition to any existing\n"
|
||||
"reports, depending on LVM command used. If it is a reporting command\n"
|
||||
"(e.g. pvs, vgs, lvs, lvm fullreport), then the log is reported in\n"
|
||||
"addition to any existing reports. Otherwise, there's only log report\n"
|
||||
"on output. For all applicable LVM commands, you can request that\n"
|
||||
"the output has only log report by using --logonly command line\n"
|
||||
"option. Use log/command_log_cols and log/command_log_sort settings\n"
|
||||
"to define fields to display and sort fields for the log report.\n"
|
||||
"You can also use log/command_log_selection to define selection\n"
|
||||
"criteria used each time the log is reported.\n")
|
||||
|
||||
cfg(log_command_log_sort_CFG, "command_log_sort", log_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED | CFG_DISALLOW_INTERACTIVE, CFG_TYPE_STRING, DEFAULT_COMMAND_LOG_SORT, vsn(2, 2, 158), NULL, 0, NULL,
|
||||
"List of columns to sort by when reporting command log.\n"
|
||||
"See <lvm command> --logonly --configreport log -o help\n"
|
||||
"for the list of possible fields.\n")
|
||||
|
||||
cfg(log_command_log_cols_CFG, "command_log_cols", log_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED | CFG_DISALLOW_INTERACTIVE, CFG_TYPE_STRING, DEFAULT_COMMAND_LOG_COLS, vsn(2, 2, 158), NULL, 0, NULL,
|
||||
"List of columns to report when reporting command log.\n"
|
||||
"See <lvm command> --logonly --configreport log -o help\n"
|
||||
"for the list of possible fields.\n")
|
||||
|
||||
cfg(log_command_log_selection_CFG, "command_log_selection", log_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED | CFG_DISALLOW_INTERACTIVE, CFG_TYPE_STRING, DEFAULT_COMMAND_LOG_SELECTION, vsn(2, 2, 158), NULL, 0, NULL,
|
||||
"Selection criteria used when reporting command log.\n"
|
||||
"You can define selection criteria that are applied each\n"
|
||||
"time log is reported. This way, it is possible to control the\n"
|
||||
"amount of log that is displayed on output and you can select\n"
|
||||
"only parts of the log that are important for you. To define\n"
|
||||
"selection criteria, use fields from log report. See also\n"
|
||||
"<lvm command> --logonly --configreport log -S help for the\n"
|
||||
"list of possible fields and selection operators. You can also\n"
|
||||
"define selection criteria for log report on command line directly\n"
|
||||
"using <lvm command> --configreport log -S <selection criteria>\n"
|
||||
"which has precedence over log/command_log_selection setting.\n"
|
||||
"For more information about selection criteria in general, see\n"
|
||||
"lvm(8) man page.\n")
|
||||
|
||||
cfg(log_verbose_CFG, "verbose", log_CFG_SECTION, 0, CFG_TYPE_INT, DEFAULT_VERBOSE, vsn(1, 0, 0), NULL, 0, NULL,
|
||||
"Controls the messages sent to stdout or stderr.\n")
|
||||
|
||||
@@ -1041,7 +1111,8 @@ cfg(activation_retry_deactivation_CFG, "retry_deactivation", activation_CFG_SECT
|
||||
cfg(activation_missing_stripe_filler_CFG, "missing_stripe_filler", activation_CFG_SECTION, CFG_ADVANCED, CFG_TYPE_STRING, DEFAULT_STRIPE_FILLER, vsn(1, 0, 0), NULL, 0, NULL,
|
||||
"Method to fill missing stripes when activating an incomplete LV.\n"
|
||||
"Using 'error' will make inaccessible parts of the device return I/O\n"
|
||||
"errors on access. You can instead use a device path, in which case,\n"
|
||||
"errors on access. Using 'zero' will return success (and zero) on I/O\n"
|
||||
"You can instead use a device path, in which case,\n"
|
||||
"that device will be used in place of missing stripes. Using anything\n"
|
||||
"other than 'error' with mirrored or snapshotted volumes is likely to\n"
|
||||
"result in data corruption.\n")
|
||||
@@ -1450,6 +1521,20 @@ cfg(disk_area_start_sector_CFG, "start_sector", disk_area_CFG_SUBSECTION, CFG_UN
|
||||
cfg(disk_area_size_CFG, "size", disk_area_CFG_SUBSECTION, CFG_UNSUPPORTED | CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, 0, vsn(1, 0, 0), NULL, 0, NULL, NULL)
|
||||
cfg(disk_area_id_CFG, "id", disk_area_CFG_SUBSECTION, CFG_UNSUPPORTED | CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL, 0, NULL, NULL)
|
||||
|
||||
cfg(report_output_format_CFG, "output_format", report_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED | CFG_DISALLOW_INTERACTIVE, CFG_TYPE_STRING, DEFAULT_REP_OUTPUT_FORMAT, vsn(2, 2, 158), NULL, 0, NULL,
|
||||
"Format of LVM command's report output.\n"
|
||||
"If there is more than one report per command, then the format\n"
|
||||
"is applied for all reports. You can also change output format\n"
|
||||
"directly on command line using --reportformat option which\n"
|
||||
"has precedence over log/output_format setting.\n"
|
||||
"Accepted values:\n"
|
||||
" basic\n"
|
||||
" Original format with columns and rows. If there is more than\n"
|
||||
" one report per command, each report is prefixed with report's\n"
|
||||
" name for identification.\n"
|
||||
" json\n"
|
||||
" JSON format.\n")
|
||||
|
||||
cfg(report_compact_output_CFG, "compact_output", report_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_REP_COMPACT_OUTPUT, vsn(2, 2, 115), NULL, 0, NULL,
|
||||
"Do not print empty values for all report fields.\n"
|
||||
"If enabled, all fields that don't have a value set for any of the\n"
|
||||
@@ -1492,7 +1577,7 @@ cfg(report_prefixes_CFG, "prefixes", report_CFG_SECTION, CFG_PROFILABLE | CFG_DE
|
||||
cfg(report_quoted_CFG, "quoted", report_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_REP_QUOTED, vsn(2, 2, 39), NULL, 0, NULL,
|
||||
"Quote field values when using field name prefixes.\n")
|
||||
|
||||
cfg(report_colums_as_rows_CFG, "colums_as_rows", report_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_REP_COLUMNS_AS_ROWS, vsn(1, 0, 0), NULL, 0, NULL,
|
||||
cfg(report_columns_as_rows_CFG, "columns_as_rows", report_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_REP_COLUMNS_AS_ROWS, vsn(1, 0, 0), NULL, 0, NULL,
|
||||
"Output each column as a row.\n"
|
||||
"If set, this also implies report/prefixes=1.\n")
|
||||
|
||||
@@ -1706,6 +1791,46 @@ cfg(report_pvsegs_cols_verbose_CFG, "pvsegs_cols_verbose", report_CFG_SECTION, C
|
||||
"List of columns to sort by when reporting 'pvs --segments' command in verbose mode.\n"
|
||||
"See 'pvs --segments -o help' for the list of possible fields.\n")
|
||||
|
||||
cfg(report_vgs_cols_full_CFG, "vgs_cols_full", report_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_VGS_COLS_FULL, vsn(2, 2, 158), NULL, 0, NULL,
|
||||
"List of columns to report for lvm fullreport's 'vgs' subreport.\n"
|
||||
"See 'vgs -o help' for the list of possible fields.\n")
|
||||
|
||||
cfg(report_pvs_cols_full_CFG, "pvs_cols_full", report_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_PVS_COLS_FULL, vsn(2, 2, 158), NULL, 0, NULL,
|
||||
"List of columns to report for lvm fullreport's 'vgs' subreport.\n"
|
||||
"See 'pvs -o help' for the list of possible fields.\n")
|
||||
|
||||
cfg(report_lvs_cols_full_CFG, "lvs_cols_full", report_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_LVS_COLS_FULL, vsn(2, 2, 158), NULL, 0, NULL,
|
||||
"List of columns to report for lvm fullreport's 'lvs' subreport.\n"
|
||||
"See 'lvs -o help' for the list of possible fields.\n")
|
||||
|
||||
cfg(report_pvsegs_cols_full_CFG, "pvsegs_cols_full", report_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_PVSEGS_COLS_FULL, vsn(2, 2, 158), NULL, 0, NULL,
|
||||
"List of columns to report for lvm fullreport's 'pvseg' subreport.\n"
|
||||
"See 'pvs --segments -o help' for the list of possible fields.\n")
|
||||
|
||||
cfg(report_segs_cols_full_CFG, "segs_cols_full", report_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_SEGS_COLS_FULL, vsn(2, 2, 158), NULL, 0, NULL,
|
||||
"List of columns to report for lvm fullreport's 'seg' subreport.\n"
|
||||
"See 'lvs --segments -o help' for the list of possible fields.\n")
|
||||
|
||||
cfg(report_vgs_sort_full_CFG, "vgs_sort_full", report_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_VGS_SORT_FULL, vsn(2, 2, 158), NULL, 0, NULL,
|
||||
"List of columns to sort by when reporting lvm fullreport's 'vgs' subreport.\n"
|
||||
"See 'vgs -o help' for the list of possible fields.\n")
|
||||
|
||||
cfg(report_pvs_sort_full_CFG, "pvs_sort_full", report_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_PVS_SORT_FULL, vsn(2, 2, 158), NULL, 0, NULL,
|
||||
"List of columns to sort by when reporting lvm fullreport's 'vgs' subreport.\n"
|
||||
"See 'pvs -o help' for the list of possible fields.\n")
|
||||
|
||||
cfg(report_lvs_sort_full_CFG, "lvs_sort_full", report_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_LVS_SORT_FULL, vsn(2, 2, 158), NULL, 0, NULL,
|
||||
"List of columns to sort by when reporting lvm fullreport's 'lvs' subreport.\n"
|
||||
"See 'lvs -o help' for the list of possible fields.\n")
|
||||
|
||||
cfg(report_pvsegs_sort_full_CFG, "pvsegs_sort_full", report_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_PVSEGS_SORT_FULL, vsn(2, 2, 158), NULL, 0, NULL,
|
||||
"List of columns to sort by when reporting for lvm fullreport's 'pvseg' subreport.\n"
|
||||
"See 'pvs --segments -o help' for the list of possible fields.\n")
|
||||
|
||||
cfg(report_segs_sort_full_CFG, "segs_sort_full", report_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, DEFAULT_SEGS_SORT_FULL, vsn(2, 2, 158), NULL, 0, NULL,
|
||||
"List of columns to sort by when reporting lvm fullreport's 'seg' subreport.\n"
|
||||
"See 'lvs --segments -o help' for the list of possible fields.\n")
|
||||
|
||||
cfg(report_mark_hidden_devices_CFG, "mark_hidden_devices", report_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, 1, vsn(2, 2, 140), NULL, 0, NULL,
|
||||
"Use brackets [] to mark hidden devices.\n")
|
||||
|
||||
|
@@ -45,6 +45,7 @@
|
||||
#define DEFAULT_DATA_ALIGNMENT_DETECTION 1
|
||||
#define DEFAULT_ISSUE_DISCARDS 0
|
||||
#define DEFAULT_PV_MIN_SIZE_KB 2048
|
||||
#define DEFAULT_ALLOW_CHANGES_WITH_DUPLICATE_PVS 0
|
||||
|
||||
#define DEFAULT_LOCKING_LIB "liblvm2clusterlock.so"
|
||||
#define DEFAULT_ERROR_WHEN_FULL 0
|
||||
@@ -65,7 +66,14 @@
|
||||
#define DEFAULT_MIRROR_LOG_FAULT_POLICY "allocate"
|
||||
#define DEFAULT_MIRROR_IMAGE_FAULT_POLICY "remove"
|
||||
#define DEFAULT_MIRROR_MAX_IMAGES 8 /* limited by kernel DM_KCOPYD_MAX_REGIONS */
|
||||
#define DEFAULT_RAID_MAX_IMAGES 8
|
||||
/* Limited by kernel failed devices bitfield in superblock (raid4/5/6 MD max 253) */
|
||||
/*
|
||||
* FIXME: Increase these to 64 and further to the MD maximum
|
||||
* once the SubLVs split and name shift got enhanced
|
||||
*/
|
||||
#define DEFAULT_RAID1_MAX_IMAGES 10
|
||||
#define DEFAULT_RAID_MAX_IMAGES 64
|
||||
#define DEFAULT_ALLOCATION_STRIPE_ALL_DEVICES 0 /* Don't stripe across all devices if not -i/--stripes given */
|
||||
|
||||
#define DEFAULT_RAID_FAULT_POLICY "warn"
|
||||
|
||||
@@ -119,6 +127,7 @@
|
||||
#define DEFAULT_CACHE_REPAIR_OPTIONS_CONFIG "#S" DEFAULT_CACHE_REPAIR_OPTION1
|
||||
#define DEFAULT_CACHE_POOL_METADATA_REQUIRE_SEPARATE_PVS 0
|
||||
#define DEFAULT_CACHE_POOL_CHUNK_SIZE 64 /* KB */
|
||||
#define DEFAULT_CACHE_POOL_MAX_CHUNKS 1000000
|
||||
#define DEFAULT_CACHE_POOL_MIN_METADATA_SIZE 2048 /* KB */
|
||||
#define DEFAULT_CACHE_POOL_MAX_METADATA_SIZE (16 * 1024 * 1024) /* KB */
|
||||
#define DEFAULT_CACHE_POLICY "mq"
|
||||
@@ -159,6 +168,7 @@
|
||||
# define DEFAULT_LOG_FACILITY LOG_USER
|
||||
#endif
|
||||
|
||||
#define DEFAULT_COMMAND_LOG_REPORT 0
|
||||
#define DEFAULT_SYSLOG 1
|
||||
#define DEFAULT_VERBOSE 0
|
||||
#define DEFAULT_SILENT 0
|
||||
@@ -206,14 +216,18 @@
|
||||
#define DEFAULT_REP_LIST_ITEM_SEPARATOR ","
|
||||
#define DEFAULT_TIME_FORMAT "%Y-%m-%d %T %z"
|
||||
|
||||
#define DEFAULT_REP_OUTPUT_FORMAT "basic"
|
||||
#define DEFAULT_COMPACT_OUTPUT_COLS ""
|
||||
|
||||
#define DEFAULT_COMMAND_LOG_SELECTION "!(log_type=status && message=success)"
|
||||
|
||||
#define DEFAULT_LVS_COLS "lv_name,vg_name,lv_attr,lv_size,pool_lv,origin,data_percent,metadata_percent,move_pv,mirror_log,copy_percent,convert_lv"
|
||||
#define DEFAULT_VGS_COLS "vg_name,pv_count,lv_count,snap_count,vg_attr,vg_size,vg_free"
|
||||
#define DEFAULT_PVS_COLS "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free"
|
||||
#define DEFAULT_SEGS_COLS "lv_name,vg_name,lv_attr,stripes,segtype,seg_size"
|
||||
#define DEFAULT_PVSEGS_COLS "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size"
|
||||
#define DEFAULT_DEVTYPES_COLS "devtype_name,devtype_max_partitions,devtype_description"
|
||||
#define DEFAULT_COMMAND_LOG_COLS "log_seq_num,log_type,log_context,log_object_type,log_object_name,log_object_id,log_object_group,log_object_group_id,log_message,log_errno,log_ret_code"
|
||||
|
||||
#define DEFAULT_LVS_COLS_VERB "lv_name,vg_name,seg_count,lv_attr,lv_size,lv_major,lv_minor,lv_kernel_major,lv_kernel_minor,pool_lv,origin,data_percent,metadata_percent,move_pv,copy_percent,mirror_log,convert_lv,lv_uuid,lv_profile"
|
||||
#define DEFAULT_VGS_COLS_VERB "vg_name,vg_attr,vg_extent_size,pv_count,lv_count,snap_count,vg_size,vg_free,vg_uuid,vg_profile"
|
||||
@@ -222,12 +236,25 @@
|
||||
#define DEFAULT_PVSEGS_COLS_VERB "pv_name,vg_name,pv_fmt,pv_attr,pv_size,pv_free,pvseg_start,pvseg_size,lv_name,seg_start_pe,segtype,seg_pe_ranges"
|
||||
#define DEFAULT_DEVTYPES_COLS_VERB "devtype_name,devtype_max_partitions,devtype_description"
|
||||
|
||||
#define DEFAULT_VGS_COLS_FULL "vg_all"
|
||||
#define DEFAULT_PVS_COLS_FULL "pv_all"
|
||||
#define DEFAULT_LVS_COLS_FULL "lv_all"
|
||||
#define DEFAULT_PVSEGS_COLS_FULL "pvseg_all,pv_uuid,lv_uuid"
|
||||
#define DEFAULT_SEGS_COLS_FULL "seg_all,lv_uuid"
|
||||
|
||||
#define DEFAULT_LVS_SORT "vg_name,lv_name"
|
||||
#define DEFAULT_VGS_SORT "vg_name"
|
||||
#define DEFAULT_PVS_SORT "pv_name"
|
||||
#define DEFAULT_SEGS_SORT "vg_name,lv_name,seg_start"
|
||||
#define DEFAULT_PVSEGS_SORT "pv_name,pvseg_start"
|
||||
#define DEFAULT_DEVTYPES_SORT "devtype_name"
|
||||
#define DEFAULT_COMMAND_LOG_SORT "log_seq_num"
|
||||
|
||||
#define DEFAULT_VGS_SORT_FULL "vg_name"
|
||||
#define DEFAULT_PVS_SORT_FULL "pv_name"
|
||||
#define DEFAULT_LVS_SORT_FULL "vg_name,lv_name"
|
||||
#define DEFAULT_PVSEGS_SORT_FULL "pv_uuid,pvseg_start"
|
||||
#define DEFAULT_SEGS_SORT_FULL "lv_uuid,seg_start"
|
||||
|
||||
#define DEFAULT_MIRROR_DEVICE_FAULT_POLICY "remove"
|
||||
#define DEFAULT_MIRROR_LOG_FAULT_POLICY "allocate"
|
||||
|
@@ -36,6 +36,9 @@
|
||||
#define DEV_EXT_UDEV_DEVTYPE "DEVTYPE"
|
||||
#define DEV_EXT_UDEV_DEVTYPE_DISK "disk"
|
||||
|
||||
/* the list of symlinks associated with device node */
|
||||
#define DEV_EXT_UDEV_DEVLINKS "DEVLINKS"
|
||||
|
||||
/*
|
||||
* DEV_EXT_UDEV_MPATH_DEVICE_PATH is set by multipath in udev db
|
||||
* with value either 0 or 1. The same functionality as
|
||||
|
@@ -494,11 +494,22 @@ int dev_open_flags(struct device *dev, int flags, int direct, int quiet)
|
||||
|
||||
#ifdef O_NOATIME
|
||||
/* Don't update atime on device inodes */
|
||||
if (!(dev->flags & DEV_REGULAR))
|
||||
if (!(dev->flags & DEV_REGULAR) && !(dev->flags & DEV_NOT_O_NOATIME))
|
||||
flags |= O_NOATIME;
|
||||
#endif
|
||||
|
||||
if ((dev->fd = open(name, flags, 0777)) < 0) {
|
||||
#ifdef O_NOATIME
|
||||
if ((errno == EPERM) && (flags & O_NOATIME)) {
|
||||
flags &= ~O_NOATIME;
|
||||
dev->flags |= DEV_NOT_O_NOATIME;
|
||||
if ((dev->fd = open(name, flags, 0777)) >= 0) {
|
||||
log_debug_devs("%s: Not using O_NOATIME", name);
|
||||
goto opened;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef O_DIRECT_SUPPORT
|
||||
if (direct && !(dev->flags & DEV_O_DIRECT_TESTED)) {
|
||||
flags &= ~O_DIRECT;
|
||||
|
@@ -125,6 +125,9 @@ struct dev_types *create_dev_types(const char *proc_dir,
|
||||
if (!strncmp("emcpower", line + i, 8) && isspace(*(line + i + 8)))
|
||||
dt->emcpower_major = line_maj;
|
||||
|
||||
if (!strncmp("loop", line + i, 4) && isspace(*(line + i + 4)))
|
||||
dt->loop_major = line_maj;
|
||||
|
||||
if (!strncmp("power2", line + i, 6) && isspace(*(line + i + 6)))
|
||||
dt->power2_major = line_maj;
|
||||
|
||||
@@ -246,6 +249,9 @@ const char *dev_subsystem_name(struct dev_types *dt, struct device *dev)
|
||||
if (MAJOR(dev->dev) == dt->blkext_major)
|
||||
return "BLKEXT";
|
||||
|
||||
if (MAJOR(dev->dev) == dt->loop_major)
|
||||
return "LOOP";
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -265,6 +271,38 @@ int major_is_scsi_device(struct dev_types *dt, int major)
|
||||
return (dt->dev_type_array[major].flags & PARTITION_SCSI_DEVICE) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
static int _loop_is_with_partscan(struct device *dev)
|
||||
{
|
||||
FILE *fp;
|
||||
int partscan = 0;
|
||||
char path[PATH_MAX];
|
||||
char buffer[64];
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%sdev/block/%d:%d/loop/partscan",
|
||||
dm_sysfs_dir(),
|
||||
(int) MAJOR(dev->dev),
|
||||
(int) MINOR(dev->dev)) < 0) {
|
||||
log_warn("Sysfs path for partscan is too long.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(fp = fopen(path, "r")))
|
||||
return 0; /* not there -> no partscan */
|
||||
|
||||
if (!fgets(buffer, sizeof(buffer), fp)) {
|
||||
log_warn("Failed to read %s.", path);
|
||||
} else if (sscanf(buffer, "%d", &partscan) != 1) {
|
||||
log_warn("Failed to parse %s '%s'.", path, buffer);
|
||||
partscan = 0;
|
||||
}
|
||||
|
||||
if (fclose(fp))
|
||||
log_sys_debug("fclose", path);
|
||||
|
||||
return partscan;
|
||||
}
|
||||
|
||||
/* See linux/genhd.h and fs/partitions/msdos */
|
||||
#define PART_MAGIC 0xAA55
|
||||
#define PART_MAGIC_OFFSET UINT64_C(0x1FE)
|
||||
@@ -294,6 +332,11 @@ static int _is_partitionable(struct dev_types *dt, struct device *dev)
|
||||
if (MAJOR(dev->dev) == dt->md_major)
|
||||
return 1;
|
||||
|
||||
/* All loop devices are partitionable via blkext (as of 3.2) */
|
||||
if ((MAJOR(dev->dev) == dt->loop_major) &&
|
||||
_loop_is_with_partscan(dev))
|
||||
return 1;
|
||||
|
||||
if ((parts <= 1) || (MINOR(dev->dev) % parts))
|
||||
return 0;
|
||||
|
||||
@@ -933,3 +976,100 @@ int dev_is_rotational(struct dev_types *dt, struct device *dev)
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef UDEV_SYNC_SUPPORT
|
||||
|
||||
/*
|
||||
* Udev daemon usually has 30s timeout to process each event by default.
|
||||
* But still, that value can be changed in udev configuration and we
|
||||
* don't have libudev API to read the actual timeout value used.
|
||||
*/
|
||||
|
||||
/* FIXME: Is this long enough to wait for udev db to get initialized?
|
||||
*
|
||||
* Take also into consideration that this check is done for each
|
||||
* device that is scanned so we don't want to wait for a long time
|
||||
* if there's something wrong with udev, e.g. timeouts! With current
|
||||
* libudev API, we can't recognize whether the event processing has
|
||||
* not finished yet and it's still being processed or whether it has
|
||||
* failed already due to timeout in udev - in both cases the
|
||||
* udev_device_get_is_initialized returns 0.
|
||||
*/
|
||||
#define UDEV_DEV_IS_MPATH_COMPONENT_ITERATION_COUNT 100
|
||||
#define UDEV_DEV_IS_MPATH_COMPONENT_USLEEP 100000
|
||||
|
||||
int udev_dev_is_mpath_component(struct device *dev)
|
||||
{
|
||||
struct udev *udev_context = udev_get_library_context();
|
||||
struct udev_device *udev_device = NULL;
|
||||
const char *value;
|
||||
int initialized = 0;
|
||||
unsigned i = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (!udev_context) {
|
||||
log_warn("WARNING: No udev context available to check if device %s is multipath component.", dev_name(dev));
|
||||
return_0;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (i >= UDEV_DEV_IS_MPATH_COMPONENT_ITERATION_COUNT)
|
||||
break;
|
||||
|
||||
if (udev_device)
|
||||
udev_device_unref(udev_device);
|
||||
|
||||
if (!(udev_device = udev_device_new_from_devnum(udev_context, 'b', dev->dev))) {
|
||||
log_warn("WARNING: Failed to get udev device handler for device %s.", dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBUDEV_UDEV_DEVICE_GET_IS_INITIALIZED
|
||||
if ((initialized = udev_device_get_is_initialized(udev_device)))
|
||||
break;
|
||||
#else
|
||||
if ((initialized = (udev_device_get_property_value(udev_device, DEV_EXT_UDEV_DEVLINKS) != NULL)))
|
||||
break;
|
||||
#endif
|
||||
|
||||
log_debug("Device %s not initialized in udev database (%u/%u, %u microseconds).", dev_name(dev),
|
||||
i + 1, UDEV_DEV_IS_MPATH_COMPONENT_ITERATION_COUNT,
|
||||
i * UDEV_DEV_IS_MPATH_COMPONENT_USLEEP);
|
||||
|
||||
usleep(UDEV_DEV_IS_MPATH_COMPONENT_USLEEP);
|
||||
i++;
|
||||
}
|
||||
|
||||
if (!initialized) {
|
||||
log_warn("WARNING: Device %s not initialized in udev database even after waiting %u microseconds.",
|
||||
dev_name(dev), i * UDEV_DEV_IS_MPATH_COMPONENT_USLEEP);
|
||||
goto out;
|
||||
}
|
||||
|
||||
value = udev_device_get_property_value(udev_device, DEV_EXT_UDEV_BLKID_TYPE);
|
||||
if (value && !strcmp(value, DEV_EXT_UDEV_BLKID_TYPE_MPATH)) {
|
||||
log_debug("Device %s is multipath component based on blkid variable in udev db (%s=\"%s\").",
|
||||
dev_name(dev), DEV_EXT_UDEV_BLKID_TYPE, value);
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
value = udev_device_get_property_value(udev_device, DEV_EXT_UDEV_MPATH_DEVICE_PATH);
|
||||
if (value && !strcmp(value, "1")) {
|
||||
log_debug("Device %s is multipath component based on multipath variable in udev db (%s=\"%s\").",
|
||||
dev_name(dev), DEV_EXT_UDEV_MPATH_DEVICE_PATH, value);
|
||||
ret = 1;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
udev_device_unref(udev_device);
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
|
||||
int udev_dev_is_mpath_component(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -43,6 +43,7 @@ struct dev_types {
|
||||
int emcpower_major;
|
||||
int power2_major;
|
||||
int dasd_major;
|
||||
int loop_major;
|
||||
struct dev_type_def dev_type_array[NUMBER_OF_MAJORS];
|
||||
};
|
||||
|
||||
@@ -58,6 +59,7 @@ int dev_is_md(struct device *dev, uint64_t *sb);
|
||||
int dev_is_swap(struct device *dev, uint64_t *signature);
|
||||
int dev_is_luks(struct device *dev, uint64_t *signature);
|
||||
int dasd_is_cdl_formatted(struct device *dev);
|
||||
int udev_dev_is_mpath_component(struct device *dev);
|
||||
|
||||
/* Signature wiping. */
|
||||
#define TYPE_LVM1_MEMBER 0x001
|
||||
|
@@ -30,6 +30,7 @@
|
||||
#define DEV_OPEN_FAILURE 0x00000080 /* Has last open failed? */
|
||||
#define DEV_USED_FOR_LV 0x00000100 /* Is device used for an LV */
|
||||
#define DEV_ASSUMED_FOR_LV 0x00000200 /* Is device assumed for an LV */
|
||||
#define DEV_NOT_O_NOATIME 0x00000400 /* Don't use O_NOATIME */
|
||||
|
||||
/*
|
||||
* Support for external device info.
|
||||
|
@@ -831,50 +831,102 @@ void display_name_error(name_error_t name_error)
|
||||
* Prompt for y or n from stdin.
|
||||
* Defaults to 'no' in silent mode.
|
||||
* All callers should support --yes and/or --force to override this.
|
||||
*
|
||||
* Accepted are either _yes[] or _no[] strings or just their outset.
|
||||
* When running without 'tty' stdin is printed to stderr.
|
||||
* 'Yes' is accepted ONLY with '\n'.
|
||||
*/
|
||||
char yes_no_prompt(const char *prompt, ...)
|
||||
{
|
||||
int c = 0, ret = 0, cb = 0;
|
||||
/* Lowercase Yes/No strings */
|
||||
static const char _yes[] = "yes";
|
||||
static const char _no[] = "no";
|
||||
const char *answer = NULL;
|
||||
int c = silent_mode() ? EOF : 0;
|
||||
int i = 0, ret = 0, sig = 0;
|
||||
char buf[12];
|
||||
va_list ap;
|
||||
|
||||
sigint_allow();
|
||||
do {
|
||||
if (c == '\n' || !c) {
|
||||
|
||||
for (;;) {
|
||||
if (!ret) {
|
||||
/* Show prompt */
|
||||
va_start(ap, prompt);
|
||||
vfprintf(stderr, prompt, ap);
|
||||
va_end(ap);
|
||||
fflush(stderr);
|
||||
if (silent_mode()) {
|
||||
fputc('n', stderr);
|
||||
ret = 'n';
|
||||
|
||||
if (c == EOF)
|
||||
break;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
i = 0;
|
||||
answer = NULL;
|
||||
}
|
||||
|
||||
nextchar:
|
||||
if ((sig = sigint_caught()))
|
||||
break; /* Check if already interrupted before getchar() */
|
||||
|
||||
if ((c = getchar()) == EOF) {
|
||||
ret = 'n'; /* SIGINT */
|
||||
cb = 1;
|
||||
break;
|
||||
/* SIGNAL or no chars on stdin (missing '\n') or ^D */
|
||||
if (!i)
|
||||
break; /* Just shown prompt,-> print [n]\n */
|
||||
|
||||
goto invalid; /* Note: c holds EOF */
|
||||
}
|
||||
|
||||
if ((i < (sizeof(buf) - 4)) && isprint(c))
|
||||
buf[i++] = c;
|
||||
|
||||
c = tolower(c);
|
||||
if ((c == 'y') || (c == 'n')) {
|
||||
/* If both 'y' and 'n' given, begin again. */
|
||||
if (ret && c != ret)
|
||||
ret = -1;
|
||||
else
|
||||
ret = c;
|
||||
}
|
||||
} while (ret < 1 || c != '\n');
|
||||
|
||||
if ((ret > 0) && (c == answer[0]))
|
||||
answer++; /* Matching, next char */
|
||||
else if (c == '\n') {
|
||||
if (feof(stdin))
|
||||
fputc('\n', stderr);
|
||||
if (ret > 0)
|
||||
break; /* Answered */
|
||||
invalid:
|
||||
if (i >= (sizeof(buf) - 4)) {
|
||||
/* '...' for missing input */
|
||||
i = sizeof(buf) - 1;
|
||||
buf[i - 1] = buf[i - 2] = buf[i - 3] = '.';
|
||||
}
|
||||
buf[i] = 0;
|
||||
log_warn("WARNING: Invalid input '%s'.", buf);
|
||||
ret = 0; /* Otherwise refresh prompt */
|
||||
} else if (!ret && (c == _yes[0])) {
|
||||
ret = 'y';
|
||||
answer = _yes + 1; /* Expecting 'Yes' */
|
||||
} else if (!ret && (c == _no[0])) {
|
||||
ret = 'n';
|
||||
answer = _no + 1; /* Expecting 'No' */
|
||||
} else if (!ret && isspace(c)) {
|
||||
/* Ignore any whitespace before */
|
||||
--i;
|
||||
goto nextchar;
|
||||
} else if ((ret > 0) && isspace(c)) {
|
||||
/* Ignore any whitespace after */
|
||||
while (*answer)
|
||||
answer++; /* jump to end-of-word */
|
||||
} else
|
||||
ret = -1; /* Read till '\n' and refresh */
|
||||
}
|
||||
|
||||
sigint_restore();
|
||||
|
||||
if (cb && !sigint_caught())
|
||||
fputc(ret, stderr);
|
||||
|
||||
if (c != '\n')
|
||||
fputc('\n', stderr);
|
||||
/* For other then Yes answer check there is really no interrupt */
|
||||
if (sig || sigint_caught()) {
|
||||
stack;
|
||||
ret = 'n';
|
||||
} else if (c == EOF) {
|
||||
fputs("[n]\n", stderr);
|
||||
ret = 'n';
|
||||
} else
|
||||
/* Not knowing if it's terminal, makes this hard.... */
|
||||
log_verbose("Accepted input: [%c]", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
81
lib/filters/filter-internal.c
Normal file
81
lib/filters/filter-internal.c
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2006 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
* 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 Lesser General Public License v.2.1.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser 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
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "filter.h"
|
||||
|
||||
static DM_LIST_INIT(_allow_devs);
|
||||
|
||||
int internal_filter_allow(struct dm_pool *mem, struct device *dev)
|
||||
{
|
||||
struct device_list *devl;
|
||||
|
||||
if (!(devl = dm_pool_alloc(mem, sizeof(*devl)))) {
|
||||
log_error("device_list element allocation failed");
|
||||
return 0;
|
||||
}
|
||||
devl->dev = dev;
|
||||
|
||||
dm_list_add(&_allow_devs, &devl->list);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void internal_filter_clear(void)
|
||||
{
|
||||
dm_list_init(&_allow_devs);
|
||||
}
|
||||
|
||||
static int _passes_internal(struct dev_filter *f __attribute__((unused)),
|
||||
struct device *dev)
|
||||
{
|
||||
struct device_list *devl;
|
||||
|
||||
if (!internal_filtering())
|
||||
return 1;
|
||||
|
||||
dm_list_iterate_items(devl, &_allow_devs) {
|
||||
if (devl->dev == dev)
|
||||
return 1;
|
||||
}
|
||||
|
||||
log_debug_devs("%s: Skipping for internal filtering.", dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _destroy(struct dev_filter *f)
|
||||
{
|
||||
if (f->use_count)
|
||||
log_error(INTERNAL_ERROR "Destroying internal filter while in use %u times.", f->use_count);
|
||||
|
||||
dm_free(f);
|
||||
}
|
||||
|
||||
struct dev_filter *internal_filter_create(void)
|
||||
{
|
||||
struct dev_filter *f;
|
||||
|
||||
if (!(f = dm_zalloc(sizeof(*f)))) {
|
||||
log_error("md filter allocation failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
f->passes_filter = _passes_internal;
|
||||
f->destroy = _destroy;
|
||||
f->use_count = 0;
|
||||
|
||||
log_debug_devs("internal filter initialised.");
|
||||
|
||||
return f;
|
||||
}
|
||||
|
@@ -40,7 +40,7 @@ static int _extract_pattern(struct dm_pool *mem, const char *pat,
|
||||
break;
|
||||
|
||||
default:
|
||||
log_info("pattern must begin with 'a' or 'r'");
|
||||
log_error("Pattern must begin with 'a' or 'r'.");
|
||||
return 0;
|
||||
}
|
||||
pat++;
|
||||
@@ -77,7 +77,7 @@ static int _extract_pattern(struct dm_pool *mem, const char *pat,
|
||||
*/
|
||||
ptr = r + strlen(r) - 1;
|
||||
if (*ptr != sep) {
|
||||
log_info("invalid separator at end of regex");
|
||||
log_error("Invalid separator at end of regex.");
|
||||
return 0;
|
||||
}
|
||||
*ptr = '\0';
|
||||
|
@@ -17,6 +17,7 @@
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <sys/sysmacros.h>
|
||||
#include <dirent.h>
|
||||
|
||||
static int _locate_sysfs_blocks(const char *sysfs_dir, char *path, size_t len,
|
||||
@@ -167,7 +168,7 @@ static int _parse_dev(const char *file, FILE *fp, dev_t *result)
|
||||
}
|
||||
|
||||
if (sscanf(buffer, "%u:%u", &major, &minor) != 2) {
|
||||
log_info("sysfs device file not correct format");
|
||||
log_error("Incorrect format for sysfs device file: %s.", file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -32,6 +32,10 @@ struct dev_filter *persistent_filter_create(struct dev_types *dt,
|
||||
const char *file);
|
||||
struct dev_filter *sysfs_filter_create(void);
|
||||
|
||||
struct dev_filter *internal_filter_create(void);
|
||||
int internal_filter_allow(struct dm_pool *mem, struct device *dev);
|
||||
void internal_filter_clear(void);
|
||||
|
||||
/*
|
||||
* patterns must be an array of strings of the form:
|
||||
* [ra]<sep><regex><sep>, eg,
|
||||
|
@@ -60,7 +60,8 @@ static void _add_pl_to_list(struct cmd_context *cmd, struct dm_list *head, struc
|
||||
if (id_equal(&data->pv_uuid, &pl->pv_uuid)) {
|
||||
char uuid[ID_LEN + 7] __attribute__((aligned(8)));
|
||||
|
||||
id_write_format(&pl->pv_uuid, uuid, ID_LEN + 7);
|
||||
if (!id_write_format(&pl->pv_uuid, uuid, ID_LEN + 7))
|
||||
stack;
|
||||
|
||||
if (!dev_subsystem_part_major(cmd->dev_types, data->dev)) {
|
||||
log_very_verbose("Ignoring duplicate PV %s on "
|
||||
@@ -90,11 +91,13 @@ int read_pool_label(struct pool_list *pl, struct labeller *l,
|
||||
pool_label_in(pd, buf);
|
||||
|
||||
get_pool_pv_uuid(&pvid, pd);
|
||||
id_write_format(&pvid, uuid, ID_LEN + 7);
|
||||
if (!id_write_format(&pvid, uuid, ID_LEN + 7))
|
||||
stack;
|
||||
log_debug_metadata("Calculated uuid %s for %s", uuid, dev_name(dev));
|
||||
|
||||
get_pool_vg_uuid(&vgid, pd);
|
||||
id_write_format(&vgid, uuid, ID_LEN + 7);
|
||||
if (!id_write_format(&vgid, uuid, ID_LEN + 7))
|
||||
stack;
|
||||
log_debug_metadata("Calculated uuid %s for %s", uuid, pd->pl_pool_name);
|
||||
|
||||
if (!(info = lvmcache_add(l, (char *) &pvid, dev, pd->pl_pool_name,
|
||||
|
@@ -181,7 +181,7 @@ static int _add_stripe_seg(struct dm_pool *mem,
|
||||
unsigned j;
|
||||
uint32_t area_len;
|
||||
|
||||
if (usp->striping & (usp->striping - 1)) {
|
||||
if (!is_power_of_2(usp->striping)) {
|
||||
log_error("Stripe size must be a power of 2");
|
||||
return 0;
|
||||
}
|
||||
|
@@ -339,7 +339,12 @@ static int _restore_vg_should_write_pv(struct physical_volume *pv, int do_pvcrea
|
||||
if (!(pv->fmt->features & FMT_PV_FLAGS))
|
||||
return 0;
|
||||
|
||||
if (!(info = lvmcache_info_from_pvid(pv->dev->pvid, 0))) {
|
||||
if (!pv->dev) {
|
||||
log_error("Failed to find device for PV.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(info = lvmcache_info_from_pvid(pv->dev->pvid, pv->dev, 0))) {
|
||||
log_error("Failed to find cached info for PV %s.", pv_dev_name(pv));
|
||||
return -1;
|
||||
}
|
||||
@@ -359,9 +364,7 @@ static int _restore_vg_should_write_pv(struct physical_volume *pv, int do_pvcrea
|
||||
|
||||
/* ORPHAN and VG locks held before calling this */
|
||||
int backup_restore_vg(struct cmd_context *cmd, struct volume_group *vg,
|
||||
int drop_lvmetad,
|
||||
int do_pvcreate,
|
||||
struct pv_create_args *pva)
|
||||
int do_pvcreate, struct pv_create_args *pva)
|
||||
{
|
||||
struct dm_list new_pvs;
|
||||
struct pv_list *pvl, *new_pvl;
|
||||
@@ -502,16 +505,6 @@ int backup_restore_vg(struct cmd_context *cmd, struct volume_group *vg,
|
||||
if (!vg_write(vg))
|
||||
return_0;
|
||||
|
||||
if (drop_lvmetad && lvmetad_used()) {
|
||||
struct volume_group *vg_lvmetad = lvmetad_vg_lookup(cmd, vg->name, NULL);
|
||||
if (vg_lvmetad) {
|
||||
/* FIXME Cope with failure to update lvmetad */
|
||||
if (!lvmetad_vg_remove(vg_lvmetad))
|
||||
stack;
|
||||
release_vg(vg_lvmetad);
|
||||
}
|
||||
}
|
||||
|
||||
if (!vg_commit(vg))
|
||||
return_0;
|
||||
|
||||
@@ -550,7 +543,7 @@ int backup_restore_from_file(struct cmd_context *cmd, const char *vg_name,
|
||||
|
||||
missing_pvs = vg_missing_pv_count(vg);
|
||||
if (missing_pvs == 0)
|
||||
r = backup_restore_vg(cmd, vg, 1, 0, NULL);
|
||||
r = backup_restore_vg(cmd, vg, 0, NULL);
|
||||
else
|
||||
log_error("Cannot restore Volume Group %s with %i PVs "
|
||||
"marked as missing.", vg->name, missing_pvs);
|
||||
|
@@ -53,9 +53,7 @@ struct volume_group *backup_read_vg(struct cmd_context *cmd,
|
||||
const char *vg_name, const char *file);
|
||||
|
||||
int backup_restore_vg(struct cmd_context *cmd, struct volume_group *vg,
|
||||
int drop_lvmetad,
|
||||
int do_pvcreate,
|
||||
struct pv_create_args *pva);
|
||||
int do_pvcreate, struct pv_create_args *pva);
|
||||
|
||||
int backup_restore_from_file(struct cmd_context *cmd, const char *vg_name,
|
||||
const char *file, int force);
|
||||
|
@@ -22,6 +22,7 @@
|
||||
#include "text_export.h"
|
||||
#include "lvm-version.h"
|
||||
#include "toolcontext.h"
|
||||
#include "config-util.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
@@ -621,6 +622,7 @@ int out_areas(struct formatter *f, const struct lv_segment *seg,
|
||||
(s == seg->area_count - 1) ? "" : ",");
|
||||
break;
|
||||
case AREA_LV:
|
||||
/* FIXME This helper code should be target-independent! Check for metadata LV property. */
|
||||
if (!(seg->status & RAID)) {
|
||||
outf(f, "\"%s\", %u%s",
|
||||
seg_lv(seg, s)->name,
|
||||
@@ -630,15 +632,19 @@ int out_areas(struct formatter *f, const struct lv_segment *seg,
|
||||
}
|
||||
|
||||
/* RAID devices are laid-out in metadata/data pairs */
|
||||
/* FIXME Validation should be elsewhere, not here! */
|
||||
if (!lv_is_raid_image(seg_lv(seg, s)) ||
|
||||
!lv_is_raid_metadata(seg_metalv(seg, s))) {
|
||||
(seg->meta_areas && seg_metalv(seg, s) && !lv_is_raid_metadata(seg_metalv(seg, s)))) {
|
||||
log_error("RAID segment has non-RAID areas");
|
||||
return 0;
|
||||
}
|
||||
|
||||
outf(f, "\"%s\", \"%s\"%s",
|
||||
seg_metalv(seg, s)->name, seg_lv(seg, s)->name,
|
||||
(s == seg->area_count - 1) ? "" : ",");
|
||||
if (seg->meta_areas && seg_metalv(seg,s))
|
||||
outf(f, "\"%s\", \"%s\"%s",
|
||||
(seg->meta_areas && seg_metalv(seg, s)) ? seg_metalv(seg, s)->name : "",
|
||||
seg_lv(seg, s)->name, (s == seg->area_count - 1) ? "" : ",");
|
||||
else
|
||||
outf(f, "\"%s\"%s", seg_lv(seg, s)->name, (s == seg->area_count - 1) ? "" : ",");
|
||||
|
||||
break;
|
||||
case AREA_UNASSIGNED:
|
||||
@@ -853,7 +859,7 @@ static int _print_historical_lv(struct formatter *f, struct historical_logical_v
|
||||
if (!_alloc_printed_indirect_descendants(&hlv->indirect_glvs, &descendants_buffer))
|
||||
goto_out;
|
||||
|
||||
outnl(f);
|
||||
outnlgo(f);
|
||||
outfgo(f, "%s {", hlv->name);
|
||||
_inc_indent(f);
|
||||
|
||||
@@ -1072,7 +1078,7 @@ struct dm_config_tree *export_vg_to_config_tree(struct volume_group *vg)
|
||||
return_NULL;
|
||||
}
|
||||
|
||||
if (!(vg_cft = dm_config_from_string(buf))) {
|
||||
if (!(vg_cft = config_tree_from_string_without_dup_node_check(buf))) {
|
||||
log_error("Error parsing metadata for VG %s.", vg->name);
|
||||
dm_free(buf);
|
||||
return_NULL;
|
||||
|
@@ -453,7 +453,7 @@ static struct raw_locn *_find_vg_rlocn(struct device_area *dev_area,
|
||||
"not match expected name %s.", vgname);
|
||||
|
||||
bad:
|
||||
if ((info = lvmcache_info_from_pvid(dev_area->dev->pvid, 0)) &&
|
||||
if ((info = lvmcache_info_from_pvid(dev_area->dev->pvid, dev_area->dev, 0)) &&
|
||||
!lvmcache_update_vgname_and_id(info, &vgsummary_orphan))
|
||||
stack;
|
||||
|
||||
@@ -1447,7 +1447,7 @@ static int _text_pv_needs_rewrite(const struct format_type *fmt, struct physical
|
||||
if (!pv->is_labelled)
|
||||
return 1;
|
||||
|
||||
if (!(info = lvmcache_info_from_pvid((const char *)&pv->id, 0))) {
|
||||
if (!(info = lvmcache_info_from_pvid((const char *)&pv->id, pv->dev, 0))) {
|
||||
log_error("Failed to find cached info for PV %s.", pv_dev_name(pv));
|
||||
return 0;
|
||||
}
|
||||
@@ -1526,10 +1526,10 @@ static int _text_pv_read(const struct format_type *fmt, const char *pv_name,
|
||||
return_0;
|
||||
|
||||
if (lvmetad_used()) {
|
||||
info = lvmcache_info_from_pvid(dev->pvid, 0);
|
||||
info = lvmcache_info_from_pvid(dev->pvid, dev, 0);
|
||||
if (!info && !lvmetad_pv_lookup_by_dev(fmt->cmd, dev, NULL))
|
||||
return 0;
|
||||
info = lvmcache_info_from_pvid(dev->pvid, 0);
|
||||
info = lvmcache_info_from_pvid(dev->pvid, dev, 0);
|
||||
} else {
|
||||
struct label *label;
|
||||
if (!(label_read(dev, &label, UINT64_C(0))))
|
||||
@@ -1815,7 +1815,7 @@ static int _text_pv_setup(const struct format_type *fmt,
|
||||
*/
|
||||
else {
|
||||
if (!pv->dev ||
|
||||
!(info = lvmcache_info_from_pvid(pv->dev->pvid, 0))) {
|
||||
!(info = lvmcache_info_from_pvid(pv->dev->pvid, pv->dev, 0))) {
|
||||
log_error("PV %s missing from cache", pv_dev_name(pv));
|
||||
return 0;
|
||||
}
|
||||
|
@@ -56,7 +56,7 @@ int text_vgsummary_import(const struct format_type *fmt,
|
||||
(dev && !config_file_read_fd(cft, dev, offset, size,
|
||||
offset2, size2, checksum_fn,
|
||||
vgsummary->mda_checksum,
|
||||
checksum_only))) {
|
||||
checksum_only, 1))) {
|
||||
log_error("Couldn't read volume group metadata.");
|
||||
goto out;
|
||||
}
|
||||
@@ -130,7 +130,7 @@ struct volume_group *text_vg_import_fd(struct format_instance *fid,
|
||||
if ((!dev && !config_file_read(cft)) ||
|
||||
(dev && !config_file_read_fd(cft, dev, offset, size,
|
||||
offset2, size2, checksum_fn, checksum,
|
||||
skip_parse)))
|
||||
skip_parse, 1)))
|
||||
goto_out;
|
||||
|
||||
if (skip_parse) {
|
||||
|
@@ -569,8 +569,11 @@ static int _read_lvnames(struct format_instance *fid __attribute__((unused)),
|
||||
if (!(lv->name = dm_pool_strdup(mem, lvn->key)))
|
||||
return_0;
|
||||
|
||||
log_debug_metadata("Importing logical volume %s.", display_lvname(lv));
|
||||
|
||||
if (!(lvn = lvn->child)) {
|
||||
log_error("Empty logical volume section.");
|
||||
log_error("Empty logical volume section for %s.",
|
||||
display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -22,6 +22,7 @@
|
||||
#define outf(args...) do {if (!out_text(args)) return_0;} while (0)
|
||||
#define outfgo(args...) do {if (!out_text(args)) goto_out;} while (0)
|
||||
#define outnl(f) do {if (!out_newline(f)) return_0;} while (0)
|
||||
#define outnlgo(f) do {if (!out_newline(f)) goto_out;} while (0)
|
||||
|
||||
struct formatter;
|
||||
struct lv_segment;
|
||||
|
@@ -140,18 +140,18 @@ static struct labeller *_find_labeller(struct device *dev, char *buf,
|
||||
sector + scan_sector);
|
||||
}
|
||||
if (xlate64(lh->sector_xl) != sector + scan_sector) {
|
||||
log_info("%s: Label for sector %" PRIu64
|
||||
" found at sector %" PRIu64
|
||||
" - ignoring", dev_name(dev),
|
||||
(uint64_t)xlate64(lh->sector_xl),
|
||||
sector + scan_sector);
|
||||
log_very_verbose("%s: Label for sector %" PRIu64
|
||||
" found at sector %" PRIu64
|
||||
" - ignoring", dev_name(dev),
|
||||
(uint64_t)xlate64(lh->sector_xl),
|
||||
sector + scan_sector);
|
||||
continue;
|
||||
}
|
||||
if (calc_crc(INITIAL_CRC, (uint8_t *)&lh->offset_xl, LABEL_SIZE -
|
||||
((uint8_t *) &lh->offset_xl - (uint8_t *) lh)) !=
|
||||
xlate32(lh->crc_xl)) {
|
||||
log_info("Label checksum incorrect on %s - "
|
||||
"ignoring", dev_name(dev));
|
||||
log_very_verbose("Label checksum incorrect on %s - "
|
||||
"ignoring", dev_name(dev));
|
||||
continue;
|
||||
}
|
||||
if (found)
|
||||
@@ -184,7 +184,7 @@ static struct labeller *_find_labeller(struct device *dev, char *buf,
|
||||
|
||||
out:
|
||||
if (!found) {
|
||||
if ((info = lvmcache_info_from_pvid(dev->pvid, 0)))
|
||||
if ((info = lvmcache_info_from_pvid(dev->pvid, dev, 0)))
|
||||
_update_lvmcache_orphan(info);
|
||||
log_very_verbose("%s: No label detected", dev_name(dev));
|
||||
}
|
||||
@@ -243,8 +243,8 @@ int label_remove(struct device *dev)
|
||||
}
|
||||
|
||||
if (wipe) {
|
||||
log_info("%s: Wiping label at sector %" PRIu64,
|
||||
dev_name(dev), sector);
|
||||
log_very_verbose("%s: Wiping label at sector %" PRIu64,
|
||||
dev_name(dev), sector);
|
||||
if (!dev_write(dev, sector << SECTOR_SHIFT, LABEL_SIZE,
|
||||
buf)) {
|
||||
log_error("Failed to remove label from %s at "
|
||||
@@ -271,16 +271,18 @@ int label_read(struct device *dev, struct label **result,
|
||||
struct lvmcache_info *info;
|
||||
int r = 0;
|
||||
|
||||
if ((info = lvmcache_info_from_pvid(dev->pvid, 1))) {
|
||||
log_debug_devs("Using cached label for %s", dev_name(dev));
|
||||
if ((info = lvmcache_info_from_pvid(dev->pvid, dev, 1))) {
|
||||
log_debug_devs("Reading label from lvmcache for %s", dev_name(dev));
|
||||
*result = lvmcache_get_label(info);
|
||||
return 1;
|
||||
}
|
||||
|
||||
log_debug_devs("Reading label from device %s", dev_name(dev));
|
||||
|
||||
if (!dev_open_readonly(dev)) {
|
||||
stack;
|
||||
|
||||
if ((info = lvmcache_info_from_pvid(dev->pvid, 0)))
|
||||
if ((info = lvmcache_info_from_pvid(dev->pvid, dev, 0)))
|
||||
_update_lvmcache_orphan(info);
|
||||
|
||||
return r;
|
||||
@@ -331,9 +333,9 @@ int label_write(struct device *dev, struct label *label)
|
||||
if (!dev_open(dev))
|
||||
return_0;
|
||||
|
||||
log_info("%s: Writing label to sector %" PRIu64 " with stored offset %"
|
||||
PRIu32 ".", dev_name(dev), label->sector,
|
||||
xlate32(lh->offset_xl));
|
||||
log_very_verbose("%s: Writing label to sector %" PRIu64 " with stored offset %"
|
||||
PRIu32 ".", dev_name(dev), label->sector,
|
||||
xlate32(lh->offset_xl));
|
||||
if (!dev_write(dev, label->sector << SECTOR_SHIFT, LABEL_SIZE, buf)) {
|
||||
log_debug_devs("Failed to write label to %s", dev_name(dev));
|
||||
r = 0;
|
||||
@@ -355,7 +357,7 @@ int label_verify(struct device *dev)
|
||||
int r = 0;
|
||||
|
||||
if (!dev_open_readonly(dev)) {
|
||||
if ((info = lvmcache_info_from_pvid(dev->pvid, 0)))
|
||||
if ((info = lvmcache_info_from_pvid(dev->pvid, dev, 0)))
|
||||
_update_lvmcache_orphan(info);
|
||||
return_0;
|
||||
}
|
||||
|
@@ -198,8 +198,10 @@ int check_lvm1_vg_inactive(struct cmd_context *cmd, const char *vgname);
|
||||
rr; \
|
||||
})
|
||||
|
||||
#define unlock_vg(cmd, vol) \
|
||||
#define unlock_vg(cmd, vg, vol) \
|
||||
do { \
|
||||
if (vg && !lvmetad_vg_update_finish(vg)) \
|
||||
stack; \
|
||||
if (is_real_vg(vol) && !sync_dev_names(cmd)) \
|
||||
stack; \
|
||||
if (!lock_vol(cmd, vol, LCK_VG_UNLOCK, NULL)) \
|
||||
@@ -207,7 +209,7 @@ int check_lvm1_vg_inactive(struct cmd_context *cmd, const char *vgname);
|
||||
} while (0)
|
||||
#define unlock_and_release_vg(cmd, vg, vol) \
|
||||
do { \
|
||||
unlock_vg(cmd, vol); \
|
||||
unlock_vg(cmd, vg, vol); \
|
||||
release_vg(vg); \
|
||||
} while (0)
|
||||
|
||||
|
@@ -367,20 +367,16 @@ static int _extend_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg,
|
||||
{
|
||||
struct logical_volume *lv = vg->sanlock_lv;
|
||||
struct lvresize_params lp = {
|
||||
.lv_name = vg->sanlock_lv->name,
|
||||
.sign = SIGN_NONE,
|
||||
.size = lv->size + ((extend_mb * 1024 * 1024) / SECTOR_SIZE),
|
||||
.percent = PERCENT_NONE,
|
||||
.resize = LV_EXTEND,
|
||||
.ac_force = 1,
|
||||
.sizeargs = 1,
|
||||
.force = 1,
|
||||
};
|
||||
|
||||
lp.size = lv->size + ((extend_mb * 1024 * 1024) / SECTOR_SIZE);
|
||||
|
||||
if (!lv_resize_prepare(cmd, lv, &lp, &vg->pvs) ||
|
||||
!lv_resize(cmd, lv, &lp, &vg->pvs)) {
|
||||
log_error("Extend LV %s/%s to size %llu failed.",
|
||||
vg->name, lv->name, (unsigned long long)lp.size);
|
||||
if (!lv_resize(lv, &lp, &vg->pvs)) {
|
||||
log_error("Extend LV %s to size %s failed.",
|
||||
display_lvname(lv), display_size(cmd, lp.size));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -391,7 +387,7 @@ static int _extend_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg,
|
||||
|
||||
static int _refresh_sanlock_lv(struct cmd_context *cmd, struct volume_group *vg)
|
||||
{
|
||||
if (!lv_refresh_suspend_resume(cmd, vg->sanlock_lv)) {
|
||||
if (!lv_refresh_suspend_resume(vg->sanlock_lv)) {
|
||||
log_error("Failed to refresh %s.", vg->sanlock_lv->name);
|
||||
return 0;
|
||||
}
|
||||
@@ -973,6 +969,7 @@ int lockd_start_vg(struct cmd_context *cmd, struct volume_group *vg, int start_i
|
||||
{
|
||||
char uuid[64] __attribute__((aligned(8)));
|
||||
daemon_reply reply;
|
||||
uint32_t lockd_flags = 0;
|
||||
int host_id = 0;
|
||||
int result;
|
||||
int ret;
|
||||
@@ -1022,13 +1019,16 @@ int lockd_start_vg(struct cmd_context *cmd, struct volume_group *vg, int start_i
|
||||
"opts = %s", start_init ? "start_init" : "none",
|
||||
NULL);
|
||||
|
||||
if (!_lockd_result(reply, &result, NULL)) {
|
||||
if (!_lockd_result(reply, &result, &lockd_flags)) {
|
||||
ret = 0;
|
||||
result = -ELOCKD;
|
||||
} else {
|
||||
ret = (result < 0) ? 0 : 1;
|
||||
}
|
||||
|
||||
if (lockd_flags & LD_RF_WARN_GL_REMOVED)
|
||||
cmd->lockd_gl_removed = 1;
|
||||
|
||||
switch (result) {
|
||||
case 0:
|
||||
log_print_unless_silent("VG %s starting %s lockspace", vg->name, vg->lock_type);
|
||||
@@ -1144,6 +1144,11 @@ int lockd_start_wait(struct cmd_context *cmd)
|
||||
|
||||
daemon_reply_destroy(reply);
|
||||
|
||||
if (cmd->lockd_gl_removed) {
|
||||
log_error("Missing global lock: global lock was lost by removing a previous VG.");
|
||||
log_error("To enable the global lock in another VG, see lvmlockctl --gl-enable.");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1560,6 +1565,12 @@ int lockd_gl(struct cmd_context *cmd, const char *def_mode, uint32_t flags)
|
||||
goto allow;
|
||||
}
|
||||
|
||||
if ((lockd_flags & LD_RF_NO_GL_LS) && (lockd_flags & LD_RF_WARN_GL_REMOVED)) {
|
||||
log_warn("Skipping global lock: VG with global lock was removed");
|
||||
force_cache_update = 1;
|
||||
goto allow;
|
||||
}
|
||||
|
||||
if ((lockd_flags & LD_RF_NO_GL_LS) || (lockd_flags & LD_RF_NO_LOCKSPACES)) {
|
||||
log_warn("Skipping global lock: lockspace not found or started");
|
||||
force_cache_update = 1;
|
||||
@@ -1975,8 +1986,9 @@ int lockd_lv_name(struct cmd_context *cmd, struct volume_group *vg,
|
||||
mode = def_mode;
|
||||
|
||||
if (mode && !strcmp(mode, "sh") && (flags & LDLV_MODE_NO_SH)) {
|
||||
log_error("Shared activation not compatible with LV type: %s/%s",
|
||||
vg->name, lv_name);
|
||||
struct logical_volume *lv = find_lv(vg, lv_name);
|
||||
log_error("Shared activation not compatible with LV type %s of %s/%s",
|
||||
lv ? lvseg_name(first_seg(lv)) : "", vg->name, lv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
342
lib/log/log.c
342
lib/log/log.c
@@ -17,6 +17,8 @@
|
||||
#include "device.h"
|
||||
#include "memlock.h"
|
||||
#include "defaults.h"
|
||||
#include "report.h"
|
||||
#include "lvm-file.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
@@ -30,6 +32,8 @@ static struct dm_str_list _log_dev_alias;
|
||||
|
||||
static int _syslog = 0;
|
||||
static int _log_to_file = 0;
|
||||
static uint64_t _log_file_max_lines = 0;
|
||||
static uint64_t _log_file_lines = 0;
|
||||
static int _log_direct = 0;
|
||||
static int _log_while_suspended = 0;
|
||||
static int _indent = 1;
|
||||
@@ -47,6 +51,163 @@ static size_t _lvm_errmsg_size = 0;
|
||||
static size_t _lvm_errmsg_len = 0;
|
||||
#define MAX_ERRMSG_LEN (512 * 1024) /* Max size of error buffer 512KB */
|
||||
|
||||
static log_report_t _log_report = {
|
||||
.report = NULL,
|
||||
.context = LOG_REPORT_CONTEXT_NULL,
|
||||
.object_type = LOG_REPORT_OBJECT_TYPE_NULL,
|
||||
.object_id = NULL,
|
||||
.object_name = NULL,
|
||||
.object_group = NULL
|
||||
};
|
||||
|
||||
#define LOG_STREAM_BUFFER_SIZE 4096
|
||||
|
||||
struct log_stream_item {
|
||||
FILE *stream;
|
||||
char *buffer;
|
||||
};
|
||||
|
||||
static struct log_stream {
|
||||
struct log_stream_item out;
|
||||
struct log_stream_item err;
|
||||
struct log_stream_item report;
|
||||
} _log_stream = {{NULL, NULL},
|
||||
{NULL, NULL},
|
||||
{NULL, NULL}};
|
||||
|
||||
#define out_stream (_log_stream.out.stream ? : stdout)
|
||||
#define err_stream (_log_stream.err.stream ? : stderr)
|
||||
#define report_stream (_log_stream.report.stream ? : stdout)
|
||||
|
||||
static int _set_custom_log_stream(struct log_stream_item *stream_item, int custom_fd)
|
||||
{
|
||||
FILE *final_stream = NULL;
|
||||
int flags;
|
||||
int r = 1;
|
||||
|
||||
if (custom_fd < 0)
|
||||
goto out;
|
||||
|
||||
if (is_valid_fd(custom_fd)) {
|
||||
if ((flags = fcntl(custom_fd, F_GETFL)) > 0) {
|
||||
if ((flags & O_ACCMODE) == O_RDONLY) {
|
||||
log_error("File descriptor %d already open in read-only "
|
||||
"mode, expected write-only or read-write mode.",
|
||||
(int) custom_fd);
|
||||
r = 0;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (custom_fd == STDIN_FILENO) {
|
||||
log_error("Can't set standard input for log output.");
|
||||
r = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (custom_fd == STDOUT_FILENO) {
|
||||
final_stream = stdout;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (custom_fd == STDERR_FILENO) {
|
||||
final_stream = stderr;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(final_stream = fdopen(custom_fd, "w"))) {
|
||||
log_error("Failed to open stream for file descriptor %d.",
|
||||
(int) custom_fd);
|
||||
r = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(stream_item->buffer = dm_malloc(LOG_STREAM_BUFFER_SIZE))) {
|
||||
log_error("Failed to allocate buffer for stream on file "
|
||||
"descriptor %d.", (int) custom_fd);
|
||||
} else {
|
||||
if (setvbuf(final_stream, stream_item->buffer, _IOLBF, LOG_STREAM_BUFFER_SIZE)) {
|
||||
log_sys_error("setvbuf", "");
|
||||
dm_free(stream_item->buffer);
|
||||
stream_item->buffer = NULL;
|
||||
}
|
||||
}
|
||||
out:
|
||||
stream_item->stream = final_stream;
|
||||
return r;
|
||||
}
|
||||
|
||||
int init_custom_log_streams(struct custom_fds *custom_fds)
|
||||
{
|
||||
return _set_custom_log_stream(&_log_stream.out, custom_fds->out) &&
|
||||
_set_custom_log_stream(&_log_stream.err, custom_fds->err) &&
|
||||
_set_custom_log_stream(&_log_stream.report, custom_fds->report);
|
||||
}
|
||||
|
||||
static void _check_and_replace_standard_log_streams(FILE *old_stream, FILE *new_stream)
|
||||
{
|
||||
if (_log_stream.out.stream == old_stream)
|
||||
_log_stream.out.stream = new_stream;
|
||||
|
||||
if (_log_stream.err.stream == old_stream)
|
||||
_log_stream.err.stream = new_stream;
|
||||
|
||||
if (_log_stream.report.stream == old_stream)
|
||||
_log_stream.report.stream = new_stream;
|
||||
}
|
||||
|
||||
/*
|
||||
* Close and reopen standard stream on file descriptor fd.
|
||||
*/
|
||||
int reopen_standard_stream(FILE **stream, const char *mode)
|
||||
{
|
||||
int fd, fd_copy, new_fd;
|
||||
const char *name;
|
||||
FILE *old_stream = *stream;
|
||||
FILE *new_stream;
|
||||
|
||||
if (old_stream == stdin) {
|
||||
fd = STDIN_FILENO;
|
||||
name = "stdin";
|
||||
} else if (old_stream == stdout) {
|
||||
fd = STDOUT_FILENO;
|
||||
name = "stdout";
|
||||
} else if (old_stream == stderr) {
|
||||
fd = STDERR_FILENO;
|
||||
name = "stderr";
|
||||
} else {
|
||||
log_error(INTERNAL_ERROR "reopen_standard_stream called on non-standard stream");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((fd_copy = dup(fd)) < 0) {
|
||||
log_sys_error("dup", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fclose(old_stream))
|
||||
log_sys_error("fclose", name);
|
||||
|
||||
if ((new_fd = dup2(fd_copy, fd)) < 0)
|
||||
log_sys_error("dup2", name);
|
||||
else if (new_fd != fd)
|
||||
log_error("dup2(%d, %d) returned %d", fd_copy, fd, new_fd);
|
||||
|
||||
if (close(fd_copy) < 0)
|
||||
log_sys_error("close", name);
|
||||
|
||||
if (!(new_stream = fdopen(fd, mode))) {
|
||||
log_sys_error("fdopen", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_check_and_replace_standard_log_streams(old_stream, new_stream);
|
||||
|
||||
*stream = new_stream;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void init_log_fn(lvm2_log_fn_t log_fn)
|
||||
{
|
||||
_lvm2_log_fn = log_fn;
|
||||
@@ -90,7 +251,7 @@ void init_log_file(const char *log_file, int append)
|
||||
log_warn("WARNING: Cannot parse content of %s.", statfile);
|
||||
} else {
|
||||
if (dm_snprintf(_log_file_path, sizeof(_log_file_path),
|
||||
"%s_%s_%d_%lld", log_file, env, pid, starttime) < 0) {
|
||||
"%s_%s_%d_%llu", log_file, env, pid, starttime) < 0) {
|
||||
log_warn("WARNING: Debug log file path is too long for epoch.");
|
||||
_log_file_path[0] = '\0';
|
||||
} else {
|
||||
@@ -101,6 +262,14 @@ void init_log_file(const char *log_file, int append)
|
||||
|
||||
if (st && fclose(st))
|
||||
log_sys_debug("fclose", statfile);
|
||||
|
||||
if ((env = getenv("LVM_LOG_FILE_MAX_LINES"))) {
|
||||
if (sscanf(env, FMTu64, &_log_file_max_lines) != 1) {
|
||||
log_warn("WARNING: Ignoring invalid LVM_LOG_MAX_LINES envvar \"%s\".", env);
|
||||
_log_file_max_lines = 0;
|
||||
}
|
||||
_log_file_lines = 0;
|
||||
}
|
||||
}
|
||||
|
||||
no_epoch:
|
||||
@@ -187,10 +356,10 @@ void fin_log(void)
|
||||
if (_log_to_file) {
|
||||
if (dm_fclose(_log_file)) {
|
||||
if (errno)
|
||||
fprintf(stderr, "failed to write log file: %s\n",
|
||||
fprintf(err_stream, "failed to write log file: %s\n",
|
||||
strerror(errno));
|
||||
else
|
||||
fprintf(stderr, "failed to write log file\n");
|
||||
fprintf(err_stream, "failed to write log file\n");
|
||||
|
||||
}
|
||||
_log_to_file = 0;
|
||||
@@ -260,16 +429,56 @@ void reset_log_duplicated(void) {
|
||||
}
|
||||
}
|
||||
|
||||
void print_log(int level, const char *file, int line, int dm_errno_or_class,
|
||||
const char *format, ...)
|
||||
static const char *_get_log_level_name(int use_stderr, int level)
|
||||
{
|
||||
static const char *log_level_names[] = {"", /* unassigned */
|
||||
"", /* unassigned */
|
||||
"fatal", /* _LOG_FATAL */
|
||||
"error", /* _LOG_ERROR */
|
||||
"warn", /* _LOG_WARN */
|
||||
"notice",/* _LOG_NOTICE */
|
||||
"info", /* _LOG_INFO */
|
||||
"debug" /* _LOG_DEBUG */
|
||||
};
|
||||
if (level == _LOG_WARN && !use_stderr)
|
||||
return "print";
|
||||
|
||||
return log_level_names[level];
|
||||
}
|
||||
|
||||
const char *log_get_report_context_name(log_report_context_t context)
|
||||
{
|
||||
static const char *log_context_names[LOG_REPORT_CONTEXT_COUNT] = {[LOG_REPORT_CONTEXT_NULL] = "",
|
||||
[LOG_REPORT_CONTEXT_SHELL] = "shell",
|
||||
[LOG_REPORT_CONTEXT_PROCESSING] = "processing"};
|
||||
return log_context_names[context];
|
||||
}
|
||||
|
||||
|
||||
const char *log_get_report_object_type_name(log_report_object_type_t object_type)
|
||||
{
|
||||
static const char *log_object_type_names[LOG_REPORT_OBJECT_TYPE_COUNT] = {[LOG_REPORT_OBJECT_TYPE_NULL] = "",
|
||||
[LOG_REPORT_OBJECT_TYPE_CMD] = "cmd",
|
||||
[LOG_REPORT_OBJECT_TYPE_ORPHAN] = "orphan",
|
||||
[LOG_REPORT_OBJECT_TYPE_PV] = "pv",
|
||||
[LOG_REPORT_OBJECT_TYPE_LABEL] = "label",
|
||||
[LOG_REPORT_OBJECT_TYPE_VG] = "vg",
|
||||
[LOG_REPORT_OBJECT_TYPE_LV] = "lv"};
|
||||
return log_object_type_names[object_type];
|
||||
}
|
||||
|
||||
__attribute__ ((format(printf, 5, 0)))
|
||||
static void _vprint_log(int level, const char *file, int line, int dm_errno_or_class,
|
||||
const char *format, va_list orig_ap)
|
||||
{
|
||||
va_list ap;
|
||||
char buf[1024], message[4096];
|
||||
int bufused, n;
|
||||
const char *trformat; /* Translated format string */
|
||||
char *newbuf;
|
||||
int use_stderr = level & _LOG_STDERR;
|
||||
int log_once = level & _LOG_ONCE;
|
||||
int use_stderr = log_stderr(level);
|
||||
int log_once = log_once(level);
|
||||
int log_bypass_report = log_bypass_report(level);
|
||||
int fatal_internal_error = 0;
|
||||
size_t msglen;
|
||||
const char *indent_spaces = "";
|
||||
@@ -277,8 +486,10 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class,
|
||||
static int _abort_on_internal_errors_env_present = -1;
|
||||
static int _abort_on_internal_errors_env = 0;
|
||||
char *env_str;
|
||||
struct dm_report *orig_report;
|
||||
int logged_via_report = 0;
|
||||
|
||||
level &= ~(_LOG_STDERR|_LOG_ONCE);
|
||||
level = log_level(level);
|
||||
|
||||
if (_abort_on_internal_errors_env_present < 0) {
|
||||
if ((env_str = getenv("DM_ABORT_ON_INTERNAL_ERRORS"))) {
|
||||
@@ -309,16 +520,17 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class,
|
||||
|
||||
if (_lvm2_log_fn ||
|
||||
(_store_errmsg && (level <= _LOG_ERR)) ||
|
||||
(_log_report.report && !log_bypass_report && (use_stderr || (level <=_LOG_WARN))) ||
|
||||
log_once) {
|
||||
va_start(ap, format);
|
||||
va_copy(ap, orig_ap);
|
||||
n = vsnprintf(message, sizeof(message), trformat, ap);
|
||||
va_end(ap);
|
||||
|
||||
/* When newer glibc returns >= sizeof(locn), we will just log what
|
||||
* has fit into buffer, it's '\0' terminated string */
|
||||
if (n < 0) {
|
||||
fprintf(stderr, _("vsnprintf failed: skipping external "
|
||||
"logging function"));
|
||||
fprintf(err_stream, _("vsnprintf failed: skipping external "
|
||||
"logging function"));
|
||||
goto log_it;
|
||||
}
|
||||
}
|
||||
@@ -356,6 +568,22 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class,
|
||||
}
|
||||
}
|
||||
|
||||
if (_log_report.report && !log_bypass_report && (use_stderr || (level <= _LOG_WARN))) {
|
||||
orig_report = _log_report.report;
|
||||
_log_report.report = NULL;
|
||||
if (!report_cmdlog(orig_report, _get_log_level_name(use_stderr, level),
|
||||
log_get_report_context_name(_log_report.context),
|
||||
log_get_report_object_type_name(_log_report.object_type),
|
||||
_log_report.object_name, _log_report.object_id,
|
||||
_log_report.object_group, _log_report.object_group_id,
|
||||
message, _lvm_errno, 0))
|
||||
fprintf(err_stream, _("failed to report cmdstatus"));
|
||||
else
|
||||
logged_via_report = 1;
|
||||
|
||||
_log_report.report = orig_report;
|
||||
}
|
||||
|
||||
if (_lvm2_log_fn) {
|
||||
_lvm2_log_fn(level, file, line, 0, message);
|
||||
if (fatal_internal_error)
|
||||
@@ -364,7 +592,7 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class,
|
||||
}
|
||||
|
||||
log_it:
|
||||
if ((verbose_level() >= level) && !_log_suppress) {
|
||||
if (!logged_via_report && ((verbose_level() >= level) && !_log_suppress)) {
|
||||
if (verbose_level() > _LOG_DEBUG) {
|
||||
(void) dm_snprintf(buf, sizeof(buf), "#%s:%d ",
|
||||
file, line);
|
||||
@@ -379,7 +607,7 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class,
|
||||
default: /* nothing to do */;
|
||||
}
|
||||
|
||||
va_start(ap, format);
|
||||
va_copy(ap, orig_ap);
|
||||
switch (level) {
|
||||
case _LOG_DEBUG:
|
||||
if (verbose_level() < _LOG_DEBUG)
|
||||
@@ -391,10 +619,10 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class,
|
||||
break;
|
||||
/* fall through */
|
||||
default:
|
||||
/* Typically only log_warn goes to stdout */
|
||||
stream = (use_stderr || (level != _LOG_WARN)) ? stderr : stdout;
|
||||
if (stream == stderr)
|
||||
fflush(stdout);
|
||||
/* Typically only log_warn goes to out_stream */
|
||||
stream = (use_stderr || (level != _LOG_WARN)) ? err_stream : out_stream;
|
||||
if (stream == err_stream)
|
||||
fflush(out_stream);
|
||||
fprintf(stream, "%s%s%s%s", buf, log_command_name(),
|
||||
_msg_prefix, indent_spaces);
|
||||
vfprintf(stream, trformat, ap);
|
||||
@@ -414,16 +642,19 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class,
|
||||
fprintf(_log_file, "%s:%d %s%s", file, line, log_command_name(),
|
||||
_msg_prefix);
|
||||
|
||||
va_start(ap, format);
|
||||
va_copy(ap, orig_ap);
|
||||
vfprintf(_log_file, trformat, ap);
|
||||
va_end(ap);
|
||||
|
||||
fputc('\n', _log_file);
|
||||
fflush(_log_file);
|
||||
|
||||
if (_log_file_max_lines && ++_log_file_lines >= _log_file_max_lines)
|
||||
fatal_internal_error = 1;
|
||||
}
|
||||
|
||||
if (_syslog && (_log_while_suspended || !critical_section())) {
|
||||
va_start(ap, format);
|
||||
va_copy(ap, orig_ap);
|
||||
vsyslog(level, trformat, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
@@ -443,7 +674,7 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class,
|
||||
|
||||
bufused += n; /* n does not include '\0' */
|
||||
|
||||
va_start(ap, format);
|
||||
va_copy(ap, orig_ap);
|
||||
n = vsnprintf(buf + bufused, sizeof(buf) - bufused,
|
||||
trformat, ap);
|
||||
va_end(ap);
|
||||
@@ -462,3 +693,74 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class,
|
||||
_already_logging = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void print_log(int level, const char *file, int line, int dm_errno_or_class,
|
||||
const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
_vprint_log(level, file, line, dm_errno_or_class, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void print_log_libdm(int level, const char *file, int line, int dm_errno_or_class,
|
||||
const char *format, ...)
|
||||
{
|
||||
FILE *orig_out_stream = out_stream;
|
||||
va_list ap;
|
||||
|
||||
/*
|
||||
* Bypass report if printing output from libdm and if we have
|
||||
* LOG_WARN level and it's not going to stderr (so we're
|
||||
* printing common message that is not an error/warning).
|
||||
*/
|
||||
if (!log_stderr(level) &&
|
||||
(log_level(level) == _LOG_WARN))
|
||||
level |= _LOG_BYPASS_REPORT;
|
||||
|
||||
_log_stream.out.stream = report_stream;
|
||||
|
||||
va_start(ap, format);
|
||||
_vprint_log(level, file, line, dm_errno_or_class, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
_log_stream.out.stream = orig_out_stream;
|
||||
}
|
||||
|
||||
log_report_t log_get_report_state(void)
|
||||
{
|
||||
return _log_report;
|
||||
}
|
||||
|
||||
void log_restore_report_state(log_report_t log_report)
|
||||
{
|
||||
_log_report = log_report;
|
||||
}
|
||||
|
||||
void log_set_report(struct dm_report *report)
|
||||
{
|
||||
_log_report.report = report;
|
||||
}
|
||||
|
||||
void log_set_report_context(log_report_context_t context)
|
||||
{
|
||||
_log_report.context = context;
|
||||
}
|
||||
|
||||
void log_set_report_object_type(log_report_object_type_t object_type)
|
||||
{
|
||||
_log_report.object_type = object_type;
|
||||
}
|
||||
|
||||
void log_set_report_object_group_and_group_id(const char *group, const char *id)
|
||||
{
|
||||
_log_report.object_group = group;
|
||||
_log_report.object_group_id = id;
|
||||
}
|
||||
|
||||
void log_set_report_object_name_and_id(const char *name, const char *id)
|
||||
{
|
||||
_log_report.object_name = name;
|
||||
_log_report.object_id = id;
|
||||
}
|
||||
|
@@ -41,15 +41,20 @@
|
||||
|
||||
#define EUNCLASSIFIED -1 /* Generic error code */
|
||||
|
||||
#define _LOG_STDERR 128 /* force things to go to stderr, even if loglevel
|
||||
would make them go to stdout */
|
||||
#define _LOG_ONCE 256 /* downgrade to NOTICE if this has been already logged */
|
||||
#define _LOG_DEBUG 7
|
||||
#define _LOG_INFO 6
|
||||
#define _LOG_NOTICE 5
|
||||
#define _LOG_WARN 4
|
||||
#define _LOG_ERR 3
|
||||
#define _LOG_FATAL 2
|
||||
#define _LOG_FATAL 0x0002
|
||||
#define _LOG_ERR 0x0003
|
||||
#define _LOG_WARN 0x0004
|
||||
#define _LOG_NOTICE 0x0005
|
||||
#define _LOG_INFO 0x0006
|
||||
#define _LOG_DEBUG 0x0007
|
||||
#define _LOG_STDERR 0x0080 /* force things to go to stderr, even if loglevel would make them go to stdout */
|
||||
#define _LOG_ONCE 0x0100 /* downgrade to NOTICE if this has been already logged */
|
||||
#define _LOG_BYPASS_REPORT 0x0200 /* do not log through report even if report available */
|
||||
#define log_level(x) ((x) & 0x0f) /* obtain message level */
|
||||
#define log_stderr(x) ((x) & _LOG_STDERR) /* obtain stderr bit */
|
||||
#define log_once(x) ((x) & _LOG_ONCE) /* obtain once bit */
|
||||
#define log_bypass_report(x) ((x) & _LOG_BYPASS_REPORT)/* obtain bypass bit */
|
||||
|
||||
#define INTERNAL_ERROR "Internal error: "
|
||||
|
||||
/*
|
||||
|
@@ -16,10 +16,16 @@
|
||||
#ifndef _LVM_LOGGING_H
|
||||
#define _LVM_LOGGING_H
|
||||
|
||||
#include "lvm-file.h"
|
||||
|
||||
__attribute__ ((format(printf, 5, 6)))
|
||||
void print_log(int level, const char *file, int line, int dm_errno_or_class,
|
||||
const char *format, ...);
|
||||
|
||||
__attribute__ ((format(printf, 5, 6)))
|
||||
void print_log_libdm(int level, const char *file, int line, int dm_errno_or_class,
|
||||
const char *format, ...);
|
||||
|
||||
#define LOG_LINE(l, x...) \
|
||||
print_log(l, __FILE__, __LINE__ , 0, ## x)
|
||||
|
||||
@@ -31,6 +37,9 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class,
|
||||
|
||||
#include "log.h"
|
||||
|
||||
int init_custom_log_streams(struct custom_fds *custom_fds);
|
||||
int reopen_standard_stream(FILE **stream, const char *mode);
|
||||
|
||||
typedef void (*lvm2_log_fn_t) (int level, const char *file, int line,
|
||||
int dm_errno_or_class, const char *message);
|
||||
|
||||
@@ -65,4 +74,49 @@ int log_suppress(int suppress);
|
||||
/* Suppress messages to syslog */
|
||||
void syslog_suppress(int suppress);
|
||||
|
||||
/* Hooks to handle logging through report. */
|
||||
typedef enum {
|
||||
LOG_REPORT_CONTEXT_NULL,
|
||||
LOG_REPORT_CONTEXT_SHELL,
|
||||
LOG_REPORT_CONTEXT_PROCESSING,
|
||||
LOG_REPORT_CONTEXT_COUNT
|
||||
} log_report_context_t;
|
||||
|
||||
typedef enum {
|
||||
LOG_REPORT_OBJECT_TYPE_NULL,
|
||||
LOG_REPORT_OBJECT_TYPE_CMD,
|
||||
LOG_REPORT_OBJECT_TYPE_ORPHAN,
|
||||
LOG_REPORT_OBJECT_TYPE_PV,
|
||||
LOG_REPORT_OBJECT_TYPE_LABEL,
|
||||
LOG_REPORT_OBJECT_TYPE_VG,
|
||||
LOG_REPORT_OBJECT_TYPE_LV,
|
||||
LOG_REPORT_OBJECT_TYPE_COUNT
|
||||
} log_report_object_type_t;
|
||||
|
||||
typedef struct log_report {
|
||||
struct dm_report *report;
|
||||
log_report_context_t context;
|
||||
log_report_object_type_t object_type;
|
||||
const char *object_name;
|
||||
const char *object_id;
|
||||
const char *object_group;
|
||||
const char *object_group_id;
|
||||
} log_report_t;
|
||||
|
||||
#define LOG_STATUS_NAME "status"
|
||||
#define LOG_STATUS_SUCCESS "success"
|
||||
#define LOG_STATUS_FAILURE "failure"
|
||||
|
||||
log_report_t log_get_report_state(void);
|
||||
void log_restore_report_state(log_report_t log_report);
|
||||
|
||||
void log_set_report(struct dm_report *report);
|
||||
void log_set_report_context(log_report_context_t context);
|
||||
void log_set_report_object_type(log_report_object_type_t object_type);
|
||||
void log_set_report_object_group_and_group_id(const char *group, const char *group_id);
|
||||
void log_set_report_object_name_and_id(const char *name, const char *id);
|
||||
|
||||
const char *log_get_report_context_name(log_report_context_t context);
|
||||
const char *log_get_report_object_type_name(log_report_object_type_t object_type);
|
||||
|
||||
#endif
|
||||
|
@@ -29,76 +29,91 @@
|
||||
#define DM_HINT_OVERHEAD_PER_BLOCK 8 /* bytes */
|
||||
#define DM_MAX_HINT_WIDTH (4+16) /* bytes. FIXME Configurable? */
|
||||
|
||||
int cache_mode_is_set(const struct lv_segment *seg)
|
||||
const char *display_cache_mode(const struct lv_segment *seg)
|
||||
{
|
||||
if (seg_is_cache(seg))
|
||||
seg = first_seg(seg->pool_lv);
|
||||
|
||||
return (seg->feature_flags & (DM_CACHE_FEATURE_WRITEBACK |
|
||||
DM_CACHE_FEATURE_WRITETHROUGH |
|
||||
DM_CACHE_FEATURE_PASSTHROUGH)) ? 1 : 0;
|
||||
if (!seg_is_cache_pool(seg) ||
|
||||
(seg->cache_mode == CACHE_MODE_UNDEFINED))
|
||||
return "";
|
||||
|
||||
return get_cache_mode_name(seg);
|
||||
}
|
||||
|
||||
const char *get_cache_mode_name(const struct lv_segment *seg)
|
||||
{
|
||||
if (seg->feature_flags & DM_CACHE_FEATURE_PASSTHROUGH)
|
||||
return "passthrough";
|
||||
|
||||
if (seg->feature_flags & DM_CACHE_FEATURE_WRITEBACK)
|
||||
const char *get_cache_mode_name(const struct lv_segment *pool_seg)
|
||||
{
|
||||
switch (pool_seg->cache_mode) {
|
||||
default:
|
||||
log_error(INTERNAL_ERROR "Cache pool %s has undefined cache mode, using writethrough instead.",
|
||||
display_lvname(pool_seg->lv));
|
||||
/* Fall through */
|
||||
case CACHE_MODE_WRITETHROUGH:
|
||||
return "writethrough";
|
||||
case CACHE_MODE_WRITEBACK:
|
||||
return "writeback";
|
||||
|
||||
if (!(seg->feature_flags & DM_CACHE_FEATURE_WRITETHROUGH))
|
||||
log_error(INTERNAL_ERROR "LV %s has uknown feature flags %" PRIu64 ", "
|
||||
"returning writethrough instead.",
|
||||
display_lvname(seg->lv), seg->feature_flags);
|
||||
|
||||
return "writethrough";
|
||||
case CACHE_MODE_PASSTHROUGH:
|
||||
return "passthrough";
|
||||
}
|
||||
}
|
||||
|
||||
int cache_set_mode(struct lv_segment *seg, const char *str)
|
||||
int set_cache_mode(cache_mode_t *mode, const char *cache_mode)
|
||||
{
|
||||
struct cmd_context *cmd = seg->lv->vg->cmd;
|
||||
int id;
|
||||
uint64_t mode;
|
||||
|
||||
if (!str && !seg_is_cache(seg))
|
||||
return 1; /* Defaults only for cache */
|
||||
|
||||
if (seg_is_cache(seg))
|
||||
seg = first_seg(seg->pool_lv);
|
||||
|
||||
if (!str) {
|
||||
if (cache_mode_is_set(seg))
|
||||
return 1; /* Default already set in cache pool */
|
||||
|
||||
id = allocation_cache_mode_CFG;
|
||||
|
||||
/* If present, check backward compatible settings */
|
||||
if (!find_config_node(cmd, cmd->cft, id) &&
|
||||
find_config_node(cmd, cmd->cft, allocation_cache_pool_cachemode_CFG))
|
||||
id = allocation_cache_pool_cachemode_CFG;
|
||||
|
||||
if (!(str = find_config_tree_str(cmd, id, NULL))) {
|
||||
log_error(INTERNAL_ERROR "Cache mode is not determined.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcmp(str, "writeback"))
|
||||
mode = DM_CACHE_FEATURE_WRITEBACK;
|
||||
else if (!strcmp(str, "writethrough"))
|
||||
mode = DM_CACHE_FEATURE_WRITETHROUGH;
|
||||
else if (!strcmp(str, "passthrough"))
|
||||
mode = DM_CACHE_FEATURE_PASSTHROUGH;
|
||||
if (!strcasecmp(cache_mode, "writethrough"))
|
||||
*mode = CACHE_MODE_WRITETHROUGH;
|
||||
else if (!strcasecmp(cache_mode, "writeback"))
|
||||
*mode = CACHE_MODE_WRITEBACK;
|
||||
else if (!strcasecmp(cache_mode, "passthrough"))
|
||||
*mode = CACHE_MODE_PASSTHROUGH;
|
||||
else {
|
||||
log_error("Cannot set unknown cache mode \"%s\".", str);
|
||||
log_error("Unknown cache mode: %s.", cache_mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
seg->feature_flags &= ~(DM_CACHE_FEATURE_WRITEBACK |
|
||||
DM_CACHE_FEATURE_WRITETHROUGH |
|
||||
DM_CACHE_FEATURE_PASSTHROUGH);
|
||||
seg->feature_flags |= mode;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int cache_set_cache_mode(struct lv_segment *seg, cache_mode_t mode)
|
||||
{
|
||||
struct cmd_context *cmd = seg->lv->vg->cmd;
|
||||
const char *str;
|
||||
int id;
|
||||
|
||||
if (seg_is_cache(seg))
|
||||
seg = first_seg(seg->pool_lv);
|
||||
else if (seg_is_cache_pool(seg)) {
|
||||
if (mode == CACHE_MODE_UNDEFINED)
|
||||
return 1; /* Defaults only for cache */
|
||||
} else {
|
||||
log_error(INTERNAL_ERROR "Cannot set cache mode for non cache volume %s.",
|
||||
display_lvname(seg->lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mode != CACHE_MODE_UNDEFINED) {
|
||||
seg->cache_mode = mode;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (seg->cache_mode != CACHE_MODE_UNDEFINED)
|
||||
return 1; /* Default already set in cache pool */
|
||||
|
||||
/* Figure default settings from config/profiles */
|
||||
id = allocation_cache_mode_CFG;
|
||||
|
||||
/* If present, check backward compatible settings */
|
||||
if (!find_config_node(cmd, cmd->cft, id) &&
|
||||
find_config_node(cmd, cmd->cft, allocation_cache_pool_cachemode_CFG))
|
||||
id = allocation_cache_pool_cachemode_CFG;
|
||||
|
||||
if (!(str = find_config_tree_str(cmd, id, NULL))) {
|
||||
log_error(INTERNAL_ERROR "Cache mode is not determined.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(set_cache_mode(&seg->cache_mode, str)))
|
||||
return_0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -111,7 +126,7 @@ void cache_check_for_warns(const struct lv_segment *seg)
|
||||
struct logical_volume *origin_lv = seg_lv(seg, 0);
|
||||
|
||||
if (lv_is_raid(origin_lv) &&
|
||||
first_seg(seg->pool_lv)->feature_flags & DM_CACHE_FEATURE_WRITEBACK)
|
||||
first_seg(seg->pool_lv)->cache_mode == CACHE_MODE_WRITEBACK)
|
||||
log_warn("WARNING: Data redundancy is lost with writeback "
|
||||
"caching of raid logical volume!");
|
||||
|
||||
@@ -120,6 +135,23 @@ void cache_check_for_warns(const struct lv_segment *seg)
|
||||
"resized and require manual uncache before resize!");
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns minimum size of cache metadata volume for give data and chunk size
|
||||
* (all values in sector)
|
||||
* Default meta size is: (Overhead + mapping size + hint size)
|
||||
*/
|
||||
static uint64_t _cache_min_metadata_size(uint64_t data_size, uint32_t chunk_size)
|
||||
{
|
||||
uint64_t min_meta_size;
|
||||
|
||||
min_meta_size = data_size / chunk_size; /* nr_chunks */
|
||||
min_meta_size *= (DM_BYTES_PER_BLOCK + DM_MAX_HINT_WIDTH + DM_HINT_OVERHEAD_PER_BLOCK);
|
||||
min_meta_size = (min_meta_size + (SECTOR_SIZE - 1)) >> SECTOR_SHIFT; /* in sectors */
|
||||
min_meta_size += DM_TRANSACTION_OVERHEAD * (1024 >> SECTOR_SHIFT);
|
||||
|
||||
return min_meta_size;
|
||||
}
|
||||
|
||||
int update_cache_pool_params(const struct segment_type *segtype,
|
||||
struct volume_group *vg, unsigned attr,
|
||||
int passed_args, uint32_t pool_data_extents,
|
||||
@@ -128,22 +160,46 @@ int update_cache_pool_params(const struct segment_type *segtype,
|
||||
{
|
||||
uint64_t min_meta_size;
|
||||
uint32_t extent_size = vg->extent_size;
|
||||
uint64_t pool_metadata_size = (uint64_t) *pool_metadata_extents * vg->extent_size;
|
||||
uint64_t pool_metadata_size = (uint64_t) *pool_metadata_extents * extent_size;
|
||||
uint64_t pool_data_size = (uint64_t) pool_data_extents * extent_size;
|
||||
uint64_t max_chunks =
|
||||
get_default_allocation_cache_pool_max_chunks_CFG(vg->cmd, vg->profile);
|
||||
/* min chunk size in a multiple of DM_CACHE_MIN_DATA_BLOCK_SIZE */
|
||||
uint64_t min_chunk_size = (((pool_data_size + max_chunks - 1) / max_chunks +
|
||||
DM_CACHE_MIN_DATA_BLOCK_SIZE - 1) /
|
||||
DM_CACHE_MIN_DATA_BLOCK_SIZE) * DM_CACHE_MIN_DATA_BLOCK_SIZE;
|
||||
|
||||
if (!(passed_args & PASS_ARG_CHUNK_SIZE))
|
||||
if (!(passed_args & PASS_ARG_CHUNK_SIZE)) {
|
||||
*chunk_size = DEFAULT_CACHE_POOL_CHUNK_SIZE * 2;
|
||||
if (*chunk_size < min_chunk_size) {
|
||||
/*
|
||||
* When using more then 'standard' default,
|
||||
* keep user informed he might be using things in untintended direction
|
||||
*/
|
||||
log_print_unless_silent("Using %s chunk size instead of default %s, "
|
||||
"so cache pool has less then " FMTu64 " chunks.",
|
||||
display_size(vg->cmd, min_chunk_size),
|
||||
display_size(vg->cmd, *chunk_size),
|
||||
max_chunks);
|
||||
*chunk_size = min_chunk_size;
|
||||
} else
|
||||
log_verbose("Setting chunk size to %s.",
|
||||
display_size(vg->cmd, *chunk_size));
|
||||
} else if (*chunk_size < min_chunk_size) {
|
||||
log_error("Chunk size %s is less then required minimal chunk size %s "
|
||||
"for a cache pool of %s size and limit " FMTu64 " chunks.",
|
||||
display_size(vg->cmd, *chunk_size),
|
||||
display_size(vg->cmd, min_chunk_size),
|
||||
display_size(vg->cmd, pool_data_size),
|
||||
max_chunks);
|
||||
log_error("To allow use of more chunks, see setting allocation/cache_pool_max_chunks.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!validate_pool_chunk_size(vg->cmd, segtype, *chunk_size))
|
||||
return_0;
|
||||
|
||||
/*
|
||||
* Default meta size is:
|
||||
* (Overhead + mapping size + hint size)
|
||||
*/
|
||||
min_meta_size = (uint64_t) pool_data_extents * extent_size / *chunk_size; /* nr_chunks */
|
||||
min_meta_size *= (DM_BYTES_PER_BLOCK + DM_MAX_HINT_WIDTH + DM_HINT_OVERHEAD_PER_BLOCK);
|
||||
min_meta_size = (min_meta_size + (SECTOR_SIZE - 1)) >> SECTOR_SHIFT; /* in sectors */
|
||||
min_meta_size += DM_TRANSACTION_OVERHEAD * (1024 >> SECTOR_SHIFT);
|
||||
min_meta_size = _cache_min_metadata_size((uint64_t) pool_data_extents * extent_size, *chunk_size);
|
||||
|
||||
/* Round up to extent size */
|
||||
if (min_meta_size % extent_size)
|
||||
@@ -173,6 +229,46 @@ int update_cache_pool_params(const struct segment_type *segtype,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate if existing cache-pool can be used with given chunk size
|
||||
* i.e. cache-pool metadata size fits all info.
|
||||
*/
|
||||
int validate_lv_cache_chunk_size(struct logical_volume *pool_lv, uint32_t chunk_size)
|
||||
{
|
||||
struct volume_group *vg = pool_lv->vg;
|
||||
uint64_t max_chunks = get_default_allocation_cache_pool_max_chunks_CFG(vg->cmd, vg->profile);
|
||||
uint64_t min_size = _cache_min_metadata_size(pool_lv->size, chunk_size);
|
||||
uint64_t chunks = pool_lv->size / chunk_size;
|
||||
int r = 1;
|
||||
|
||||
if (min_size > first_seg(pool_lv)->metadata_lv->size) {
|
||||
log_error("Cannot use chunk size %s with cache pool %s metadata size %s.",
|
||||
display_size(vg->cmd, chunk_size),
|
||||
display_lvname(pool_lv),
|
||||
display_size(vg->cmd, first_seg(pool_lv)->metadata_lv->size));
|
||||
log_error("Minimal size for cache pool %s metadata with chunk size %s would be %s.",
|
||||
display_lvname(pool_lv),
|
||||
display_size(vg->cmd, chunk_size),
|
||||
display_size(vg->cmd, min_size));
|
||||
r = 0;
|
||||
}
|
||||
|
||||
if (chunks > max_chunks) {
|
||||
log_error("Cannot use too small chunk size %s with cache pool %s data volume size %s.",
|
||||
display_size(vg->cmd, chunk_size),
|
||||
display_lvname(pool_lv),
|
||||
display_size(pool_lv->vg->cmd, pool_lv->size));
|
||||
log_error("Maximum configured chunks for a cache pool is " FMTu64 ".",
|
||||
max_chunks);
|
||||
log_error("Use smaller cache pool (<%s) or bigger cache chunk size (>=%s) or enable higher "
|
||||
"values in 'allocation/cache_pool_max_chunks'.",
|
||||
display_size(vg->cmd, chunk_size * max_chunks),
|
||||
display_size(vg->cmd, pool_lv->size / max_chunks));
|
||||
r = 0;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
/*
|
||||
* Validate arguments for converting origin into cached volume with given cache pool.
|
||||
*
|
||||
@@ -284,6 +380,7 @@ struct logical_volume *lv_cache_create(struct logical_volume *pool_lv,
|
||||
*/
|
||||
int lv_cache_wait_for_clean(struct logical_volume *cache_lv, int *is_clean)
|
||||
{
|
||||
const struct logical_volume *lock_lv = lv_lock_holder(cache_lv);
|
||||
struct lv_segment *cache_seg = first_seg(cache_lv);
|
||||
struct lv_status_cache *status;
|
||||
int cleaner_policy;
|
||||
@@ -295,9 +392,11 @@ int lv_cache_wait_for_clean(struct logical_volume *cache_lv, int *is_clean)
|
||||
for (;;) {
|
||||
if (!lv_cache_status(cache_lv, &status))
|
||||
return_0;
|
||||
|
||||
if (status->cache->fail) {
|
||||
dm_pool_destroy(status->mem);
|
||||
log_warn("WARNING: Skippping flush for failed cache.");
|
||||
log_warn("WARNING: Skippping flush for failed cache %s.",
|
||||
display_lvname(cache_lv));
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -313,23 +412,31 @@ int lv_cache_wait_for_clean(struct logical_volume *cache_lv, int *is_clean)
|
||||
if (!dirty_blocks)
|
||||
break;
|
||||
|
||||
log_print_unless_silent("Flushing " FMTu64 " blocks for cache %s.",
|
||||
dirty_blocks, display_lvname(cache_lv));
|
||||
if (cleaner_policy) {
|
||||
log_print_unless_silent(FMTu64 " blocks must still be flushed.",
|
||||
dirty_blocks);
|
||||
/* TODO: Use centralized place */
|
||||
usleep(500000);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Switch to cleaner policy to flush the cache */
|
||||
log_print_unless_silent("Flushing cache for %s.",
|
||||
display_lvname(cache_lv));
|
||||
cache_seg->cleaner_policy = 1;
|
||||
/* Reaload kernel with "cleaner" policy */
|
||||
if (!lv_update_and_reload_origin(cache_lv))
|
||||
return_0;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: add check if extra suspend resume is necessary
|
||||
* ATM this is workaround for missing cache sync when cache gets clean
|
||||
*/
|
||||
if (1) {
|
||||
if (!lv_refresh_suspend_resume(lock_lv))
|
||||
return_0;
|
||||
}
|
||||
|
||||
cache_seg->cleaner_policy = 0;
|
||||
*is_clean = 1;
|
||||
|
||||
return 1;
|
||||
@@ -368,12 +475,15 @@ int lv_cache_remove(struct logical_volume *cache_lv)
|
||||
if (!lv_is_active_locally(cache_lv)) {
|
||||
/* Give up any remote locks */
|
||||
if (!deactivate_lv(cache_lv->vg->cmd, cache_lv)) {
|
||||
log_error("Cannot deactivate remotely active cache lv.");
|
||||
log_error("Cannot deactivate remotely active cache volume %s.",
|
||||
display_lvname(cache_lv));
|
||||
return 0;
|
||||
}
|
||||
/* For inactive writethrough just drop cache layer */
|
||||
if (first_seg(cache_seg->pool_lv)->feature_flags &
|
||||
DM_CACHE_FEATURE_WRITETHROUGH) {
|
||||
|
||||
switch (first_seg(cache_seg->pool_lv)->cache_mode) {
|
||||
case CACHE_MODE_WRITETHROUGH:
|
||||
case CACHE_MODE_PASSTHROUGH:
|
||||
/* For inactive pass/writethrough just drop cache layer */
|
||||
corigin_lv = seg_lv(cache_seg, 0);
|
||||
if (!detach_pool_lv(cache_seg))
|
||||
return_0;
|
||||
@@ -382,17 +492,17 @@ int lv_cache_remove(struct logical_volume *cache_lv)
|
||||
if (!lv_remove(corigin_lv))
|
||||
return_0;
|
||||
return 1;
|
||||
default:
|
||||
/* Otherwise localy activate volume to sync dirty blocks */
|
||||
cache_lv->status |= LV_TEMPORARY;
|
||||
if (!activate_lv_excl_local(cache_lv->vg->cmd, cache_lv) ||
|
||||
!lv_is_active_locally(cache_lv)) {
|
||||
log_error("Failed to active cache locally %s.",
|
||||
display_lvname(cache_lv));
|
||||
return 0;
|
||||
}
|
||||
cache_lv->status &= ~LV_TEMPORARY;
|
||||
}
|
||||
|
||||
/* Otherwise localy active volume is need to sync dirty blocks */
|
||||
cache_lv->status |= LV_TEMPORARY;
|
||||
if (!activate_lv_excl_local(cache_lv->vg->cmd, cache_lv) ||
|
||||
!lv_is_active_locally(cache_lv)) {
|
||||
log_error("Failed to active cache locally %s.",
|
||||
display_lvname(cache_lv));
|
||||
return 0;
|
||||
}
|
||||
cache_lv->status &= ~LV_TEMPORARY;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -441,6 +551,7 @@ int lv_cache_remove(struct logical_volume *cache_lv)
|
||||
|
||||
if (!(cache_seg->areas = dm_pool_zalloc(cache_lv->vg->vgmem, sizeof(*cache_seg->areas))))
|
||||
return_0;
|
||||
|
||||
if (!set_lv_segment_area_lv(cache_seg, 0, cache_lv, 0, 0))
|
||||
return_0;
|
||||
|
||||
@@ -602,6 +713,40 @@ out:
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Universal 'wrapper' function do-it-all
|
||||
* to update all commonly specified cache parameters
|
||||
*/
|
||||
int cache_set_params(struct lv_segment *seg,
|
||||
cache_mode_t mode,
|
||||
const char *policy_name,
|
||||
const struct dm_config_tree *policy_settings,
|
||||
uint32_t chunk_size)
|
||||
{
|
||||
struct lv_segment *pool_seg;
|
||||
|
||||
if (!cache_set_cache_mode(seg, mode))
|
||||
return_0;
|
||||
|
||||
if (!cache_set_policy(seg, policy_name, policy_settings))
|
||||
return_0;
|
||||
|
||||
pool_seg = seg_is_cache(seg) ? first_seg(seg->pool_lv) : seg;
|
||||
|
||||
if (chunk_size) {
|
||||
if (!validate_lv_cache_chunk_size(pool_seg->lv, chunk_size))
|
||||
return_0;
|
||||
pool_seg->chunk_size = chunk_size;
|
||||
} else {
|
||||
/* TODO: some calc_policy solution for cache ? */
|
||||
if (!recalculate_pool_chunk_size_with_dev_hints(pool_seg->lv, 0,
|
||||
THIN_CHUNK_SIZE_CALC_METHOD_GENERIC))
|
||||
return_0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wipe cache pool metadata area before use.
|
||||
*
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2013 Red Hat, Inc. All rights reserved.
|
||||
* Copyright (C) 2004-2016 Red Hat, Inc. All rights reserved.
|
||||
*
|
||||
* This file is part of LVM2.
|
||||
*
|
||||
@@ -53,7 +53,7 @@ static struct dm_list *_format_pvsegs(struct dm_pool *mem, const struct lv_segme
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (metadata_areas_only && (!seg_is_raid(seg) || lv_is_raid_metadata(seg->lv) || lv_is_raid_image(seg->lv)))
|
||||
if (metadata_areas_only && (!seg_is_raid_with_meta(seg) || !seg->meta_areas || lv_is_raid_metadata(seg->lv) || lv_is_raid_image(seg->lv)))
|
||||
goto out;
|
||||
|
||||
for (s = 0; s < seg->area_count; s++) {
|
||||
@@ -345,6 +345,91 @@ uint64_t lvseg_size(const struct lv_segment *seg)
|
||||
return (uint64_t) seg->len * seg->lv->vg->extent_size;
|
||||
}
|
||||
|
||||
dm_percent_t lvseg_percent_with_info_and_seg_status(const struct lv_with_info_and_seg_status *lvdm,
|
||||
percent_get_t type)
|
||||
{
|
||||
dm_percent_t p;
|
||||
uint64_t csize;
|
||||
const struct lv_segment *seg;
|
||||
const struct lv_seg_status *s = &lvdm->seg_status;
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
* Later move to segment methods, instead of using single place.
|
||||
* Also handle logic for mirror segments and it total_* summing
|
||||
* Esentially rework _target_percent API for segtype.
|
||||
*/
|
||||
switch (s->type) {
|
||||
case SEG_STATUS_CACHE:
|
||||
if (s->cache->fail || s->cache->error)
|
||||
p = DM_PERCENT_INVALID;
|
||||
else {
|
||||
switch (type) {
|
||||
case PERCENT_GET_DIRTY:
|
||||
p = (s->cache->used_blocks) ?
|
||||
dm_make_percent(s->cache->dirty_blocks,
|
||||
s->cache->used_blocks) : DM_PERCENT_0;
|
||||
break;
|
||||
case PERCENT_GET_METADATA:
|
||||
p = dm_make_percent(s->cache->metadata_used_blocks,
|
||||
s->cache->metadata_total_blocks);
|
||||
break;
|
||||
default:
|
||||
p = dm_make_percent(s->cache->used_blocks,
|
||||
s->cache->total_blocks);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SEG_STATUS_SNAPSHOT:
|
||||
if (s->snapshot->merge_failed)
|
||||
p = DM_PERCENT_INVALID;
|
||||
else if (s->snapshot->invalid)
|
||||
p = DM_PERCENT_100; /* Shown as 100% full */
|
||||
else if (s->snapshot->has_metadata_sectors &&
|
||||
(s->snapshot->used_sectors == s->snapshot->metadata_sectors))
|
||||
p = DM_PERCENT_0;
|
||||
else
|
||||
p = dm_make_percent(s->snapshot->used_sectors,
|
||||
s->snapshot->total_sectors);
|
||||
break;
|
||||
case SEG_STATUS_THIN_POOL:
|
||||
if (s->thin_pool->fail || s->thin_pool->error)
|
||||
p = DM_PERCENT_INVALID;
|
||||
else if (type == PERCENT_GET_METADATA)
|
||||
p = dm_make_percent(s->thin_pool->used_metadata_blocks,
|
||||
s->thin_pool->total_metadata_blocks);
|
||||
else
|
||||
p = dm_make_percent(s->thin_pool->used_data_blocks,
|
||||
s->thin_pool->total_data_blocks);
|
||||
break;
|
||||
case SEG_STATUS_THIN:
|
||||
if (s->thin->fail || (type != PERCENT_GET_DATA))
|
||||
/* TODO: expose highest mapped sector */
|
||||
p = DM_PERCENT_INVALID;
|
||||
else {
|
||||
seg = first_seg(lvdm->lv);
|
||||
/* Pool allocates whole chunk so round-up to nearest one */
|
||||
csize = first_seg(seg->pool_lv)->chunk_size;
|
||||
csize = ((seg->lv->size + csize - 1) / csize) * csize;
|
||||
if (s->thin->mapped_sectors <= csize)
|
||||
p = dm_make_percent(s->thin->mapped_sectors, csize);
|
||||
else {
|
||||
log_warn("WARNING: Thin volume %s maps %s while the size is only %s.",
|
||||
display_lvname(seg->lv),
|
||||
display_size(lvdm->lv->vg->cmd, s->thin->mapped_sectors),
|
||||
display_size(lvdm->lv->vg->cmd, csize));
|
||||
/* Don't show nonsense numbers like i.e. 1000% full */
|
||||
p = DM_PERCENT_100;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
p = DM_PERCENT_INVALID;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
uint32_t lv_kernel_read_ahead(const struct logical_volume *lv)
|
||||
{
|
||||
struct lvinfo info;
|
||||
@@ -452,8 +537,8 @@ struct logical_volume *lv_origin_lv(const struct logical_volume *lv)
|
||||
|
||||
if (lv_is_cow(lv))
|
||||
origin = origin_from_cow(lv);
|
||||
else if (lv_is_cache(lv) && first_seg(lv)->origin)
|
||||
origin = first_seg(lv)->origin;
|
||||
else if (lv_is_cache(lv) && !lv_is_pending_delete(lv))
|
||||
origin = seg_lv(first_seg(lv), 0);
|
||||
else if (lv_is_thin_volume(lv) && first_seg(lv)->origin)
|
||||
origin = first_seg(lv)->origin;
|
||||
else if (lv_is_thin_volume(lv) && first_seg(lv)->external_lv)
|
||||
@@ -493,14 +578,14 @@ char *lv_name_dup(struct dm_pool *mem, const struct logical_volume *lv)
|
||||
|
||||
char *lv_fullname_dup(struct dm_pool *mem, const struct logical_volume *lv)
|
||||
{
|
||||
char lvfullname[NAME_LEN * 2 + 2];
|
||||
char lvfullname[NAME_LEN * 2 + 2];
|
||||
|
||||
if (dm_snprintf(lvfullname, sizeof(lvfullname), "%s/%s", lv->vg->name, lv->name) < 0) {
|
||||
log_error("lvfullname snprintf failed");
|
||||
return NULL;
|
||||
}
|
||||
if (dm_snprintf(lvfullname, sizeof(lvfullname), "%s/%s", lv->vg->name, lv->name) < 0) {
|
||||
log_error("lvfullname snprintf failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dm_pool_strdup(mem, lvfullname);
|
||||
return dm_pool_strdup(mem, lvfullname);
|
||||
}
|
||||
|
||||
struct logical_volume *lv_parent(const struct logical_volume *lv)
|
||||
@@ -846,7 +931,7 @@ char *lv_dmpath_dup(struct dm_pool *mem, const struct logical_volume *lv)
|
||||
if (!*lv->vg->name)
|
||||
return dm_pool_strdup(mem, "");
|
||||
|
||||
if (!(name = dm_build_dm_name(mem, lv->vg->name, lv->name, NULL))) {
|
||||
if (!(name = dm_build_dm_name(mem, lv->vg->name, lv->name, NULL))) {
|
||||
log_error("dm_build_dm_name failed");
|
||||
return NULL;
|
||||
}
|
||||
@@ -903,7 +988,6 @@ int lv_mirror_image_in_sync(const struct logical_volume *lv)
|
||||
int lv_raid_image_in_sync(const struct logical_volume *lv)
|
||||
{
|
||||
unsigned s;
|
||||
dm_percent_t percent;
|
||||
char *raid_health;
|
||||
struct lv_segment *seg, *raid_seg = NULL;
|
||||
|
||||
@@ -933,12 +1017,6 @@ int lv_raid_image_in_sync(const struct logical_volume *lv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!lv_raid_percent(raid_seg->lv, &percent))
|
||||
return_0;
|
||||
|
||||
if (percent == DM_PERCENT_100)
|
||||
return 1;
|
||||
|
||||
/* Find out which sub-LV this is. */
|
||||
for (s = 0; s < raid_seg->area_count; s++)
|
||||
if (seg_lv(raid_seg, s) == lv)
|
||||
@@ -1012,7 +1090,7 @@ int lv_raid_healthy(const struct logical_volume *lv)
|
||||
/* Find out which sub-LV this is. */
|
||||
for (s = 0; s < raid_seg->area_count; s++)
|
||||
if ((lv_is_raid_image(lv) && (seg_lv(raid_seg, s) == lv)) ||
|
||||
(lv_is_raid_metadata(lv) && (seg_metalv(raid_seg,s) == lv)))
|
||||
(lv_is_raid_metadata(lv) && (seg_metalv(raid_seg, s) == lv)))
|
||||
break;
|
||||
if (s == raid_seg->area_count) {
|
||||
log_error(INTERNAL_ERROR
|
||||
@@ -1029,7 +1107,6 @@ int lv_raid_healthy(const struct logical_volume *lv)
|
||||
|
||||
char *lv_attr_dup_with_info_and_seg_status(struct dm_pool *mem, const struct lv_with_info_and_seg_status *lvdm)
|
||||
{
|
||||
dm_percent_t snap_percent;
|
||||
const struct logical_volume *lv = lvdm->lv;
|
||||
struct lv_segment *seg;
|
||||
char *repstr;
|
||||
@@ -1057,9 +1134,9 @@ char *lv_attr_dup_with_info_and_seg_status(struct dm_pool *mem, const struct lv_
|
||||
else if (lv_is_cache_type(lv))
|
||||
repstr[0] = 'C';
|
||||
else if (lv_is_raid(lv))
|
||||
repstr[0] = (lv->status & LV_NOTSYNCED) ? 'R' : 'r';
|
||||
repstr[0] = (lv_is_not_synced(lv)) ? 'R' : 'r';
|
||||
else if (lv_is_mirror(lv))
|
||||
repstr[0] = (lv->status & LV_NOTSYNCED) ? 'M' : 'm';
|
||||
repstr[0] = (lv_is_not_synced(lv)) ? 'M' : 'm';
|
||||
else if (lv_is_thin_volume(lv))
|
||||
repstr[0] = lv_is_merging_origin(lv) ?
|
||||
'O' : (lv_is_merging_thin_snapshot(lv) ? 'S' : 'V');
|
||||
@@ -1121,19 +1198,18 @@ char *lv_attr_dup_with_info_and_seg_status(struct dm_pool *mem, const struct lv_
|
||||
repstr[4] = 'd'; /* Inactive without table */
|
||||
|
||||
/* Snapshot dropped? */
|
||||
if (lvdm->info.live_table && lv_is_cow(lv)) {
|
||||
if (!lv_snapshot_percent(lv, &snap_percent) ||
|
||||
snap_percent == DM_PERCENT_INVALID) {
|
||||
if (lvdm->info.live_table &&
|
||||
(lvdm->seg_status.type == SEG_STATUS_SNAPSHOT)) {
|
||||
if (lvdm->seg_status.snapshot->invalid) {
|
||||
if (lvdm->info.suspended)
|
||||
repstr[4] = 'S'; /* Susp Inv snapshot */
|
||||
else
|
||||
repstr[4] = 'I'; /* Invalid snapshot */
|
||||
}
|
||||
else if (snap_percent == LVM_PERCENT_MERGE_FAILED) {
|
||||
} else if (lvdm->seg_status.snapshot->merge_failed) {
|
||||
if (lvdm->info.suspended)
|
||||
repstr[4] = 'M'; /* Susp snapshot merge failed */
|
||||
else
|
||||
repstr[4] = 'm'; /* snapshot merge failed */
|
||||
repstr[4] = 'm'; /* Snapshot merge failed */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1342,6 +1418,7 @@ int lv_active_change(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
enum activation_change activate, int needs_exclusive)
|
||||
{
|
||||
const char *ay_with_mode = NULL;
|
||||
struct lv_segment *seg = first_seg(lv);
|
||||
|
||||
if (activate == CHANGE_ASY)
|
||||
ay_with_mode = "sh";
|
||||
@@ -1350,67 +1427,81 @@ int lv_active_change(struct cmd_context *cmd, struct logical_volume *lv,
|
||||
|
||||
if (is_change_activating(activate) &&
|
||||
!lockd_lv(cmd, lv, ay_with_mode, LDLV_PERSISTENT)) {
|
||||
log_error("Failed to lock logical volume %s/%s", lv->vg->name, lv->name);
|
||||
log_error("Failed to lock logical volume %s.", display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (activate) {
|
||||
case CHANGE_AN:
|
||||
deactivate:
|
||||
log_verbose("Deactivating logical volume \"%s\"", lv->name);
|
||||
log_verbose("Deactivating logical volume %s.", display_lvname(lv));
|
||||
if (!deactivate_lv(cmd, lv))
|
||||
return_0;
|
||||
break;
|
||||
case CHANGE_ALN:
|
||||
if (vg_is_clustered(lv->vg) && (needs_exclusive || _lv_is_exclusive(lv))) {
|
||||
if (!lv_is_active_locally(lv)) {
|
||||
log_error("Cannot deactivate remotely exclusive device locally.");
|
||||
log_error("Cannot deactivate remotely exclusive device %s locally.",
|
||||
display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
/* Unlock whole exclusive activation */
|
||||
goto deactivate;
|
||||
}
|
||||
log_verbose("Deactivating logical volume \"%s\" locally.",
|
||||
lv->name);
|
||||
log_verbose("Deactivating logical volume %s locally.",
|
||||
display_lvname(lv));
|
||||
if (!deactivate_lv_local(cmd, lv))
|
||||
return_0;
|
||||
break;
|
||||
case CHANGE_ALY:
|
||||
case CHANGE_AAY:
|
||||
if (!raid4_is_supported(cmd, seg->segtype))
|
||||
goto no_raid4;
|
||||
|
||||
if (needs_exclusive || _lv_is_exclusive(lv)) {
|
||||
log_verbose("Activating logical volume \"%s\" exclusively locally.",
|
||||
lv->name);
|
||||
log_verbose("Activating logical volume %s exclusively locally.",
|
||||
display_lvname(lv));
|
||||
if (!activate_lv_excl_local(cmd, lv))
|
||||
return_0;
|
||||
} else {
|
||||
log_verbose("Activating logical volume \"%s\" locally.",
|
||||
lv->name);
|
||||
log_verbose("Activating logical volume %s locally.",
|
||||
display_lvname(lv));
|
||||
if (!activate_lv_local(cmd, lv))
|
||||
return_0;
|
||||
}
|
||||
break;
|
||||
case CHANGE_AEY:
|
||||
exclusive:
|
||||
log_verbose("Activating logical volume \"%s\" exclusively.",
|
||||
lv->name);
|
||||
if (!raid4_is_supported(cmd, seg->segtype))
|
||||
goto no_raid4;
|
||||
|
||||
log_verbose("Activating logical volume %s exclusively.",
|
||||
display_lvname(lv));
|
||||
if (!activate_lv_excl(cmd, lv))
|
||||
return_0;
|
||||
break;
|
||||
case CHANGE_ASY:
|
||||
case CHANGE_AY:
|
||||
default:
|
||||
if (!raid4_is_supported(cmd, seg->segtype))
|
||||
goto no_raid4;
|
||||
|
||||
if (needs_exclusive || _lv_is_exclusive(lv))
|
||||
goto exclusive;
|
||||
log_verbose("Activating logical volume \"%s\".", lv->name);
|
||||
log_verbose("Activating logical volume %s.", display_lvname(lv));
|
||||
if (!activate_lv(cmd, lv))
|
||||
return_0;
|
||||
}
|
||||
|
||||
if (!is_change_activating(activate) &&
|
||||
!lockd_lv(cmd, lv, "un", LDLV_PERSISTENT))
|
||||
log_error("Failed to unlock logical volume %s/%s", lv->vg->name, lv->name);
|
||||
log_error("Failed to unlock logical volume %s.", display_lvname(lv));
|
||||
|
||||
return 1;
|
||||
|
||||
no_raid4:
|
||||
log_error("Failed to activate %s LV %s", lvseg_name(seg), display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *lv_active_dup(struct dm_pool *mem, const struct logical_volume *lv)
|
||||
@@ -1463,13 +1554,16 @@ const struct logical_volume *lv_lock_holder(const struct logical_volume *lv)
|
||||
if (lv_is_cow(lv))
|
||||
return lv_lock_holder(origin_from_cow(lv));
|
||||
|
||||
if (lv_is_thin_pool(lv))
|
||||
if (lv_is_thin_pool(lv)) {
|
||||
/* Find any active LV from the pool */
|
||||
dm_list_iterate_items(sl, &lv->segs_using_this_lv)
|
||||
if (lv_is_active(sl->seg->lv)) {
|
||||
log_debug("Thin volume \"%s\" is active.", sl->seg->lv->name);
|
||||
log_debug_activation("Thin volume %s is active.",
|
||||
display_lvname(lv));
|
||||
return sl->seg->lv;
|
||||
}
|
||||
return lv;
|
||||
}
|
||||
|
||||
/* RAID changes visibility of splitted LVs but references them still as leg/meta */
|
||||
if ((lv_is_raid_image(lv) || lv_is_raid_metadata(lv)) && lv_is_visible(lv))
|
||||
@@ -1497,3 +1591,19 @@ struct profile *lv_config_profile(const struct logical_volume *lv)
|
||||
{
|
||||
return lv->profile ? : lv->vg->profile;
|
||||
}
|
||||
|
||||
int lv_has_constant_stripes(struct logical_volume *lv)
|
||||
{
|
||||
uint32_t previous_area_count = 0;
|
||||
struct lv_segment *seg;
|
||||
|
||||
dm_list_iterate_items(seg, &lv->segments) {
|
||||
if (!seg_is_striped(seg))
|
||||
return 0;
|
||||
if (previous_area_count && previous_area_count != seg->area_count)
|
||||
return 0;
|
||||
previous_area_count = seg->area_count;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@@ -197,4 +197,13 @@ char *lv_profile_dup(struct dm_pool *mem, const struct logical_volume *lv);
|
||||
char *lv_lock_args_dup(struct dm_pool *mem, const struct logical_volume *lv);
|
||||
char *lvseg_kernel_discards_dup_with_info_and_seg_status(struct dm_pool *mem, const struct lv_with_info_and_seg_status *lvdm);
|
||||
char *lv_time_dup(struct dm_pool *mem, const struct logical_volume *lv, int iso_mode);
|
||||
|
||||
typedef enum {
|
||||
PERCENT_GET_DATA = 0,
|
||||
PERCENT_GET_METADATA,
|
||||
PERCENT_GET_DIRTY
|
||||
} percent_get_t;
|
||||
dm_percent_t lvseg_percent_with_info_and_seg_status(const struct lv_with_info_and_seg_status *lvdm,
|
||||
percent_get_t type);
|
||||
|
||||
#endif /* _LVM_LV_H */
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user