mirror of
git://sourceware.org/git/lvm2.git
synced 2025-10-23 23:33:15 +03:00
Compare commits
832 Commits
v2_02_141
...
dev-bmr-dm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
e53ecf91f7 | ||
|
|
c76df666c9 | ||
|
|
e3efcdc9f5 | ||
|
|
16019b518e | ||
|
|
a2d2a61339 | ||
|
|
263c1a4db5 | ||
|
|
3c53acb378 | ||
|
|
49f32e4859 | ||
|
|
2a2efab0b1 | ||
|
|
c81005dcc4 | ||
|
|
07d92322c1 | ||
|
|
d90d99b23d | ||
|
|
e77b6968b6 | ||
|
|
c215e1be5c | ||
|
|
d8bdc7af37 | ||
|
|
84a5b19539 | ||
|
|
8b5967e1e3 | ||
|
|
588455d03e | ||
|
|
aa91fe3d3c | ||
|
|
ecae76c713 | ||
|
|
2bb33aaf55 | ||
|
|
379874a2d0 | ||
|
|
9d976c0002 | ||
|
|
eb8edccd58 | ||
|
|
546b2006dd | ||
|
|
31b11d0228 | ||
|
|
a88828c845 | ||
|
|
bc239f15eb | ||
|
|
d12ba022c8 | ||
|
|
d564d9f39c | ||
|
|
2dc6b97856 | ||
|
|
4ddf5a11cf | ||
|
|
17ad884836 | ||
|
|
69c2f56005 | ||
|
|
8c4b717f4d | ||
|
|
0bf5518626 | ||
|
|
412f09ca33 | ||
|
|
447daa9179 | ||
|
|
edcb3e65c6 | ||
|
|
99e96f3ce9 | ||
|
|
cbf99be43a | ||
|
|
556eba1835 | ||
|
|
9d4f9defc3 | ||
|
|
c9373a0c2a | ||
|
|
01181a299e | ||
|
|
4d095c2fbb | ||
|
|
11dd362454 | ||
|
|
1134ab6324 | ||
|
|
6e6f8025ff | ||
|
|
e44c4806db | ||
|
|
d00b70c789 | ||
|
|
0778d2e985 | ||
|
|
2763b928de | ||
|
|
7ce486e881 | ||
|
|
545b58542c | ||
|
|
22a71e1119 | ||
|
|
509b2e5247 | ||
|
|
57f468a53e | ||
|
|
600d7ca15f | ||
|
|
5c104c5de9 | ||
|
|
9553260673 | ||
|
|
5e9e43074a | ||
|
|
593900b795 | ||
|
|
fa904a844f | ||
|
|
6b1c0a4190 | ||
|
|
c5cd5b4b69 | ||
|
|
a5722ed7e7 | ||
|
|
c9cf85f606 | ||
|
|
2a17610899 | ||
|
|
1e380864e5 | ||
|
|
04a83148ce | ||
|
|
ff867c2ef6 | ||
|
|
a4ef8fa25e | ||
|
|
e41ee70acc | ||
|
|
a6a32a7c0e | ||
|
|
a400eba174 | ||
|
|
50633039bf | ||
|
|
59b0bd7b64 | ||
|
|
88c19d5d72 | ||
|
|
7e5881dd13 | ||
|
|
d1c1f60047 | ||
|
|
e2ceb90095 | ||
|
|
f5cf6cc9ed | ||
|
|
9cccf5245a | ||
|
|
e2cd882f2e | ||
|
|
662090e358 | ||
|
|
6954de22e2 | ||
|
|
150cff9cce | ||
|
|
7ae5f6fc5c | ||
|
|
ff09bda7d5 | ||
|
|
f0179fac31 | ||
|
|
c68fb55ac1 | ||
|
|
3f6e7f90ca | ||
|
|
9a299d2d7a | ||
|
|
579bd92009 | ||
|
|
56c68b3476 | ||
|
|
a28c81cbae | ||
|
|
1be74cfd7f | ||
|
|
0f7975cb35 | ||
|
|
147c9c01a2 | ||
|
|
fe65a86cbc | ||
|
|
07a60b59f7 | ||
|
|
9e14abb887 | ||
|
|
3aab784aa5 | ||
|
|
af148a9d77 | ||
|
|
5cfa6cb347 | ||
|
|
74e704bb44 | ||
|
|
a09d65891f | ||
|
|
f40dfb48ad | ||
|
|
e6768997e1 | ||
|
|
ed0c779bd1 | ||
|
|
50866034a2 | ||
|
|
f2e59e05ed | ||
|
|
248f47d489 | ||
|
|
9ab53dddf5 | ||
|
|
8b2108e6b1 | ||
|
|
097a724bda | ||
|
|
e2d4f53c82 | ||
|
|
7dcbf1dfd0 | ||
|
|
f19ec0e36d | ||
|
|
55001ae9ec | ||
|
|
307ab2c179 | ||
|
|
07c25429e2 | ||
|
|
9be7bca4be | ||
|
|
0584e85736 | ||
|
|
261a85ced9 | ||
|
|
5b2227c2c1 | ||
|
|
7c1937f8df | ||
|
|
d4c03cf7b2 | ||
|
|
76fc41fb9d | ||
|
|
2d0d58b867 | ||
|
|
60befab773 | ||
|
|
42f04a0f77 | ||
|
|
9f28eb4c20 | ||
|
|
15d1824fac | ||
|
|
7ed5a65ee5 | ||
|
|
91d32f9d1b | ||
|
|
0e774d5ae7 | ||
|
|
c577984630 | ||
|
|
bc5376b151 | ||
|
|
5034bb8d18 | ||
|
|
b10253ab4d | ||
|
|
86db143307 | ||
|
|
fc7dacaa4c | ||
|
|
8bbec41bd4 | ||
|
|
fe7ae37f5e | ||
|
|
9a086a6607 | ||
|
|
06ef7ba876 | ||
|
|
8b258a005b | ||
|
|
52e0d0db44 | ||
|
|
8c27c52749 | ||
|
|
91bb202ded | ||
|
|
bc0372cf9d | ||
|
|
d6e8f18e4d | ||
|
|
b9d07f7a12 | ||
|
|
a5d53aec83 | ||
|
|
d243db5466 | ||
|
|
109b7e2095 | ||
|
|
37b548c6b5 | ||
|
|
6129d2e64d | ||
|
|
82d92009ae | ||
|
|
1b0775916b | ||
|
|
bb93a28bc1 | ||
|
|
c1d376b1ab | ||
|
|
c6d383a6da | ||
|
|
1056894f1f | ||
|
|
cf39346697 | ||
|
|
65d2d66d02 | ||
|
|
1216efdf15 | ||
|
|
94f78e0183 | ||
|
|
3d26d283ea | ||
|
|
6a915270fc | ||
|
|
ed002ed22a | ||
|
|
2a47f0957f | ||
|
|
e69e81388d | ||
|
|
16b1272597 | ||
|
|
ca28ea88be | ||
|
|
a0c7875c54 | ||
|
|
86e9d12b6f | ||
|
|
cc57cd39ab | ||
|
|
8fad9b9e5d | ||
|
|
ca7bac53cf | ||
|
|
f231bdb20b | ||
|
|
03b0a78640 | ||
|
|
65d9f742f8 | ||
|
|
e58d24293f | ||
|
|
0d4b6bdfc0 | ||
|
|
6c10d1f3d9 | ||
|
|
2879eff86e | ||
|
|
464ebe04db | ||
|
|
51ed48f855 | ||
|
|
746f8af2a8 | ||
|
|
0d5b9deff2 | ||
|
|
7daba90fa3 | ||
|
|
a39563c0b0 | ||
|
|
a78e696070 | ||
|
|
2159a1429d | ||
|
|
6807eb8863 | ||
|
|
0f10823ec9 | ||
|
|
9f4451193b | ||
|
|
5bfc0edd12 | ||
|
|
00e641ef37 | ||
|
|
8507db1924 | ||
|
|
b36cf94318 | ||
|
|
8e9deb2e70 | ||
|
|
5aade9c402 | ||
|
|
a494d85ef6 | ||
|
|
386208e593 | ||
|
|
86a23b2145 | ||
|
|
70cbd8f1a5 | ||
|
|
5c415afd85 | ||
|
|
569ba79abf | ||
|
|
29d1533a49 | ||
|
|
9918d95490 | ||
|
|
8f01ee3035 | ||
|
|
18291fd016 | ||
|
|
e72184bb76 | ||
|
|
55d1105fc9 | ||
|
|
5434e9f5ea | ||
|
|
e655ccb418 | ||
|
|
d7ca164597 | ||
|
|
cb968ee875 | ||
|
|
c6e1845f76 | ||
|
|
2cf13b701a | ||
|
|
6d19c14c28 | ||
|
|
3f5629302a | ||
|
|
90c5d470c2 | ||
|
|
6cf1eff46d | ||
|
|
a8319e62c0 | ||
|
|
2d5dc6512e | ||
|
|
18cf5e8e67 | ||
|
|
d03b1779b4 | ||
|
|
7b15ca5c9a | ||
|
|
68ec240b99 | ||
|
|
6c78175126 | ||
|
|
f94fe2c91f | ||
|
|
9720898c8e | ||
|
|
67c5006e12 | ||
|
|
967a0889a0 | ||
|
|
418b7c6be8 | ||
|
|
e447ab87e9 | ||
|
|
fa06c2263b | ||
|
|
27245d97e5 | ||
|
|
b114b4d723 | ||
|
|
ad9cbe2714 | ||
|
|
5764c484ea | ||
|
|
9a78c258ba | ||
|
|
45c82260db | ||
|
|
b39473a537 | ||
|
|
8a601454e1 | ||
|
|
1297b0c8be | ||
|
|
fc628e92ba | ||
|
|
0ab1110164 | ||
|
|
ff6e124a33 | ||
|
|
74272e163d | ||
|
|
53b064b9ae | ||
|
|
f833a6d074 | ||
|
|
ccebf3100d | ||
|
|
b1399090cd | ||
|
|
f545dd5952 | ||
|
|
9a34de9cb9 | ||
|
|
8f47119f6f | ||
|
|
0339a5f447 | ||
|
|
715f1fb4c4 | ||
|
|
f228750386 | ||
|
|
8b9953e8c5 | ||
|
|
2af5971523 | ||
|
|
e112f5ce05 | ||
|
|
28f96d5ab6 | ||
|
|
2e7a121062 | ||
|
|
f7e0a4cc18 | ||
|
|
baee45a103 | ||
|
|
673bc0636c | ||
|
|
a61bc70f62 | ||
|
|
c45af2df4e | ||
|
|
a0842d1f25 | ||
|
|
54d3d976c7 | ||
|
|
3a0ef77305 | ||
|
|
790b2e8748 | ||
|
|
eeaa5a4481 | ||
|
|
937f72b168 | ||
|
|
e573eca554 | ||
|
|
5e718ec666 | ||
|
|
49839b6d96 | ||
|
|
e04a0184cb | ||
|
|
a975dc530e | ||
|
|
25bf0beedb | ||
|
|
fd349cb2e5 | ||
|
|
88ce15004e | ||
|
|
3d610fabbd | ||
|
|
57cd94b9e3 | ||
|
|
afae3355f8 | ||
|
|
058eb3739d | ||
|
|
3b93c11030 | ||
|
|
872d5922e1 | ||
|
|
36d0dcef38 | ||
|
|
8173c2ff9a | ||
|
|
b2d819eafa | ||
|
|
e3b904fd08 | ||
|
|
250b915364 | ||
|
|
4013e21ba8 | ||
|
|
731a9c1354 | ||
|
|
9898126596 | ||
|
|
183bd8ca03 | ||
|
|
2988fa3c21 | ||
|
|
85a593c191 | ||
|
|
3e31f51869 | ||
|
|
88530b2ef3 | ||
|
|
081359f6cd | ||
|
|
26b826ccf5 | ||
|
|
0d620e681f | ||
|
|
f1bc68beb4 | ||
|
|
045d086a57 | ||
|
|
23a4801dbc | ||
|
|
1a9f743eef | ||
|
|
e9b523e304 | ||
|
|
5c29b54d4d | ||
|
|
cdcf4cc794 | ||
|
|
ec8a75a321 | ||
|
|
86a651854f | ||
|
|
abd9618dd8 | ||
|
|
a68e601886 | ||
|
|
172bad0d56 | ||
|
|
66e175702a | ||
|
|
79f2596215 | ||
|
|
3d3b132d9a | ||
|
|
11fd7b7c64 | ||
|
|
d4a5c252c5 | ||
|
|
2d630ea78a | ||
|
|
3dae416229 | ||
|
|
1f8fd5a152 | ||
|
|
985ed7822f | ||
|
|
42ae586fa7 | ||
|
|
a77ded3001 | ||
|
|
4de6caf5b5 | ||
|
|
c201ee09bd | ||
|
|
04d34da706 | ||
|
|
30c69b3f8a | ||
|
|
0cd7d2332c | ||
|
|
a9940bd3c9 | ||
|
|
749a7cecf8 | ||
|
|
6a1c22aadf | ||
|
|
71671778ab | ||
|
|
ff2267012a | ||
|
|
5dd615c41e | ||
|
|
bafbc72c8c | ||
|
|
5e5ad77f5f | ||
|
|
3ac860334b | ||
|
|
718a695b0c | ||
|
|
664fa6d1be | ||
|
|
77f034bfa3 | ||
|
|
e793253084 | ||
|
|
79809d6cdc | ||
|
|
9267e0c5e7 | ||
|
|
63c052b9e2 | ||
|
|
ddfec5b51a | ||
|
|
9e3a9eab0e | ||
|
|
a9634e993a | ||
|
|
68955a8102 | ||
|
|
9b92cb2760 | ||
|
|
eaaae185b7 | ||
|
|
b08eb91df1 | ||
|
|
2e168a52b0 | ||
|
|
6d6e063a0f | ||
|
|
eccc91f9b0 | ||
|
|
0236a34224 | ||
|
|
42da7a6c38 | ||
|
|
f54253d396 | ||
|
|
5d7e83c73c | ||
|
|
c0b836e316 | ||
|
|
05cc1b87a9 | ||
|
|
e717ce555b | ||
|
|
7d8a67714f | ||
|
|
dbc71dc05e | ||
|
|
293aabe4cd | ||
|
|
f501f083bf | ||
|
|
05eb87ca2d | ||
|
|
ecc0406886 | ||
|
|
21034644b6 | ||
|
|
64aab5885d | ||
|
|
70d0b210e1 | ||
|
|
d7dd8bf9d6 | ||
|
|
f4efc2435c | ||
|
|
ec81c42179 | ||
|
|
367fb85e44 | ||
|
|
c716813651 | ||
|
|
161ae36363 | ||
|
|
ed5e5c38b5 | ||
|
|
3178cfc818 | ||
|
|
2d6ef123a6 | ||
|
|
fba54ae55e | ||
|
|
275c9f7e77 | ||
|
|
30a73d1604 | ||
|
|
44b3909e58 | ||
|
|
3ef36f05b6 | ||
|
|
8b6cad3219 | ||
|
|
eefcbd5b28 | ||
|
|
04ab1fa572 | ||
|
|
a89ddda1a2 | ||
|
|
42b394c142 | ||
|
|
bcf7f80791 | ||
|
|
485bce1ae3 | ||
|
|
f31d596c0d | ||
|
|
0358ca3aad | ||
|
|
0fb3669d49 | ||
|
|
ecfa465366 | ||
|
|
1a141e8623 | ||
|
|
ef5583a209 | ||
|
|
bc19a16fc7 | ||
|
|
e5fc48411a | ||
|
|
7dce72ee18 | ||
|
|
6af2ce6244 | ||
|
|
887e96ded7 | ||
|
|
0f353e2053 | ||
|
|
545e58fd18 | ||
|
|
5987562cf9 | ||
|
|
055c628e38 | ||
|
|
065526c590 | ||
|
|
b077e7374f | ||
|
|
13f3e92632 | ||
|
|
45be3c875f | ||
|
|
099d99975c | ||
|
|
cc9e683adc | ||
|
|
0000db7f98 | ||
|
|
abbaeef096 | ||
|
|
698e0eb851 | ||
|
|
8ad93874d6 | ||
|
|
a4e25f4381 | ||
|
|
2f00d57e6f | ||
|
|
9b9f1ae772 | ||
|
|
08de88535e | ||
|
|
db494d7d34 | ||
|
|
d84a80afb5 | ||
|
|
531ced90dc | ||
|
|
f75e42c06c | ||
|
|
e0b1415105 | ||
|
|
d97f1c89de | ||
|
|
3436d5b791 | ||
|
|
b6e3080fff | ||
|
|
73f1d444c8 | ||
|
|
52999133a3 | ||
|
|
d320d9c52b | ||
|
|
10128c9bd6 | ||
|
|
2950adc2ab | ||
|
|
4cbaaa5c98 | ||
|
|
71ea2e1602 | ||
|
|
7593221f94 | ||
|
|
54b41db9a6 | ||
|
|
a522af93b7 | ||
|
|
4361543f3e | ||
|
|
952498ef4d | ||
|
|
228b8245e4 | ||
|
|
672deaebc5 | ||
|
|
337aa4ca8e | ||
|
|
0daf9d7ac5 | ||
|
|
a077a64983 | ||
|
|
befe0078ad | ||
|
|
ba111e7f4a | ||
|
|
53058e5234 | ||
|
|
cc23fdbd13 | ||
|
|
5a32d2c1d9 | ||
|
|
032cf8ade6 | ||
|
|
acf7815aca | ||
|
|
740d27f9fe | ||
|
|
3f916e8285 | ||
|
|
0baf66a992 | ||
|
|
f91622741f | ||
|
|
e32e793c43 | ||
|
|
3b76e9fd98 | ||
|
|
02f2916b5b | ||
|
|
8423be80ee | ||
|
|
c0e0f5a923 | ||
|
|
1498bc8cc4 | ||
|
|
ec43f55445 | ||
|
|
762b0d697f | ||
|
|
0ad40a76c0 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -7,6 +7,8 @@
|
||||
*.orig
|
||||
*.pc
|
||||
*.pot
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.rej
|
||||
*.so
|
||||
*.so.*
|
||||
|
||||
@@ -95,7 +95,7 @@ endif
|
||||
DISTCLEAN_TARGETS += cscope.out
|
||||
CLEAN_DIRS += autom4te.cache
|
||||
|
||||
check check_system check_cluster check_local check_lvmetad check_lvmpolld unit: all
|
||||
check check_system check_cluster check_local check_lvmetad check_lvmpolld check_lvmlockd_test check_lvmlockd_dlm check_lvmlockd_sanlock unit: all
|
||||
$(MAKE) -C test $(@)
|
||||
|
||||
conf.generate: tools
|
||||
@@ -166,8 +166,11 @@ install_tmpfiles_configuration:
|
||||
|
||||
LCOV_TRACES = libdm.info lib.info liblvm.info tools.info \
|
||||
libdaemon/client.info libdaemon/server.info \
|
||||
daemons/clvmd.info daemons/dmeventd.info \
|
||||
daemons/lvmetad.info
|
||||
daemons/clvmd.info \
|
||||
daemons/dmeventd.info \
|
||||
daemons/lvmetad.info \
|
||||
daemons/lvmlockd.info \
|
||||
daemons/lvmpolld.info
|
||||
|
||||
CLEAN_TARGETS += $(LCOV_TRACES)
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.02.115-git (2016-01-25)
|
||||
1.02.131-git (2016-07-06)
|
||||
|
||||
242
WHATS_NEW
242
WHATS_NEW
@@ -1,3 +1,245 @@
|
||||
Version 2.02.161 -
|
||||
================================
|
||||
|
||||
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.
|
||||
Add str_list_wipe.
|
||||
Improve support for interrupting procesing of volumes during lvchange.
|
||||
Use failed command return code when lvchanging read-only volume.
|
||||
Show creation transaction_id and zeroing state of pool with thin volume.
|
||||
Stop checking for dm_cache_mq policy with cache target 1.9 (alias to smq).
|
||||
Check first /sys/module/dm_* dir existance before using modprobe.
|
||||
Remove mpath from 10-dm.rules, superseded by 11-dm-mpath.rules (mpath>=0.6.0).
|
||||
|
||||
Version 2.02.151 - 23rd April 2016
|
||||
==================================
|
||||
Fix error path after reusing of _setup_task (2.02.150).
|
||||
Fix memory access for empty sysfs values (2.02.149).
|
||||
Disable lvmetad when lvm1 metadata is seen, so commands revert to scanning.
|
||||
Suppress errors when snapshot merge gets delayed because volume is in use.
|
||||
Avoid internal snapshot LV names in messages.
|
||||
Autodetect and use /run/lock dir when available instead of /var/lock.
|
||||
lvchange --refresh for merging thin origin will retry to deactivate snapshot.
|
||||
Recognize in-progress snapshot merge for thin volumes from dm table.
|
||||
Avoid deciding to initiate a pending snapshot merge during resume.
|
||||
Improve retrying lvmetad requests while lvmetad is being updated.
|
||||
Read devices instead of using the lvmetad cache if rescan fails.
|
||||
Move lvmetad token/filter check and device rescan to the start of commands.
|
||||
Don't try deactivating fictional internal LV before snapshot merge. (2.02.105)
|
||||
When not obtaining devs from udev, check they exist before caching them.
|
||||
Detect device mismatch also when compiling without udev support.
|
||||
|
||||
Version 2.02.150 - 9th April 2016
|
||||
=================================
|
||||
Avoid using flushing dm status ioctl when checking for usable DM device.
|
||||
Check for devices without LVM- uuid prefix only with kernels < 3.X.
|
||||
Reuse %FREE size aproximation with lvcreate -l%PVS thin-pool.
|
||||
Allow the lvmdump directory to exist already provided it is empty.
|
||||
Show lvconverted percentage with 2 decimal digits.
|
||||
Fix regression in suspend when repairing --type mirror (2.02.133).
|
||||
|
||||
Version 2.02.149 - 1st April 2016
|
||||
=================================
|
||||
Do not flush thin-pool when checking metadata fullness.
|
||||
Remove spurious error about no value in /sys/dev/block/major:minor/dm/uuid.
|
||||
Fix device mismatch detection for LV if persistent .cache file is used.
|
||||
Fix holder device not being found in /dev while sysfs has it during dev scan.
|
||||
|
||||
Version 2.02.148 - 26th March 2016
|
||||
==================================
|
||||
Introduce TARGET_NAME and MODULE NAME macros.
|
||||
Replace hard-coded module and target names with macros.
|
||||
Add pv_major and pv_minor report fields.
|
||||
Detect and warn about mismatch between devices used and assumed for an LV.
|
||||
|
||||
Version 2.02.147 - 19th March 2016
|
||||
==================================
|
||||
If available, use /proc/self/mountinfo to detect mounted volume in fsadm.
|
||||
Fix resize of stacked raid thin data volume (2.02.141).
|
||||
Fix test for lvremove failure in lvconvert --uncache (2.02.146).
|
||||
|
||||
Version 2.02.146 - 11th March 2016
|
||||
==================================
|
||||
More man page cleanups in lvconvert.
|
||||
Fix makefile vpath in /udev when generating udev rules files.
|
||||
Another attempt to improve VG name parsing for lvconvert (2.02.144).
|
||||
Use new cache status info and skip flushing for failed cache.
|
||||
Support --uncache with missing PVs.
|
||||
Tidy report field names, headings and widths.
|
||||
Add vgscan --notifydbus to send a dbus notification.
|
||||
Add dbus notification from commands after a PV/VG/LV changes state.
|
||||
|
||||
Version 2.02.145 - 4th March 2016
|
||||
=================================
|
||||
Make it possible to use lvremove and lvrename on historical LVs.
|
||||
For historical LVs, report 'none' for lv_layout and 'history' for lv_role.
|
||||
Add full_{ancestors,descendants} fields to report LV ancestry with history.
|
||||
Report (h)istorical state within 5th bit (State) of the lv_attr field.
|
||||
Add lv_historical reporting field to report if LV is historical or not.
|
||||
Add lv_time_removed reporting field to display removal time for hist. LVs.
|
||||
Report lv_name, lv_uuid, vg_name, lv_time for historical LVs.
|
||||
Add --nohistory switch to lvremove to disable history recording on demand.
|
||||
Add -H|--history switch to lvs and lvdisplay to include historical LVs.
|
||||
Create historical LVs out of removed thin snapshot LVs and record in history.
|
||||
Add metadata/lvs_history_retention_time for automatic removal of hist. LVs.
|
||||
Add metadata/record_lvs_history config for switching LV history recording.
|
||||
Add support and infrastructure for tracking historical LVs.
|
||||
Improve lvconvert man page.
|
||||
Add kernel_cache_policy lvs field.
|
||||
Display [unknown] instead of 'unknown device' in pvs output.
|
||||
Fix error path when pvcreate allocation fails (2.02.144).
|
||||
Display [unknown] instead of blank for unknown VG names in pvs output.
|
||||
|
||||
Version 2.02.144 - 26th February 2016
|
||||
=====================================
|
||||
Use new PV processing code in pvcreate/vgcreate/vgextend/pvremove.
|
||||
Add new PV processing code that prompts user without locks held.
|
||||
Prevent lvmlockd blocking with new flag requiring sanlock 3.3.0.
|
||||
Only show (u)sed pv_attr char when PV is not (a)llocatable. (2.02.143)
|
||||
Update makefile to generate lcov output also for lvmpolld and lvmlockd.
|
||||
Fix SystemdService lvm2-lvmdbusd.service name.
|
||||
Improve support for env LVM_VG_NAME for reference VG name in lvconvert.
|
||||
Fix regression when lvresize accepted zero sizes. (2.02.141)
|
||||
Always warn user about PV in use even when pvremove uses --force --force.
|
||||
Use uninitialized pool header detection in all cases.
|
||||
Fix read error detection when checking for uninitialized thin-pool header.
|
||||
Fix error path for internal error in lvmetad VG lookup code.
|
||||
|
||||
Version 2.02.143 - 21st February 2016
|
||||
=====================================
|
||||
Fix error path when sending thin-pool message fails in update_pool_lv().
|
||||
Support reporting CheckNeeded and Fail state for thin-pool and thin LV.
|
||||
For failing thin-pool and thin volume correctly report percentage as INVALID.
|
||||
Report -1, not 'unkown' for lv_{snapshot_invalid,merge_failed} with --binary.
|
||||
Add configure --enable-dbus-service for an LVM D-Bus service.
|
||||
Replace configure --enable-python_bindings with python2 and python3 versions.
|
||||
If PV belongs to some VG and metadata missing, skip it if system ID is used.
|
||||
Automatically change PV header extension to latest version if writing PV/VG.
|
||||
Identify used PVs in pv_attr field by new 'u' character.
|
||||
Add pv_in_use reporting field to report if PV is used or not.
|
||||
Add pv_ext_vsn reporting field to report PV header extension version.
|
||||
Add protective flag marking PVs as used even if no metadata available.
|
||||
|
||||
Version 2.02.142 - 15th February 2016
|
||||
=====================================
|
||||
Fix memory pool corruption in pvmove (2.02.141).
|
||||
Support control of spare metadata creation when repairing thin-pool.
|
||||
Fix config type of 'log/verbose' from bool to int (2.02.99).
|
||||
Fix inverted data LV thinp watermark calc for dmeventd response (2.02.133).
|
||||
Use use_blkid_wiping=0 if not defined in lvm.conf and support not compiled in.
|
||||
Do not check for suspended devices if scanning for lvmetad update.
|
||||
Clear cached bootloader areas when PV format changed.
|
||||
Fix partn table filter with external_device_info_source="udev" and blkid<2.20.
|
||||
|
||||
Version 2.02.141 - 25th January 2016
|
||||
====================================
|
||||
Add metadata/check_pv_device_sizes switch to lvm.conf for device size checks.
|
||||
|
||||
91
WHATS_NEW_DM
91
WHATS_NEW_DM
@@ -1,3 +1,92 @@
|
||||
Version 1.02.131 -
|
||||
================================
|
||||
|
||||
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.
|
||||
|
||||
Version 1.02.123 - 23rd April 2016
|
||||
==================================
|
||||
Do not strip LVM- when debug reporting not found uuid.
|
||||
|
||||
Version 1.02.122 - 9th April 2016
|
||||
=================================
|
||||
Change log_debug ioctl flags from single characters into words.
|
||||
|
||||
Version 1.02.121 - 26th March 2016
|
||||
==================================
|
||||
Adjust raid status function.
|
||||
|
||||
Version 1.02.120 - 11th March 2016
|
||||
==================================
|
||||
Improve parsing of cache status and report Fail, Error, needs_check, ro.
|
||||
|
||||
Version 1.02.119 - 4th March 2016
|
||||
=================================
|
||||
Fix dm_config_write_node and variants to return error on subsection failures.
|
||||
Remove 4096 char limit due to buffer size if writing dm_config_node.
|
||||
|
||||
Version 1.02.118 - 26th February 2016
|
||||
=====================================
|
||||
Fix string boundary check in _get_canonical_field_name().
|
||||
Always initialized hist struct in _stats_parse_histogram().
|
||||
|
||||
Version 1.02.117 - 21st February 2016
|
||||
=====================================
|
||||
Improve status parsing for thin-pool and thin devices.
|
||||
|
||||
Version 1.02.116 - 15th February 2016
|
||||
=====================================
|
||||
Use fully aligned allocations for dm_pool_strdup/strndup() (1.02.64).
|
||||
Fix thin-pool table parameter feature order to match kernel output.
|
||||
|
||||
Version 1.02.115 - 25th January 2016
|
||||
====================================
|
||||
Fix man page for dmsetup udevcreatecookie.
|
||||
@@ -54,7 +143,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.
|
||||
|
||||
311
aclocal.m4
vendored
311
aclocal.m4
vendored
@@ -12,6 +12,63 @@
|
||||
# PARTICULAR PURPOSE.
|
||||
|
||||
m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_python_module.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_PYTHON_MODULE(modname[, fatal, python])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Checks for Python module.
|
||||
#
|
||||
# If fatal is non-empty then absence of a module will trigger an error.
|
||||
# The third parameter can either be "python" for Python 2 or "python3" for
|
||||
# Python 3; defaults to Python 3.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Andrew Collier
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 8
|
||||
|
||||
AU_ALIAS([AC_PYTHON_MODULE], [AX_PYTHON_MODULE])
|
||||
AC_DEFUN([AX_PYTHON_MODULE],[
|
||||
if test -z $PYTHON;
|
||||
then
|
||||
if test -z "$3";
|
||||
then
|
||||
PYTHON="python3"
|
||||
else
|
||||
PYTHON="$3"
|
||||
fi
|
||||
fi
|
||||
PYTHON_NAME=`basename $PYTHON`
|
||||
AC_MSG_CHECKING($PYTHON_NAME module: $1)
|
||||
$PYTHON -c "import $1" 2>/dev/null
|
||||
if test $? -eq 0;
|
||||
then
|
||||
AC_MSG_RESULT(yes)
|
||||
eval AS_TR_CPP(HAVE_PYMOD_$1)=yes
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
eval AS_TR_CPP(HAVE_PYMOD_$1)=no
|
||||
#
|
||||
if test -n "$2"
|
||||
then
|
||||
AC_MSG_ERROR(failed to find required module $1)
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
])
|
||||
|
||||
# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
|
||||
# serial 1 (pkg-config-0.24)
|
||||
#
|
||||
@@ -29,7 +86,7 @@ m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
@@ -227,4 +284,256 @@ AS_VAR_COPY([$1], [pkg_cv_][$1])
|
||||
AS_VAR_IF([$1], [""], [$5], [$4])dnl
|
||||
])# PKG_CHECK_VAR
|
||||
|
||||
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
|
||||
# AM_PATH_PYTHON([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
# ---------------------------------------------------------------------------
|
||||
# Adds support for distributing Python modules and packages. To
|
||||
# install modules, copy them to $(pythondir), using the python_PYTHON
|
||||
# automake variable. To install a package with the same name as the
|
||||
# automake package, install to $(pkgpythondir), or use the
|
||||
# pkgpython_PYTHON automake variable.
|
||||
#
|
||||
# The variables $(pyexecdir) and $(pkgpyexecdir) are provided as
|
||||
# locations to install python extension modules (shared libraries).
|
||||
# Another macro is required to find the appropriate flags to compile
|
||||
# extension modules.
|
||||
#
|
||||
# If your package is configured with a different prefix to python,
|
||||
# users will have to add the install directory to the PYTHONPATH
|
||||
# environment variable, or create a .pth file (see the python
|
||||
# documentation for details).
|
||||
#
|
||||
# If the MINIMUM-VERSION argument is passed, AM_PATH_PYTHON will
|
||||
# cause an error if the version of python installed on the system
|
||||
# doesn't meet the requirement. MINIMUM-VERSION should consist of
|
||||
# numbers and dots only.
|
||||
AC_DEFUN([AM_PATH_PYTHON],
|
||||
[
|
||||
dnl Find a Python interpreter. Python versions prior to 2.0 are not
|
||||
dnl supported. (2.0 was released on October 16, 2000).
|
||||
m4_define_default([_AM_PYTHON_INTERPRETER_LIST],
|
||||
[python python2 python3 python3.3 python3.2 python3.1 python3.0 python2.7 dnl
|
||||
python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python2.0])
|
||||
|
||||
AC_ARG_VAR([PYTHON], [the Python interpreter])
|
||||
|
||||
m4_if([$1],[],[
|
||||
dnl No version check is needed.
|
||||
# Find any Python interpreter.
|
||||
if test -z "$PYTHON"; then
|
||||
AC_PATH_PROGS([PYTHON], _AM_PYTHON_INTERPRETER_LIST, :)
|
||||
fi
|
||||
am_display_PYTHON=python
|
||||
], [
|
||||
dnl A version check is needed.
|
||||
if test -n "$PYTHON"; then
|
||||
# If the user set $PYTHON, use it and don't search something else.
|
||||
AC_MSG_CHECKING([whether $PYTHON version is >= $1])
|
||||
AM_PYTHON_CHECK_VERSION([$PYTHON], [$1],
|
||||
[AC_MSG_RESULT([yes])],
|
||||
[AC_MSG_RESULT([no])
|
||||
AC_MSG_ERROR([Python interpreter is too old])])
|
||||
am_display_PYTHON=$PYTHON
|
||||
else
|
||||
# Otherwise, try each interpreter until we find one that satisfies
|
||||
# VERSION.
|
||||
AC_CACHE_CHECK([for a Python interpreter with version >= $1],
|
||||
[am_cv_pathless_PYTHON],[
|
||||
for am_cv_pathless_PYTHON in _AM_PYTHON_INTERPRETER_LIST none; do
|
||||
test "$am_cv_pathless_PYTHON" = none && break
|
||||
AM_PYTHON_CHECK_VERSION([$am_cv_pathless_PYTHON], [$1], [break])
|
||||
done])
|
||||
# Set $PYTHON to the absolute path of $am_cv_pathless_PYTHON.
|
||||
if test "$am_cv_pathless_PYTHON" = none; then
|
||||
PYTHON=:
|
||||
else
|
||||
AC_PATH_PROG([PYTHON], [$am_cv_pathless_PYTHON])
|
||||
fi
|
||||
am_display_PYTHON=$am_cv_pathless_PYTHON
|
||||
fi
|
||||
])
|
||||
|
||||
if test "$PYTHON" = :; then
|
||||
dnl Run any user-specified action, or abort.
|
||||
m4_default([$3], [AC_MSG_ERROR([no suitable Python interpreter found])])
|
||||
else
|
||||
|
||||
dnl Query Python for its version number. Getting [:3] seems to be
|
||||
dnl the best way to do this; it's what "site.py" does in the standard
|
||||
dnl library.
|
||||
|
||||
AC_CACHE_CHECK([for $am_display_PYTHON version], [am_cv_python_version],
|
||||
[am_cv_python_version=`$PYTHON -c "import sys; sys.stdout.write(sys.version[[:3]])"`])
|
||||
AC_SUBST([PYTHON_VERSION], [$am_cv_python_version])
|
||||
|
||||
dnl Use the values of $prefix and $exec_prefix for the corresponding
|
||||
dnl values of PYTHON_PREFIX and PYTHON_EXEC_PREFIX. These are made
|
||||
dnl distinct variables so they can be overridden if need be. However,
|
||||
dnl general consensus is that you shouldn't need this ability.
|
||||
|
||||
AC_SUBST([PYTHON_PREFIX], ['${prefix}'])
|
||||
AC_SUBST([PYTHON_EXEC_PREFIX], ['${exec_prefix}'])
|
||||
|
||||
dnl At times (like when building shared libraries) you may want
|
||||
dnl to know which OS platform Python thinks this is.
|
||||
|
||||
AC_CACHE_CHECK([for $am_display_PYTHON platform], [am_cv_python_platform],
|
||||
[am_cv_python_platform=`$PYTHON -c "import sys; sys.stdout.write(sys.platform)"`])
|
||||
AC_SUBST([PYTHON_PLATFORM], [$am_cv_python_platform])
|
||||
|
||||
# Just factor out some code duplication.
|
||||
am_python_setup_sysconfig="\
|
||||
import sys
|
||||
# Prefer sysconfig over distutils.sysconfig, for better compatibility
|
||||
# with python 3.x. See automake bug#10227.
|
||||
try:
|
||||
import sysconfig
|
||||
except ImportError:
|
||||
can_use_sysconfig = 0
|
||||
else:
|
||||
can_use_sysconfig = 1
|
||||
# Can't use sysconfig in CPython 2.7, since it's broken in virtualenvs:
|
||||
# <https://github.com/pypa/virtualenv/issues/118>
|
||||
try:
|
||||
from platform import python_implementation
|
||||
if python_implementation() == 'CPython' and sys.version[[:3]] == '2.7':
|
||||
can_use_sysconfig = 0
|
||||
except ImportError:
|
||||
pass"
|
||||
|
||||
dnl Set up 4 directories:
|
||||
|
||||
dnl pythondir -- where to install python scripts. This is the
|
||||
dnl site-packages directory, not the python standard library
|
||||
dnl directory like in previous automake betas. This behavior
|
||||
dnl is more consistent with lispdir.m4 for example.
|
||||
dnl Query distutils for this directory.
|
||||
AC_CACHE_CHECK([for $am_display_PYTHON script directory],
|
||||
[am_cv_python_pythondir],
|
||||
[if test "x$prefix" = xNONE
|
||||
then
|
||||
am_py_prefix=$ac_default_prefix
|
||||
else
|
||||
am_py_prefix=$prefix
|
||||
fi
|
||||
am_cv_python_pythondir=`$PYTHON -c "
|
||||
$am_python_setup_sysconfig
|
||||
if can_use_sysconfig:
|
||||
sitedir = sysconfig.get_path('purelib', vars={'base':'$am_py_prefix'})
|
||||
else:
|
||||
from distutils import sysconfig
|
||||
sitedir = sysconfig.get_python_lib(0, 0, prefix='$am_py_prefix')
|
||||
sys.stdout.write(sitedir)"`
|
||||
case $am_cv_python_pythondir in
|
||||
$am_py_prefix*)
|
||||
am__strip_prefix=`echo "$am_py_prefix" | sed 's|.|.|g'`
|
||||
am_cv_python_pythondir=`echo "$am_cv_python_pythondir" | sed "s,^$am__strip_prefix,$PYTHON_PREFIX,"`
|
||||
;;
|
||||
*)
|
||||
case $am_py_prefix in
|
||||
/usr|/System*) ;;
|
||||
*)
|
||||
am_cv_python_pythondir=$PYTHON_PREFIX/lib/python$PYTHON_VERSION/site-packages
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
])
|
||||
AC_SUBST([pythondir], [$am_cv_python_pythondir])
|
||||
|
||||
dnl pkgpythondir -- $PACKAGE directory under pythondir. Was
|
||||
dnl PYTHON_SITE_PACKAGE in previous betas, but this naming is
|
||||
dnl more consistent with the rest of automake.
|
||||
|
||||
AC_SUBST([pkgpythondir], [\${pythondir}/$PACKAGE])
|
||||
|
||||
dnl pyexecdir -- directory for installing python extension modules
|
||||
dnl (shared libraries)
|
||||
dnl Query distutils for this directory.
|
||||
AC_CACHE_CHECK([for $am_display_PYTHON extension module directory],
|
||||
[am_cv_python_pyexecdir],
|
||||
[if test "x$exec_prefix" = xNONE
|
||||
then
|
||||
am_py_exec_prefix=$am_py_prefix
|
||||
else
|
||||
am_py_exec_prefix=$exec_prefix
|
||||
fi
|
||||
am_cv_python_pyexecdir=`$PYTHON -c "
|
||||
$am_python_setup_sysconfig
|
||||
if can_use_sysconfig:
|
||||
sitedir = sysconfig.get_path('platlib', vars={'platbase':'$am_py_prefix'})
|
||||
else:
|
||||
from distutils import sysconfig
|
||||
sitedir = sysconfig.get_python_lib(1, 0, prefix='$am_py_prefix')
|
||||
sys.stdout.write(sitedir)"`
|
||||
case $am_cv_python_pyexecdir in
|
||||
$am_py_exec_prefix*)
|
||||
am__strip_prefix=`echo "$am_py_exec_prefix" | sed 's|.|.|g'`
|
||||
am_cv_python_pyexecdir=`echo "$am_cv_python_pyexecdir" | sed "s,^$am__strip_prefix,$PYTHON_EXEC_PREFIX,"`
|
||||
;;
|
||||
*)
|
||||
case $am_py_exec_prefix in
|
||||
/usr|/System*) ;;
|
||||
*)
|
||||
am_cv_python_pyexecdir=$PYTHON_EXEC_PREFIX/lib/python$PYTHON_VERSION/site-packages
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
])
|
||||
AC_SUBST([pyexecdir], [$am_cv_python_pyexecdir])
|
||||
|
||||
dnl pkgpyexecdir -- $(pyexecdir)/$(PACKAGE)
|
||||
|
||||
AC_SUBST([pkgpyexecdir], [\${pyexecdir}/$PACKAGE])
|
||||
|
||||
dnl Run any user-specified action.
|
||||
$2
|
||||
fi
|
||||
|
||||
])
|
||||
|
||||
|
||||
# AM_PYTHON_CHECK_VERSION(PROG, VERSION, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
|
||||
# ---------------------------------------------------------------------------
|
||||
# Run ACTION-IF-TRUE if the Python interpreter PROG has version >= VERSION.
|
||||
# Run ACTION-IF-FALSE otherwise.
|
||||
# This test uses sys.hexversion instead of the string equivalent (first
|
||||
# word of sys.version), in order to cope with versions such as 2.2c1.
|
||||
# This supports Python 2.0 or higher. (2.0 was released on October 16, 2000).
|
||||
AC_DEFUN([AM_PYTHON_CHECK_VERSION],
|
||||
[prog="import sys
|
||||
# split strings by '.' and convert to numeric. Append some zeros
|
||||
# because we need at least 4 digits for the hex conversion.
|
||||
# map returns an iterator in Python 3.0 and a list in 2.x
|
||||
minver = list(map(int, '$2'.split('.'))) + [[0, 0, 0]]
|
||||
minverhex = 0
|
||||
# xrange is not present in Python 3.0 and range returns an iterator
|
||||
for i in list(range(0, 4)): minverhex = (minverhex << 8) + minver[[i]]
|
||||
sys.exit(sys.hexversion < minverhex)"
|
||||
AS_IF([AM_RUN_LOG([$1 -c "$prog"])], [$3], [$4])])
|
||||
|
||||
# Copyright (C) 2001-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# AM_RUN_LOG(COMMAND)
|
||||
# -------------------
|
||||
# Run COMMAND, save the exit status in ac_status, and log it.
|
||||
# (This has been adapted from Autoconf's _AC_RUN_LOG macro.)
|
||||
AC_DEFUN([AM_RUN_LOG],
|
||||
[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
|
||||
($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
|
||||
ac_status=$?
|
||||
echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
|
||||
(exit $ac_status); }])
|
||||
|
||||
m4_include([acinclude.m4])
|
||||
|
||||
170
autoconf/py-compile
Executable file
170
autoconf/py-compile
Executable file
@@ -0,0 +1,170 @@
|
||||
#!/bin/sh
|
||||
# py-compile - Compile a Python program
|
||||
|
||||
scriptversion=2011-06-08.12; # UTC
|
||||
|
||||
# Copyright (C) 2000-2014 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
if [ -z "$PYTHON" ]; then
|
||||
PYTHON=python
|
||||
fi
|
||||
|
||||
me=py-compile
|
||||
|
||||
usage_error ()
|
||||
{
|
||||
echo "$me: $*" >&2
|
||||
echo "Try '$me --help' for more information." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
basedir=
|
||||
destdir=
|
||||
while test $# -ne 0; do
|
||||
case "$1" in
|
||||
--basedir)
|
||||
if test $# -lt 2; then
|
||||
usage_error "option '--basedir' requires an argument"
|
||||
else
|
||||
basedir=$2
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
--destdir)
|
||||
if test $# -lt 2; then
|
||||
usage_error "option '--destdir' requires an argument"
|
||||
else
|
||||
destdir=$2
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
cat <<\EOF
|
||||
Usage: py-compile [--help] [--version] [--basedir DIR] [--destdir DIR] FILES..."
|
||||
|
||||
Byte compile some python scripts FILES. Use --destdir to specify any
|
||||
leading directory path to the FILES that you don't want to include in the
|
||||
byte compiled file. Specify --basedir for any additional path information you
|
||||
do want to be shown in the byte compiled file.
|
||||
|
||||
Example:
|
||||
py-compile --destdir /tmp/pkg-root --basedir /usr/share/test test.py test2.py
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v|--version)
|
||||
echo "$me $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
usage_error "unrecognized option '$1'"
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
files=$*
|
||||
if test -z "$files"; then
|
||||
usage_error "no files given"
|
||||
fi
|
||||
|
||||
# if basedir was given, then it should be prepended to filenames before
|
||||
# byte compilation.
|
||||
if [ -z "$basedir" ]; then
|
||||
pathtrans="path = file"
|
||||
else
|
||||
pathtrans="path = os.path.join('$basedir', file)"
|
||||
fi
|
||||
|
||||
# if destdir was given, then it needs to be prepended to the filename to
|
||||
# byte compile but not go into the compiled file.
|
||||
if [ -z "$destdir" ]; then
|
||||
filetrans="filepath = path"
|
||||
else
|
||||
filetrans="filepath = os.path.normpath('$destdir' + os.sep + path)"
|
||||
fi
|
||||
|
||||
$PYTHON -c "
|
||||
import sys, os, py_compile, imp
|
||||
|
||||
files = '''$files'''
|
||||
|
||||
sys.stdout.write('Byte-compiling python modules...\n')
|
||||
for file in files.split():
|
||||
$pathtrans
|
||||
$filetrans
|
||||
if not os.path.exists(filepath) or not (len(filepath) >= 3
|
||||
and filepath[-3:] == '.py'):
|
||||
continue
|
||||
sys.stdout.write(file)
|
||||
sys.stdout.flush()
|
||||
if hasattr(imp, 'get_tag'):
|
||||
py_compile.compile(filepath, imp.cache_from_source(filepath), path)
|
||||
else:
|
||||
py_compile.compile(filepath, filepath + 'c', path)
|
||||
sys.stdout.write('\n')" || exit $?
|
||||
|
||||
# this will fail for python < 1.5, but that doesn't matter ...
|
||||
$PYTHON -O -c "
|
||||
import sys, os, py_compile, imp
|
||||
|
||||
# pypy does not use .pyo optimization
|
||||
if hasattr(sys, 'pypy_translation_info'):
|
||||
sys.exit(0)
|
||||
|
||||
files = '''$files'''
|
||||
sys.stdout.write('Byte-compiling python modules (optimized versions) ...\n')
|
||||
for file in files.split():
|
||||
$pathtrans
|
||||
$filetrans
|
||||
if not os.path.exists(filepath) or not (len(filepath) >= 3
|
||||
and filepath[-3:] == '.py'):
|
||||
continue
|
||||
sys.stdout.write(file)
|
||||
sys.stdout.flush()
|
||||
if hasattr(imp, 'get_tag'):
|
||||
py_compile.compile(filepath, imp.cache_from_source(filepath, False), path)
|
||||
else:
|
||||
py_compile.compile(filepath, filepath + 'o', path)
|
||||
sys.stdout.write('\n')" 2>/dev/null || :
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -465,6 +478,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
|
||||
@@ -525,7 +587,7 @@ log {
|
||||
# debug output if the class is listed here. Classes currently
|
||||
# available: memory, devices, activation, allocation, lvmetad,
|
||||
# metadata, cache, locking, lvmpolld. Use "all" to see everything.
|
||||
debug_classes = [ "memory", "devices", "activation", "allocation", "lvmetad", "metadata", "cache", "locking", "lvmpolld" ]
|
||||
debug_classes = [ "memory", "devices", "activation", "allocation", "lvmetad", "metadata", "cache", "locking", "lvmpolld", "dbus" ]
|
||||
}
|
||||
|
||||
# Configuration section backup.
|
||||
@@ -851,13 +913,23 @@ global {
|
||||
# devices/global_filter.
|
||||
use_lvmetad = @DEFAULT_USE_LVMETAD@
|
||||
|
||||
# Configuration option global/lvmetad_update_wait_time.
|
||||
# The number of seconds a command will wait for lvmetad update to finish.
|
||||
# After waiting for this period, a command will not use lvmetad, and
|
||||
# will revert to disk scanning.
|
||||
# This configuration option has an automatic default value.
|
||||
# lvmetad_update_wait_time = 10
|
||||
|
||||
# Configuration option global/use_lvmlockd.
|
||||
# Use lvmlockd for locking among hosts using LVM on shared storage.
|
||||
# See lvmlockd(8) for more information.
|
||||
# Applicable only if LVM is compiled with lockd support in which
|
||||
# case there is also lvmlockd(8) man page available for more
|
||||
# information.
|
||||
use_lvmlockd = 0
|
||||
|
||||
# Configuration option global/lvmlockd_lock_retries.
|
||||
# Retry lvmlockd lock requests this many times.
|
||||
# Applicable only if LVM is compiled with lockd support
|
||||
# This configuration option has an automatic default value.
|
||||
# lvmlockd_lock_retries = 3
|
||||
|
||||
@@ -867,7 +939,8 @@ global {
|
||||
# LVs have been created, the internal LV needs to be extended. lvcreate
|
||||
# will automatically extend the internal LV when needed by the amount
|
||||
# specified here. Setting this to 0 disables the automatic extension
|
||||
# and can cause lvcreate to fail.
|
||||
# and can cause lvcreate to fail. Applicable only if LVM is compiled
|
||||
# with lockd support
|
||||
# This configuration option has an automatic default value.
|
||||
# sanlock_lv_extend = 256
|
||||
|
||||
@@ -1014,7 +1087,14 @@ global {
|
||||
# a native systemd service, which allows it to be started on demand,
|
||||
# and to use its own control group. When this option is disabled, LVM
|
||||
# commands will supervise long running operations by forking themselves.
|
||||
# Applicable only if LVM is compiled with lvmpolld support.
|
||||
use_lvmpolld = @DEFAULT_USE_LVMPOLLD@
|
||||
|
||||
# Configuration option global/notify_dbus.
|
||||
# Enable D-Bus notification from LVM commands.
|
||||
# When enabled, an LVM command that changes PVs, changes VG metadata,
|
||||
# or changes the activation state of an LV will send a notification.
|
||||
notify_dbus = 1
|
||||
}
|
||||
|
||||
# Configuration section activation.
|
||||
@@ -1426,6 +1506,22 @@ activation {
|
||||
# This configuration option has an automatic default value.
|
||||
# check_pv_device_sizes = 1
|
||||
|
||||
# Configuration option metadata/record_lvs_history.
|
||||
# When enabled, LVM keeps history records about removed LVs in
|
||||
# metadata. The information that is recorded in metadata for
|
||||
# historical LVs is reduced when compared to original
|
||||
# information kept in metadata for live LVs. Currently, this
|
||||
# feature is supported for thin and thin snapshot LVs only.
|
||||
# This configuration option has an automatic default value.
|
||||
# record_lvs_history = 0
|
||||
|
||||
# Configuration option metadata/lvs_history_retention_time.
|
||||
# Retention time in seconds after which a record about individual
|
||||
# historical logical volume is automatically destroyed.
|
||||
# A value of 0 disables this feature.
|
||||
# This configuration option has an automatic default value.
|
||||
# lvs_history_retention_time = 0
|
||||
|
||||
# Configuration option metadata/pvmetadatacopies.
|
||||
# Number of copies of metadata to store on each PV.
|
||||
# The --pvmetadatacopies option overrides this setting.
|
||||
@@ -1504,6 +1600,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
|
||||
@@ -1820,10 +1932,76 @@ 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.
|
||||
# mark_hidden_devices = 1
|
||||
|
||||
# Configuration option report/two_word_unknown_device.
|
||||
# Use the two words 'unknown device' in place of '[unknown]'.
|
||||
# This is displayed when the device for a PV is not known.
|
||||
# This configuration option has an automatic default value.
|
||||
# two_word_unknown_device = 0
|
||||
# }
|
||||
|
||||
# Configuration section dmeventd.
|
||||
|
||||
@@ -51,6 +51,7 @@ local {
|
||||
# Configuration option local/host_id.
|
||||
# The lvmlockd sanlock host_id.
|
||||
# This must be unique among all hosts, and must be between 1 and 2000.
|
||||
# Applicable only if LVM is compiled with lockd support
|
||||
# This configuration option has an automatic default value.
|
||||
# host_id = 0
|
||||
}
|
||||
|
||||
144
configure.in
144
configure.in
@@ -1,6 +1,6 @@
|
||||
###############################################################################
|
||||
## Copyright (C) 2000-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 copyrighted material is made available to anyone wishing to use,
|
||||
## modify, copy, or redistribute it subject to the terms and conditions
|
||||
@@ -11,7 +11,7 @@
|
||||
## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
################################################################################
|
||||
|
||||
AC_PREREQ(2.61)
|
||||
AC_PREREQ(2.69)
|
||||
################################################################################
|
||||
dnl -- Process this file with autoconf to produce a configure script.
|
||||
AC_INIT
|
||||
@@ -37,8 +37,8 @@ case "$host_os" in
|
||||
LDDEPS="$LDDEPS .export.sym"
|
||||
LIB_SUFFIX=so
|
||||
DEVMAPPER=yes
|
||||
LVMETAD=no
|
||||
LVMPOLLD=no
|
||||
BUILD_LVMETAD=no
|
||||
BUILD_LVMPOLLD=no
|
||||
LOCKDSANLOCK=no
|
||||
LOCKDDLM=no
|
||||
ODIRECT=yes
|
||||
@@ -85,6 +85,7 @@ AC_PROG_MKDIR_P
|
||||
AC_PROG_RANLIB
|
||||
AC_PATH_TOOL(CFLOW_CMD, cflow)
|
||||
AC_PATH_TOOL(CSCOPE_CMD, cscope)
|
||||
AC_PATH_TOOL(CHMOD, chmod)
|
||||
|
||||
################################################################################
|
||||
dnl -- Check for header files.
|
||||
@@ -1128,9 +1129,8 @@ AC_ARG_ENABLE(lvmetad,
|
||||
AC_HELP_STRING([--enable-lvmetad],
|
||||
[enable the LVM Metadata Daemon]),
|
||||
LVMETAD=$enableval)
|
||||
AC_MSG_RESULT($LVMETAD)
|
||||
|
||||
BUILD_LVMETAD=$LVMETAD
|
||||
test -n "$LVMETAD" && BUILD_LVMETAD=$LVMETAD
|
||||
AC_MSG_RESULT($BUILD_LVMETAD)
|
||||
|
||||
################################################################################
|
||||
dnl -- Build lvmpolld
|
||||
@@ -1139,9 +1139,8 @@ AC_ARG_ENABLE(lvmpolld,
|
||||
AC_HELP_STRING([--enable-lvmpolld],
|
||||
[enable the LVM Polling Daemon]),
|
||||
LVMPOLLD=$enableval)
|
||||
AC_MSG_RESULT($LVMPOLLD)
|
||||
|
||||
BUILD_LVMPOLLD=$LVMPOLLD
|
||||
test -n "$LVMPOLLD" && BUILD_LVMPOLLD=$LVMPOLLD
|
||||
AC_MSG_RESULT($BUILD_LVMPOLLD)
|
||||
|
||||
################################################################################
|
||||
BUILD_LVMLOCKD=no
|
||||
@@ -1158,7 +1157,7 @@ BUILD_LOCKDSANLOCK=$LOCKDSANLOCK
|
||||
|
||||
dnl -- Look for sanlock libraries
|
||||
if test "$BUILD_LOCKDSANLOCK" = yes; then
|
||||
PKG_CHECK_MODULES(LOCKD_SANLOCK, libsanlock_client, [HAVE_LOCKD_SANLOCK=yes], $bailout)
|
||||
PKG_CHECK_MODULES(LOCKD_SANLOCK, libsanlock_client >= 3.3.0, [HAVE_LOCKD_SANLOCK=yes], $bailout)
|
||||
AC_DEFINE([LOCKDSANLOCK_SUPPORT], 1, [Define to 1 to include code that uses lvmlockd sanlock option.])
|
||||
BUILD_LVMLOCKD=yes
|
||||
fi
|
||||
@@ -1187,8 +1186,10 @@ AC_MSG_CHECKING(whether to build lvmlockd)
|
||||
AC_MSG_RESULT($BUILD_LVMLOCKD)
|
||||
|
||||
if test "$BUILD_LVMLOCKD" = yes; then
|
||||
AS_IF([test -n "$BUILD_LVMPOLLD"], [BUILD_LVMPOLLD=yes; AC_MSG_WARN([Enabling lvmpolld - required by lvmlockd.])])
|
||||
AS_IF([test -n "$BUILD_LVMETAD"], [BUILD_LVMETAD=yes; AC_MSG_WARN([Enabling lvmetad - required by lvmlockd.])])
|
||||
AS_IF([test "$LVMPOLLD" = no], [AC_MSG_ERROR([cannot build lvmlockd with --disable-lvmpolld.])])
|
||||
AS_IF([test "$LVMETAD" = no], [AC_MSG_ERROR([cannot build lvmlockd with --disable-lvmetad.])])
|
||||
AS_IF([test "$BUILD_LVMPOLLD" = no], [BUILD_LVMPOLLD=yes; AC_MSG_WARN([Enabling lvmpolld - required by lvmlockd.])])
|
||||
AS_IF([test "$BUILD_LVMETAD" = no], [BUILD_LVMETAD=yes; AC_MSG_WARN([Enabling lvmetad - required by lvmlockd.])])
|
||||
AC_MSG_CHECKING([defaults for use_lvmlockd])
|
||||
AC_ARG_ENABLE(use_lvmlockd,
|
||||
AC_HELP_STRING([--disable-use-lvmlockd],
|
||||
@@ -1267,6 +1268,28 @@ fi
|
||||
AC_DEFINE_UNQUOTED(DEFAULT_USE_LVMPOLLD, [$DEFAULT_USE_LVMPOLLD],
|
||||
[Use lvmpolld by default.])
|
||||
|
||||
################################################################################
|
||||
dnl -- Build notifydbus
|
||||
AC_MSG_CHECKING(whether to build notifydbus)
|
||||
AC_ARG_ENABLE(notify-dbus,
|
||||
AC_HELP_STRING([--enable-notify-dbus],
|
||||
[enable LVM notification using dbus]),
|
||||
NOTIFYDBUS=$enableval)
|
||||
AC_MSG_RESULT($NOTIFYDBUS)
|
||||
|
||||
BUILD_NOTIFYDBUS=$NOTIFYDBUS
|
||||
|
||||
if test "$BUILD_NOTIFYDBUS" = yes; then
|
||||
AC_DEFINE([NOTIFYDBUS_SUPPORT], 1, [Define to 1 to include code that uses dbus notification.])
|
||||
LIBS="-lsystemd $LIBS"
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Look for dbus libraries
|
||||
if test "$BUILD_NOTIFYDBUS" = yes; then
|
||||
PKG_CHECK_MODULES(NOTIFY_DBUS, systemd >= 221, [HAVE_NOTIFY_DBUS=yes], $bailout)
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
|
||||
dnl -- Enable blkid wiping functionality
|
||||
@@ -1437,25 +1460,76 @@ test "$CMDLIB" = yes \
|
||||
&& LVM2CMD_LIB=-llvm2cmd \
|
||||
|| LVM2CMD_LIB=
|
||||
|
||||
################################################################################
|
||||
dnl -- Enable D-Bus service
|
||||
AC_MSG_CHECKING(whether to include Python D-Bus support)
|
||||
AC_ARG_ENABLE(dbus-service,
|
||||
AC_HELP_STRING([--enable-dbus-service], [install D-Bus support]),
|
||||
BUILD_LVMDBUSD=$enableval, BUILD_LVMDBUSD=no)
|
||||
AC_MSG_RESULT($BUILD_LVMDBUSD)
|
||||
|
||||
################################################################################
|
||||
dnl -- Enable Python liblvm2app bindings
|
||||
AC_MSG_CHECKING(whether to build Python wrapper for liblvm2app.so)
|
||||
AC_ARG_ENABLE(python_bindings,
|
||||
AC_HELP_STRING([--enable-python_bindings], [build Python applib bindings]),
|
||||
AC_HELP_STRING([--enable-python_bindings], [build default Python applib bindings]),
|
||||
PYTHON_BINDINGS=$enableval, PYTHON_BINDINGS=no)
|
||||
AC_MSG_RESULT($PYTHON_BINDINGS)
|
||||
|
||||
AC_MSG_CHECKING(whether to build Python2 wrapper for liblvm2app.so)
|
||||
AC_ARG_ENABLE(python2_bindings,
|
||||
AC_HELP_STRING([--enable-python2_bindings], [build Python2 applib bindings]),
|
||||
PYTHON2_BINDINGS=$enableval, PYTHON2_BINDINGS=no)
|
||||
AC_MSG_RESULT($PYTHON2_BINDINGS)
|
||||
|
||||
|
||||
AC_MSG_CHECKING(whether to build Python3 wrapper for liblvm2app.so)
|
||||
AC_ARG_ENABLE(python3_bindings,
|
||||
AC_HELP_STRING([--enable-python3_bindings], [build Python3 applib bindings]),
|
||||
PYTHON3_BINDINGS=$enableval, PYTHON3_BINDINGS=no)
|
||||
AC_MSG_RESULT($PYTHON3_BINDINGS)
|
||||
|
||||
if test "$PYTHON_BINDINGS" = yes; then
|
||||
test "$APPLIB" != yes && AC_MSG_ERROR([--enable-python_bindings requires --enable-applib])
|
||||
AC_MSG_ERROR([--enable-python-bindings is replaced by --enable-python2-bindings and --enable-python3-bindings])
|
||||
fi
|
||||
|
||||
AC_PATH_TOOL(PYTHON, python)
|
||||
test -z "$PYTHON" && AC_MSG_ERROR([python is required for --enable-python_bindings but cannot be found])
|
||||
if test "$PYTHON2_BINDINGS" = yes; then
|
||||
AM_PATH_PYTHON([2])
|
||||
AC_PATH_TOOL(PYTHON2, python2)
|
||||
test -z "$PYTHON2" && AC_MSG_ERROR([python2 is required for --enable-python2_bindings but cannot be found])
|
||||
AC_PATH_TOOL(PYTHON2_CONFIG, python2-config)
|
||||
test -z "$PYTHON2_CONFIG" && AC_PATH_TOOL(PYTHON2_CONFIG, python-config)
|
||||
test -z "$PYTHON2_CONFIG" && AC_MSG_ERROR([python headers are required for --enable-python2_bindings but cannot be found])
|
||||
PYTHON2_INCDIRS=`"$PYTHON2_CONFIG" --includes`
|
||||
PYTHON2_LIBDIRS=`"$PYTHON2_CONFIG" --libs`
|
||||
PYTHON2DIR=$pythondir
|
||||
PYTHON_BINDINGS=yes
|
||||
fi
|
||||
|
||||
AC_PATH_TOOL(PYTHON_CONFIG, python-config)
|
||||
test -z "$PYTHON_CONFIG" && AC_MSG_ERROR([python headers are required for --enable-python_bindings but cannot be found])
|
||||
if test "$PYTHON3_BINDINGS" = yes -o "$BUILD_LVMDBUSD" = yes; then
|
||||
unset PYTHON PYTHON_CONFIG
|
||||
unset am_cv_pathless_PYTHON ac_cv_path_PYTHON am_cv_python_platform
|
||||
unset am_cv_python_pythondir am_cv_python_version am_cv_python_pyexecdir
|
||||
unset ac_cv_path_PYTHON_CONFIG ac_cv_path_ac_pt_PYTHON_CONFIG
|
||||
AM_PATH_PYTHON([3])
|
||||
PYTHON3=$PYTHON
|
||||
test -z "$PYTHON3" && AC_MSG_ERROR([python3 is required for --enable-python3_bindings or --enable-dbus-service but cannot be found])
|
||||
AC_PATH_TOOL(PYTHON3_CONFIG, python3-config)
|
||||
test -z "$PYTHON3_CONFIG" && AC_MSG_ERROR([python3 headers are required for --enable-python3_bindings or --enable-dbus-service but cannot be found])
|
||||
PYTHON3_INCDIRS=`"$PYTHON3_CONFIG" --includes`
|
||||
PYTHON3_LIBDIRS=`"$PYTHON3_CONFIG" --libs`
|
||||
PYTHON3DIR=$pythondir
|
||||
PYTHON_BINDINGS=yes
|
||||
fi
|
||||
|
||||
PYTHON_INCDIRS=`"$PYTHON_CONFIG" --includes`
|
||||
PYTHON_LIBDIRS=`"$PYTHON_CONFIG" --libs`
|
||||
if test "$BUILD_LVMDBUSD" = yes; then
|
||||
# To get this macro, install autoconf-archive package then run autoreconf
|
||||
AC_PYTHON_MODULE([pyudev], [Required], python3)
|
||||
AC_PYTHON_MODULE([dbus], [Required], python3)
|
||||
fi
|
||||
|
||||
if test "$PYTHON_BINDINGS" = yes -o "$PYTHON2_BINDINGS" = yes -o "$PYTHON3_BINDINGS" = yes; then
|
||||
test "$APPLIB" != yes && AC_MSG_ERROR([Python_bindings require --enable-applib])
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
@@ -1859,8 +1933,12 @@ AC_DEFINE_UNQUOTED(DEFAULT_CACHE_SUBDIR, ["$DEFAULT_CACHE_SUBDIR"],
|
||||
|
||||
AC_ARG_WITH(default-locking-dir,
|
||||
AC_HELP_STRING([--with-default-locking-dir=DIR],
|
||||
[default locking directory [/var/lock/lvm]]),
|
||||
DEFAULT_LOCK_DIR=$withval, DEFAULT_LOCK_DIR="/var/lock/lvm")
|
||||
[default locking directory [autodetect_lock_dir/lvm]]),
|
||||
DEFAULT_LOCK_DIR=$withval,
|
||||
[AC_MSG_CHECKING(for default lock directory)
|
||||
DEFAULT_LOCK_DIR="$RUN_DIR/lock/lvm"
|
||||
test -d "$RUN_DIR/lock" || DEFAULT_LOCK_DIR="/var/lock/lvm"
|
||||
AC_MSG_RESULT($DEFAULT_LOCK_DIR)])
|
||||
AC_DEFINE_UNQUOTED(DEFAULT_LOCK_DIR, ["$DEFAULT_LOCK_DIR"],
|
||||
[Name of default locking directory.])
|
||||
|
||||
@@ -1907,14 +1985,17 @@ AC_SUBST(AWK)
|
||||
AC_SUBST(BLKID_PC)
|
||||
AC_SUBST(BUILD_CMIRRORD)
|
||||
AC_SUBST(BUILD_DMEVENTD)
|
||||
AC_SUBST(BUILD_LVMDBUSD)
|
||||
AC_SUBST(BUILD_LVMETAD)
|
||||
AC_SUBST(BUILD_LVMPOLLD)
|
||||
AC_SUBST(BUILD_LVMLOCKD)
|
||||
AC_SUBST(BUILD_LOCKDSANLOCK)
|
||||
AC_SUBST(BUILD_LOCKDDLM)
|
||||
AC_SUBST(BUILD_NOTIFYDBUS)
|
||||
AC_SUBST(CACHE)
|
||||
AC_SUBST(CFLAGS)
|
||||
AC_SUBST(CFLOW_CMD)
|
||||
AC_SUBST(CHMOD)
|
||||
AC_SUBST(CLDFLAGS)
|
||||
AC_SUBST(CLDNOWHOLEARCHIVE)
|
||||
AC_SUBST(CLDWHOLEARCHIVE)
|
||||
@@ -1991,10 +2072,17 @@ AC_SUBST(PKGCONFIG)
|
||||
AC_SUBST(POOL)
|
||||
AC_SUBST(M_LIBS)
|
||||
AC_SUBST(PTHREAD_LIBS)
|
||||
AC_SUBST(PYTHON)
|
||||
AC_SUBST(PYTHON2)
|
||||
AC_SUBST(PYTHON3)
|
||||
AC_SUBST(PYTHON_BINDINGS)
|
||||
AC_SUBST(PYTHON_INCDIRS)
|
||||
AC_SUBST(PYTHON_LIBDIRS)
|
||||
AC_SUBST(PYTHON2_BINDINGS)
|
||||
AC_SUBST(PYTHON3_BINDINGS)
|
||||
AC_SUBST(PYTHON2_INCDIRS)
|
||||
AC_SUBST(PYTHON3_INCDIRS)
|
||||
AC_SUBST(PYTHON2_LIBDIRS)
|
||||
AC_SUBST(PYTHON3_LIBDIRS)
|
||||
AC_SUBST(PYTHON2DIR)
|
||||
AC_SUBST(PYTHON3DIR)
|
||||
AC_SUBST(QUORUM_CFLAGS)
|
||||
AC_SUBST(QUORUM_LIBS)
|
||||
AC_SUBST(RAID)
|
||||
@@ -2066,6 +2154,8 @@ 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
|
||||
@@ -2103,12 +2193,14 @@ 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
|
||||
|
||||
@@ -44,8 +44,12 @@ ifeq ("@BUILD_LVMLOCKD@", "yes")
|
||||
SUBDIRS += lvmlockd
|
||||
endif
|
||||
|
||||
ifeq ("@BUILD_LVMDBUSD@", "yes")
|
||||
SUBDIRS += lvmdbusd
|
||||
endif
|
||||
|
||||
ifeq ($(MAKECMDGOALS),distclean)
|
||||
SUBDIRS = clvmd cmirrord dmeventd lvmetad lvmpolld lvmlockd
|
||||
SUBDIRS = clvmd cmirrord dmeventd lvmetad lvmpolld lvmlockd lvmdbusd
|
||||
endif
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
@@ -50,7 +50,7 @@ struct clvm_header {
|
||||
#define CLVMD_FLAG_REMOTE 8 /* Do this on all nodes except for the local node */
|
||||
|
||||
/* Name of the local socket to communicate between lvm and clvmd */
|
||||
static const char CLVMD_SOCKNAME[]= DEFAULT_RUN_DIR "/clvmd.sock";
|
||||
#define CLVMD_SOCKNAME DEFAULT_RUN_DIR "/clvmd.sock"
|
||||
|
||||
/* Internal commands & replies */
|
||||
#define CLVMD_CMD_REPLY 1
|
||||
|
||||
@@ -1440,7 +1440,7 @@ static void cpg_leave_callback(struct clog_cpg *match,
|
||||
free(rq);
|
||||
}
|
||||
}
|
||||
for (i = 0, j = 0; i < match->checkpoints_needed; i++, j++) {
|
||||
for (i = 0, j = 0; (int) i < match->checkpoints_needed; i++, j++) {
|
||||
match->checkpoint_requesters[j] = match->checkpoint_requesters[i];
|
||||
if (match->checkpoint_requesters[i] == left->nodeid) {
|
||||
LOG_ERROR("[%s] Removing pending ckpt from needed list (%u is leaving)",
|
||||
|
||||
@@ -573,6 +573,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;
|
||||
|
||||
@@ -855,9 +855,9 @@ int dm_event_get_version(struct dm_event_fifos *fifos, int *version) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void dm_event_log_set(int debug_level, int use_syslog)
|
||||
void dm_event_log_set(int debug_log_level, int use_syslog)
|
||||
{
|
||||
_debug_level = debug_level;
|
||||
_debug_level = debug_log_level;
|
||||
_use_syslog = use_syslog;
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ int dm_event_register_handler(const struct dm_event_handler *dmevh);
|
||||
int dm_event_unregister_handler(const struct dm_event_handler *dmevh);
|
||||
|
||||
/* Set debug level for logging, and whether to log on stdout/stderr or syslog */
|
||||
void dm_event_log_set(int debug_level, int use_syslog);
|
||||
void dm_event_log_set(int debug_log_level, int use_syslog);
|
||||
|
||||
/* Log messages acroding to current debug level */
|
||||
__attribute__((format(printf, 6, 0)))
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include "lib.h"
|
||||
#include "libdevmapper-event.h"
|
||||
#include "dmeventd_lvm.h"
|
||||
#include "defaults.h"
|
||||
#include "activate.h" /* For TARGET_NAME* */
|
||||
|
||||
/* FIXME Reformat to 80 char lines. */
|
||||
|
||||
@@ -138,7 +138,7 @@ void process_event(struct dm_task *dmt,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(target_type, "mirror")) {
|
||||
if (strcmp(target_type, TARGET_NAME_MIRROR)) {
|
||||
log_info("%s has unmirrored portion.", device);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -148,8 +148,8 @@ static void _umount(const char *device, int major, int minor)
|
||||
continue; /* can't stat, skip this one */
|
||||
|
||||
if (S_ISBLK(st.st_mode) &&
|
||||
major(st.st_rdev) == major &&
|
||||
minor(st.st_rdev) == minor) {
|
||||
(int) major(st.st_rdev) == major &&
|
||||
(int) minor(st.st_rdev) == minor) {
|
||||
log_error("Unmounting invalid snapshot %s from %s.", device, words[1]);
|
||||
if (!_run(UMOUNT_COMMAND, "-fl", words[1], NULL))
|
||||
log_error("Failed to umount snapshot %s from %s: %s.",
|
||||
|
||||
@@ -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,7 +298,7 @@ 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);
|
||||
@@ -248,10 +306,12 @@ static void _use_policy(struct dm_task *dmt, struct dso_state *state)
|
||||
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,11 +409,21 @@ 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);
|
||||
|
||||
|
||||
67
daemons/lvmdbusd/Makefile.in
Normal file
67
daemons/lvmdbusd/Makefile.in
Normal file
@@ -0,0 +1,67 @@
|
||||
#
|
||||
# Copyright (C) 2016 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 General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
top_builddir = @top_builddir@
|
||||
|
||||
lvmdbusdir = $(python3dir)/lvmdbusd
|
||||
|
||||
LVMDBUS_SRCDIR_FILES = \
|
||||
automatedproperties.py \
|
||||
background.py \
|
||||
cfg.py \
|
||||
cmdhandler.py \
|
||||
fetch.py \
|
||||
__init__.py \
|
||||
job.py \
|
||||
loader.py \
|
||||
lvmdb.py \
|
||||
main.py \
|
||||
lvm_shell_proxy.py \
|
||||
lv.py \
|
||||
manager.py \
|
||||
objectmanager.py \
|
||||
pv.py \
|
||||
refresh.py \
|
||||
request.py \
|
||||
state.py \
|
||||
udevwatch.py \
|
||||
utils.py \
|
||||
vg.py
|
||||
|
||||
LVMDBUS_BUILDDIR_FILES = \
|
||||
path.py
|
||||
|
||||
LVMDBUSD = $(srcdir)/lvmdbusd
|
||||
|
||||
include $(top_builddir)/make.tmpl
|
||||
|
||||
.PHONY: install_lvmdbusd
|
||||
|
||||
install_lvmdbusd:
|
||||
$(INSTALL_DIR) $(sbindir)
|
||||
$(INSTALL_SCRIPT) $(LVMDBUSD) $(sbindir)
|
||||
$(INSTALL_DIR) $(DESTDIR)$(lvmdbusdir)
|
||||
(cd $(srcdir); $(INSTALL_DATA) $(LVMDBUS_SRCDIR_FILES) $(DESTDIR)$(lvmdbusdir))
|
||||
$(INSTALL_DATA) $(LVMDBUS_BUILDDIR_FILES) $(DESTDIR)$(lvmdbusdir)
|
||||
PYTHON=$(PYTHON3) $(PYCOMPILE) --destdir "$(DESTDIR)" --basedir "$(lvmdbusdir)" $(LVMDBUS_SRCDIR_FILES) $(LVMDBUS_BUILDDIR_FILES)
|
||||
$(CHMOD) 755 $(DESTDIR)$(lvmdbusdir)/__pycache__
|
||||
$(CHMOD) 444 $(DESTDIR)$(lvmdbusdir)/__pycache__/*.py[co]
|
||||
|
||||
install_lvm2: install_lvmdbusd
|
||||
|
||||
install: install_lvm2
|
||||
|
||||
DISTCLEAN_TARGETS+= \
|
||||
$(LVMDBUS_BUILDDIR_FILES)
|
||||
10
daemons/lvmdbusd/__init__.py
Normal file
10
daemons/lvmdbusd/__init__.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from .main import main
|
||||
175
daemons/lvmdbusd/automatedproperties.py
Normal file
175
daemons/lvmdbusd/automatedproperties.py
Normal file
@@ -0,0 +1,175 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import dbus
|
||||
from . import cfg
|
||||
from .utils import get_properties, add_properties, get_object_property_diff, \
|
||||
log_debug
|
||||
from .state import State
|
||||
|
||||
|
||||
# noinspection PyPep8Naming,PyUnresolvedReferences
|
||||
class AutomatedProperties(dbus.service.Object):
|
||||
"""
|
||||
This class implements the needed interfaces for:
|
||||
org.freedesktop.DBus.Properties
|
||||
|
||||
Other classes inherit from it to get the same behavior
|
||||
"""
|
||||
|
||||
def __init__(self, object_path, search_method=None):
|
||||
dbus.service.Object.__init__(self, cfg.bus, object_path)
|
||||
self._ap_interface = []
|
||||
self._ap_o_path = object_path
|
||||
self._ap_search_method = search_method
|
||||
self.state = None
|
||||
|
||||
def dbus_object_path(self):
|
||||
return self._ap_o_path
|
||||
|
||||
def emit_data(self):
|
||||
props = {}
|
||||
|
||||
for i in self.interface():
|
||||
props[i] = self.GetAll(i)
|
||||
|
||||
return self._ap_o_path, props
|
||||
|
||||
def set_interface(self, interface):
|
||||
"""
|
||||
With inheritance we can't easily tell what interfaces a class provides
|
||||
so we will have each class that implements an interface tell the
|
||||
base AutomatedProperties what it is they do provide. This is kind of
|
||||
clunky and perhaps we can figure out a better way to do this later.
|
||||
:param interface: An interface the object supports
|
||||
:return:
|
||||
"""
|
||||
if interface not in self._ap_interface:
|
||||
self._ap_interface.append(interface)
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
def interface(self, all_interfaces=False):
|
||||
if all_interfaces:
|
||||
cpy = list(self._ap_interface)
|
||||
cpy.extend(
|
||||
["org.freedesktop.DBus.Introspectable",
|
||||
"org.freedesktop.DBus.Properties"])
|
||||
return cpy
|
||||
|
||||
return self._ap_interface
|
||||
|
||||
# Properties
|
||||
# noinspection PyUnusedLocal
|
||||
@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
|
||||
in_signature='ss', out_signature='v')
|
||||
def Get(self, interface_name, property_name):
|
||||
value = getattr(self, property_name)
|
||||
# Note: If we get an exception in this handler we won't know about it,
|
||||
# only the side effect of no returned value!
|
||||
log_debug('Get (%s), type (%s), value(%s)' %
|
||||
(property_name, str(type(value)), str(value)))
|
||||
return value
|
||||
|
||||
@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
|
||||
in_signature='s', out_signature='a{sv}')
|
||||
def GetAll(self, interface_name):
|
||||
if interface_name in self.interface(True):
|
||||
# Using introspection, lets build this dynamically
|
||||
properties = get_properties(self)
|
||||
if interface_name in properties:
|
||||
return properties[interface_name][1]
|
||||
return {}
|
||||
raise dbus.exceptions.DBusException(
|
||||
self._ap_interface,
|
||||
'The object %s does not implement the %s interface'
|
||||
% (self.__class__, interface_name))
|
||||
|
||||
@dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE,
|
||||
in_signature='ssv')
|
||||
def Set(self, interface_name, property_name, new_value):
|
||||
setattr(self, property_name, new_value)
|
||||
self.PropertiesChanged(interface_name,
|
||||
{property_name: new_value}, [])
|
||||
|
||||
# As dbus-python does not support introspection for properties we will
|
||||
# get the autogenerated xml and then add our wanted properties to it.
|
||||
@dbus.service.method(dbus_interface=dbus.INTROSPECTABLE_IFACE,
|
||||
out_signature='s')
|
||||
def Introspect(self):
|
||||
r = dbus.service.Object.Introspect(self, self._ap_o_path, cfg.bus)
|
||||
# Look at the properties in the class
|
||||
props = get_properties(self)
|
||||
|
||||
for int_f, v in props.items():
|
||||
r = add_properties(r, int_f, v[0])
|
||||
|
||||
return r
|
||||
|
||||
@dbus.service.signal(dbus_interface=dbus.PROPERTIES_IFACE,
|
||||
signature='sa{sv}as')
|
||||
def PropertiesChanged(self, interface_name, changed_properties,
|
||||
invalidated_properties):
|
||||
log_debug(('SIGNAL: PropertiesChanged(%s, %s, %s, %s)' %
|
||||
(str(self._ap_o_path), str(interface_name),
|
||||
str(changed_properties), str(invalidated_properties))))
|
||||
|
||||
def refresh(self, search_key=None, object_state=None):
|
||||
"""
|
||||
Take the values (properties) of an object and update them with what
|
||||
lvm currently has. You can either fetch the new ones or supply the
|
||||
new state to be updated with
|
||||
:param search_key: The value to use to search for
|
||||
:param object_state: Use this as the new object state
|
||||
"""
|
||||
num_changed = 0
|
||||
|
||||
# If we can't do a lookup, bail now, this happens if we blindly walk
|
||||
# through all dbus objects as some don't have a search method, like
|
||||
# 'Manager' object.
|
||||
if not self._ap_search_method:
|
||||
return
|
||||
|
||||
search = self.lvm_id
|
||||
if search_key:
|
||||
search = search_key
|
||||
|
||||
# Either we have the new object state or we need to go fetch it
|
||||
if object_state:
|
||||
new_state = object_state
|
||||
else:
|
||||
new_state = self._ap_search_method([search])[0]
|
||||
assert isinstance(new_state, State)
|
||||
|
||||
assert new_state
|
||||
|
||||
# When we refresh an object the object identifiers might have changed
|
||||
# because LVM allows the user to change them (name & uuid), thus if
|
||||
# they have changed we need to update the object manager so that
|
||||
# look-ups will happen correctly
|
||||
old_id = self.state.identifiers()
|
||||
new_id = new_state.identifiers()
|
||||
if old_id[0] != new_id[0] or old_id[1] != new_id[1]:
|
||||
cfg.om.lookup_update(self, new_id[0], new_id[1])
|
||||
|
||||
# Grab the properties values, then replace the state of the object
|
||||
# and retrieve the new values
|
||||
# TODO: We need to add locking to prevent concurrent access to the
|
||||
# properties so that a client is not accessing while we are
|
||||
# replacing.
|
||||
o_prop = get_properties(self)
|
||||
self.state = new_state
|
||||
n_prop = get_properties(self)
|
||||
|
||||
changed = get_object_property_diff(o_prop, n_prop)
|
||||
|
||||
if changed:
|
||||
for int_f, v in changed.items():
|
||||
self.PropertiesChanged(int_f, v, [])
|
||||
num_changed += 1
|
||||
return num_changed
|
||||
173
daemons/lvmdbusd/background.py
Normal file
173
daemons/lvmdbusd/background.py
Normal file
@@ -0,0 +1,173 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, 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 .utils import pv_range_append, pv_dest_ranges, log_error
|
||||
import traceback
|
||||
|
||||
_rlock = threading.RLock()
|
||||
_thread_list = list()
|
||||
|
||||
|
||||
def pv_move_lv_cmd(move_options, lv_full_name,
|
||||
pv_source, pv_source_range, pv_dest_range_list):
|
||||
cmd = ['pvmove', '-i', '1']
|
||||
cmd.extend(options_to_cli_args(move_options))
|
||||
|
||||
if lv_full_name:
|
||||
cmd.extend(['-n', lv_full_name])
|
||||
|
||||
pv_range_append(cmd, pv_source, *pv_source_range)
|
||||
pv_dest_ranges(cmd, pv_dest_range_list)
|
||||
|
||||
return cmd
|
||||
|
||||
|
||||
def lv_merge_cmd(merge_options, lv_full_name):
|
||||
cmd = ['lvconvert', '--merge', '-i', '1']
|
||||
cmd.extend(options_to_cli_args(merge_options))
|
||||
cmd.append(lv_full_name)
|
||||
return cmd
|
||||
|
||||
|
||||
def _move_merge(interface_name, cmd, job_state):
|
||||
add(cmd, job_state)
|
||||
|
||||
done = job_state.Wait(-1)
|
||||
if not done:
|
||||
ec, err_msg = job_state.GetError
|
||||
raise dbus.exceptions.DBusException(
|
||||
interface_name,
|
||||
'Exit code %s, stderr = %s' % (str(ec), err_msg))
|
||||
|
||||
cfg.load()
|
||||
return '/'
|
||||
|
||||
|
||||
def move(interface_name, lv_name, pv_src_obj, pv_source_range,
|
||||
pv_dests_and_ranges, move_options, job_state):
|
||||
"""
|
||||
Common code for the pvmove handling.
|
||||
:param interface_name: What dbus interface we are providing for
|
||||
:param lv_name: Optional (None or name of LV to move)
|
||||
:param pv_src_obj: dbus object patch for source PV
|
||||
: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 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)
|
||||
if pv_src:
|
||||
|
||||
# Check to see if we are handling a move to a specific
|
||||
# destination(s)
|
||||
if len(pv_dests_and_ranges):
|
||||
for pr in pv_dests_and_ranges:
|
||||
pv_dbus_obj = cfg.om.get_object_by_path(pr[0])
|
||||
if not pv_dbus_obj:
|
||||
raise dbus.exceptions.DBusException(
|
||||
interface_name,
|
||||
'PV Destination (%s) not found' % pr[0])
|
||||
|
||||
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, 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, 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, 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 background_execute(command, background_job):
|
||||
|
||||
# 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
|
||||
|
||||
background_job.set_result(process.returncode, out[1])
|
||||
|
||||
except Exception:
|
||||
# In the unlikely event that we blow up, we need to unblock caller which
|
||||
# is waiting on an answer.
|
||||
st = traceback.format_exc()
|
||||
error = "Exception in background thread: \n%s" % st
|
||||
log_error(error)
|
||||
background_job.set_result(1, error)
|
||||
|
||||
|
||||
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)
|
||||
83
daemons/lvmdbusd/cfg.py
Normal file
83
daemons/lvmdbusd/cfg.py
Normal file
@@ -0,0 +1,83 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import multiprocessing
|
||||
import queue
|
||||
import itertools
|
||||
try:
|
||||
from . import path
|
||||
except SystemError:
|
||||
import path
|
||||
|
||||
LVM_CMD = os.getenv('LVM_BINARY', path.LVM_BINARY)
|
||||
|
||||
# This is the global object manager
|
||||
om = None
|
||||
|
||||
# This is the global bus connection
|
||||
bus = None
|
||||
|
||||
# Shared state variable across all processes
|
||||
run = multiprocessing.Value('i', 1)
|
||||
|
||||
# Debug
|
||||
DEBUG = True
|
||||
|
||||
# Use lvm shell
|
||||
USE_SHELL = False
|
||||
|
||||
# Lock used by pprint
|
||||
stdout_lock = multiprocessing.Lock()
|
||||
|
||||
kick_q = multiprocessing.Queue()
|
||||
worker_q = queue.Queue()
|
||||
|
||||
# Main event loop
|
||||
loop = None
|
||||
|
||||
BASE_INTERFACE = 'com.redhat.lvmdbus1'
|
||||
PV_INTERFACE = BASE_INTERFACE + '.Pv'
|
||||
VG_INTERFACE = BASE_INTERFACE + '.Vg'
|
||||
LV_INTERFACE = BASE_INTERFACE + '.Lv'
|
||||
LV_COMMON_INTERFACE = BASE_INTERFACE + '.LvCommon'
|
||||
THIN_POOL_INTERFACE = BASE_INTERFACE + '.ThinPool'
|
||||
CACHE_POOL_INTERFACE = BASE_INTERFACE + '.CachePool'
|
||||
LV_CACHED = BASE_INTERFACE + '.CachedLv'
|
||||
SNAPSHOT_INTERFACE = BASE_INTERFACE + '.Snapshot'
|
||||
MANAGER_INTERFACE = BASE_INTERFACE + '.Manager'
|
||||
JOB_INTERFACE = BASE_INTERFACE + '.Job'
|
||||
|
||||
BASE_OBJ_PATH = '/' + BASE_INTERFACE.replace('.', '/')
|
||||
PV_OBJ_PATH = BASE_OBJ_PATH + '/Pv'
|
||||
VG_OBJ_PATH = BASE_OBJ_PATH + '/Vg'
|
||||
LV_OBJ_PATH = BASE_OBJ_PATH + '/Lv'
|
||||
THIN_POOL_PATH = BASE_OBJ_PATH + "/ThinPool"
|
||||
CACHE_POOL_PATH = BASE_OBJ_PATH + "/CachePool"
|
||||
HIDDEN_LV_PATH = BASE_OBJ_PATH + "/HiddenLv"
|
||||
MANAGER_OBJ_PATH = BASE_OBJ_PATH + '/Manager'
|
||||
JOB_OBJ_PATH = BASE_OBJ_PATH + '/Job'
|
||||
|
||||
# Counters for object path generation
|
||||
pv_id = itertools.count()
|
||||
vg_id = itertools.count()
|
||||
lv_id = itertools.count()
|
||||
thin_id = itertools.count()
|
||||
cache_pool_id = itertools.count()
|
||||
job_id = itertools.count()
|
||||
hidden_lv = itertools.count()
|
||||
|
||||
# Used to prevent circular imports...
|
||||
load = None
|
||||
|
||||
# Global cached state
|
||||
db = None
|
||||
|
||||
# lvm flight recorder
|
||||
blackbox = None
|
||||
724
daemons/lvmdbusd/cmdhandler.py
Normal file
724
daemons/lvmdbusd/cmdhandler.py
Normal file
@@ -0,0 +1,724 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from subprocess import Popen, PIPE
|
||||
import time
|
||||
import threading
|
||||
from itertools import chain
|
||||
import collections
|
||||
|
||||
try:
|
||||
from . import cfg
|
||||
from .utils import pv_dest_ranges, log_debug, log_error
|
||||
from .lvm_shell_proxy import LVMShellProxy
|
||||
except SystemError:
|
||||
import cfg
|
||||
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
|
||||
total_count = 0
|
||||
|
||||
# We need to prevent different threads from using the same lvm shell
|
||||
# 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):
|
||||
|
||||
def __init__(self, start, ended, cmd, ec, stdout_txt, stderr_txt):
|
||||
self.start = start
|
||||
self.ended = ended
|
||||
self.cmd = cmd
|
||||
self.ec = ec
|
||||
self.stdout_txt = stdout_txt
|
||||
self.stderr_txt = stderr_txt
|
||||
|
||||
def __str__(self):
|
||||
return "EC= %d for %s\n" \
|
||||
"STARTED: %f, ENDED: %f\n" \
|
||||
"STDOUT=%s\n" \
|
||||
"STDERR=%s\n" % \
|
||||
(self.ec, str(self.cmd), self.start, self.ended, self.stdout_txt,
|
||||
self.stderr_txt)
|
||||
|
||||
|
||||
class LvmFlightRecorder(object):
|
||||
|
||||
def __init__(self):
|
||||
self.queue = collections.deque(maxlen=16)
|
||||
|
||||
def add(self, lvm_exec_meta):
|
||||
self.queue.append(lvm_exec_meta)
|
||||
|
||||
def dump(self):
|
||||
with cmd_lock:
|
||||
log_error("LVM dbus flight recorder START")
|
||||
for c in self.queue:
|
||||
log_error(str(c))
|
||||
log_error("LVM dbus flight recorder END")
|
||||
|
||||
|
||||
cfg.blackbox = LvmFlightRecorder()
|
||||
|
||||
|
||||
def _debug_c(cmd, exit_code, out):
|
||||
log_error('CMD= %s' % ' '.join(cmd))
|
||||
log_error(("EC= %d" % exit_code))
|
||||
log_error(("STDOUT=\n %s\n" % out[0]))
|
||||
log_error(("STDERR=\n %s\n" % out[1]))
|
||||
|
||||
|
||||
def call_lvm(command, debug=False):
|
||||
"""
|
||||
Call an executable and return a tuple of exitcode, stdout, stderr
|
||||
:param command: Command to execute
|
||||
:param debug: Dump debug to stdout
|
||||
"""
|
||||
# print 'STACK:'
|
||||
# for line in traceback.format_stack():
|
||||
# print line.strip()
|
||||
|
||||
# Prepend the full lvm executable so that we can run different versions
|
||||
# in different locations on the same box
|
||||
command.insert(0, cfg.LVM_CMD)
|
||||
|
||||
process = Popen(command, stdout=PIPE, stderr=PIPE, close_fds=True)
|
||||
out = process.communicate()
|
||||
|
||||
stdout_text = bytes(out[0]).decode("utf-8")
|
||||
stderr_text = bytes(out[1]).decode("utf-8")
|
||||
|
||||
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]) and 'help' not in command:
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
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
|
||||
else:
|
||||
_t_call = call_lvm
|
||||
|
||||
|
||||
def time_wrapper(command, debug=False):
|
||||
global total_time
|
||||
global total_count
|
||||
|
||||
with cmd_lock:
|
||||
start = time.time()
|
||||
results = _t_call(command, debug)
|
||||
ended = time.time()
|
||||
total_time += (ended - start)
|
||||
total_count += 1
|
||||
cfg.blackbox.add(LvmExecutionMeta(start, ended, command, *results))
|
||||
return results
|
||||
|
||||
|
||||
call = time_wrapper
|
||||
|
||||
|
||||
# Default cmd
|
||||
# Place default arguments for every command here.
|
||||
def _dc(cmd, args):
|
||||
c = [cmd, '--noheading', '--separator', '%s' % SEP, '--nosuffix',
|
||||
'--unbuffered', '--units', 'b']
|
||||
c.extend(args)
|
||||
return c
|
||||
|
||||
|
||||
def parse(out):
|
||||
rc = []
|
||||
|
||||
for line in out.split('\n'):
|
||||
# This line includes separators, so process them
|
||||
if SEP in line:
|
||||
elem = line.split(SEP)
|
||||
cleaned_elem = []
|
||||
for e in elem:
|
||||
e = e.strip()
|
||||
cleaned_elem.append(e)
|
||||
|
||||
if len(cleaned_elem) > 1:
|
||||
rc.append(cleaned_elem)
|
||||
else:
|
||||
t = line.strip()
|
||||
if len(t) > 0:
|
||||
rc.append(t)
|
||||
return rc
|
||||
|
||||
|
||||
def parse_column_names(out, column_names):
|
||||
lines = parse(out)
|
||||
rc = []
|
||||
|
||||
for i in range(0, len(lines)):
|
||||
d = dict(list(zip(column_names, lines[i])))
|
||||
rc.append(d)
|
||||
|
||||
return rc
|
||||
|
||||
|
||||
def options_to_cli_args(options):
|
||||
rc = []
|
||||
for k, v in list(dict(options).items()):
|
||||
if k.startswith("-"):
|
||||
rc.append(k)
|
||||
else:
|
||||
rc.append("--%s" % k)
|
||||
if v != "":
|
||||
rc.append(str(v))
|
||||
return rc
|
||||
|
||||
|
||||
def pv_remove(device, remove_options):
|
||||
cmd = ['pvremove']
|
||||
cmd.extend(options_to_cli_args(remove_options))
|
||||
cmd.append(device)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def _tag(operation, what, add, rm, tag_options):
|
||||
cmd = [operation]
|
||||
cmd.extend(options_to_cli_args(tag_options))
|
||||
|
||||
if isinstance(what, list):
|
||||
cmd.extend(what)
|
||||
else:
|
||||
cmd.append(what)
|
||||
|
||||
if add:
|
||||
cmd.extend(list(chain.from_iterable(('--addtag', x) for x in add)))
|
||||
if rm:
|
||||
cmd.extend(list(chain.from_iterable(('--deltag', x) for x in rm)))
|
||||
|
||||
return call(cmd, False)
|
||||
|
||||
|
||||
def pv_tag(pv_devices, add, rm, tag_options):
|
||||
return _tag('pvchange', pv_devices, add, rm, tag_options)
|
||||
|
||||
|
||||
def vg_tag(vg_name, add, rm, tag_options):
|
||||
return _tag('vgchange', vg_name, add, rm, tag_options)
|
||||
|
||||
|
||||
def lv_tag(lv_name, add, rm, tag_options):
|
||||
return _tag('lvchange', lv_name, add, rm, tag_options)
|
||||
|
||||
|
||||
def vg_rename(vg, new_name, rename_options):
|
||||
cmd = ['vgrename']
|
||||
cmd.extend(options_to_cli_args(rename_options))
|
||||
cmd.extend([vg, new_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_remove(vg_name, remove_options):
|
||||
cmd = ['vgremove']
|
||||
cmd.extend(options_to_cli_args(remove_options))
|
||||
cmd.extend(['-f', vg_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_lv_create(vg_name, create_options, name, size_bytes, pv_dests):
|
||||
cmd = ['lvcreate']
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
cmd.extend(['--size', str(size_bytes) + 'B'])
|
||||
cmd.extend(['--name', name, vg_name])
|
||||
pv_dest_ranges(cmd, pv_dests)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_lv_snapshot(vg_name, snapshot_options, name, size_bytes):
|
||||
cmd = ['lvcreate']
|
||||
cmd.extend(options_to_cli_args(snapshot_options))
|
||||
cmd.extend(["-s"])
|
||||
|
||||
if size_bytes != 0:
|
||||
cmd.extend(['--size', str(size_bytes) + 'B'])
|
||||
|
||||
cmd.extend(['--name', name, vg_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_lv_create_linear(vg_name, create_options, name, size_bytes, thin_pool):
|
||||
cmd = ['lvcreate']
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
|
||||
if not thin_pool:
|
||||
cmd.extend(['--size', str(size_bytes) + 'B'])
|
||||
else:
|
||||
cmd.extend(['--thin', '--size', str(size_bytes) + 'B'])
|
||||
cmd.extend(['--name', name, vg_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_lv_create_striped(vg_name, create_options, name, size_bytes,
|
||||
num_stripes, stripe_size_kb, thin_pool):
|
||||
cmd = ['lvcreate']
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
|
||||
if not thin_pool:
|
||||
cmd.extend(['--size', str(size_bytes) + 'B'])
|
||||
else:
|
||||
cmd.extend(['--thin', '--size', str(size_bytes) + 'B'])
|
||||
|
||||
cmd.extend(['--stripes', str(num_stripes)])
|
||||
|
||||
if stripe_size_kb != 0:
|
||||
cmd.extend(['--stripesize', str(stripe_size_kb)])
|
||||
|
||||
cmd.extend(['--name', name, vg_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def _vg_lv_create_raid(vg_name, create_options, name, raid_type, size_bytes,
|
||||
num_stripes, stripe_size_kb):
|
||||
cmd = ['lvcreate']
|
||||
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
|
||||
cmd.extend(['--type', raid_type])
|
||||
cmd.extend(['--size', str(size_bytes) + 'B'])
|
||||
|
||||
if num_stripes != 0:
|
||||
cmd.extend(['--stripes', str(num_stripes)])
|
||||
|
||||
if stripe_size_kb != 0:
|
||||
cmd.extend(['--stripesize', str(stripe_size_kb)])
|
||||
|
||||
cmd.extend(['--name', name, vg_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_lv_create_raid(vg_name, create_options, name, raid_type, size_bytes,
|
||||
num_stripes, stripe_size_kb):
|
||||
cmd = ['lvcreate']
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
|
||||
return _vg_lv_create_raid(vg_name, create_options, name, raid_type,
|
||||
size_bytes, num_stripes, stripe_size_kb)
|
||||
|
||||
|
||||
def vg_lv_create_mirror(vg_name, create_options, name, size_bytes, num_copies):
|
||||
cmd = ['lvcreate']
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
|
||||
cmd.extend(['--type', 'mirror'])
|
||||
cmd.extend(['--mirrors', str(num_copies)])
|
||||
cmd.extend(['--size', str(size_bytes) + 'B'])
|
||||
cmd.extend(['--name', name, vg_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_create_cache_pool(md_full_name, data_full_name, create_options):
|
||||
cmd = ['lvconvert']
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
cmd.extend(['--type', 'cache-pool', '--force', '-y',
|
||||
'--poolmetadata', md_full_name, data_full_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_create_thin_pool(md_full_name, data_full_name, create_options):
|
||||
cmd = ['lvconvert']
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
cmd.extend(['--type', 'thin-pool', '--force', '-y',
|
||||
'--poolmetadata', md_full_name, data_full_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def lv_remove(lv_path, remove_options):
|
||||
cmd = ['lvremove']
|
||||
cmd.extend(options_to_cli_args(remove_options))
|
||||
cmd.extend(['-f', lv_path])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def lv_rename(lv_path, new_name, rename_options):
|
||||
cmd = ['lvrename']
|
||||
cmd.extend(options_to_cli_args(rename_options))
|
||||
cmd.extend([lv_path, new_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def lv_resize(lv_full_name, size_change, pv_dests,
|
||||
resize_options):
|
||||
cmd = ['lvresize', '--force']
|
||||
|
||||
cmd.extend(options_to_cli_args(resize_options))
|
||||
|
||||
if size_change < 0:
|
||||
cmd.append("-L-%dB" % (-size_change))
|
||||
else:
|
||||
cmd.append("-L+%dB" % (size_change))
|
||||
|
||||
cmd.append(lv_full_name)
|
||||
pv_dest_ranges(cmd, pv_dests)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def lv_lv_create(lv_full_name, create_options, name, size_bytes):
|
||||
cmd = ['lvcreate']
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
cmd.extend(['--virtualsize', str(size_bytes) + 'B', '-T'])
|
||||
cmd.extend(['--name', name, lv_full_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
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',
|
||||
cache_pool_full_name, lv_full_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def lv_detach_cache(lv_full_name, detach_options, destroy_cache):
|
||||
cmd = ['lvconvert']
|
||||
if destroy_cache:
|
||||
option = '--uncache'
|
||||
else:
|
||||
# Currently fairly dangerous
|
||||
# see: https://bugzilla.redhat.com/show_bug.cgi?id=1248972
|
||||
option = '--splitcache'
|
||||
cmd.extend(options_to_cli_args(detach_options))
|
||||
# needed to prevent interactive questions
|
||||
cmd.extend(["--yes", "--force"])
|
||||
cmd.extend([option, lv_full_name])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def supports_json():
|
||||
cmd = ['help']
|
||||
rc, out, err = call(cmd)
|
||||
if rc == 0:
|
||||
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_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']
|
||||
|
||||
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:
|
||||
return json.loads(out)
|
||||
return None
|
||||
|
||||
|
||||
def pv_retrieve_with_segs(device=None):
|
||||
d = []
|
||||
err = ""
|
||||
out = ""
|
||||
rc = 0
|
||||
|
||||
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', 'pvseg_start', 'pvseg_size', 'segtype']
|
||||
|
||||
# Lvm has some issues where it returns failure when querying pvs when other
|
||||
# operations are in process, see:
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=1274085
|
||||
for i in range(0, 10):
|
||||
cmd = _dc('pvs', ['-o', ','.join(columns)])
|
||||
|
||||
if device:
|
||||
cmd.extend(device)
|
||||
|
||||
rc, out, err = call(cmd)
|
||||
|
||||
if rc == 0:
|
||||
d = parse_column_names(out, columns)
|
||||
break
|
||||
else:
|
||||
time.sleep(0.2)
|
||||
log_debug("LVM Bug workaround, retrying pvs command...")
|
||||
|
||||
if rc != 0:
|
||||
msg = "We were unable to get pvs to return without error after " \
|
||||
"trying 10 times, RC=%d, STDERR=(%s), STDOUT=(%s)" % \
|
||||
(rc, err, out)
|
||||
log_error(msg)
|
||||
raise RuntimeError(msg)
|
||||
|
||||
return d
|
||||
|
||||
|
||||
def pv_resize(device, size_bytes, create_options):
|
||||
cmd = ['pvresize']
|
||||
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
|
||||
if size_bytes != 0:
|
||||
cmd.extend(['--setphysicalvolumesize', str(size_bytes) + 'B'])
|
||||
|
||||
cmd.extend([device])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def pv_create(create_options, devices):
|
||||
cmd = ['pvcreate', '-ff']
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
cmd.extend(devices)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def pv_allocatable(device, yes, allocation_options):
|
||||
yn = 'n'
|
||||
|
||||
if yes:
|
||||
yn = 'y'
|
||||
|
||||
cmd = ['pvchange']
|
||||
cmd.extend(options_to_cli_args(allocation_options))
|
||||
cmd.extend(['-x', yn, device])
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def pv_scan(activate, cache, device_paths, major_minors, scan_options):
|
||||
cmd = ['pvscan']
|
||||
cmd.extend(options_to_cli_args(scan_options))
|
||||
|
||||
if activate:
|
||||
cmd.extend(['--activate', "ay"])
|
||||
|
||||
if cache:
|
||||
cmd.append('--cache')
|
||||
|
||||
if len(device_paths) > 0:
|
||||
for d in device_paths:
|
||||
cmd.append(d)
|
||||
|
||||
if len(major_minors) > 0:
|
||||
for mm in major_minors:
|
||||
cmd.append("%s:%s" % (mm))
|
||||
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_create(create_options, pv_devices, name):
|
||||
cmd = ['vgcreate']
|
||||
cmd.extend(options_to_cli_args(create_options))
|
||||
cmd.append(name)
|
||||
cmd.extend(pv_devices)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_change(change_options, name):
|
||||
cmd = ['vgchange']
|
||||
cmd.extend(options_to_cli_args(change_options))
|
||||
cmd.append(name)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_reduce(vg_name, missing, pv_devices, reduce_options):
|
||||
cmd = ['vgreduce']
|
||||
cmd.extend(options_to_cli_args(reduce_options))
|
||||
|
||||
if len(pv_devices) == 0:
|
||||
cmd.append('--all')
|
||||
if missing:
|
||||
cmd.append('--removemissing')
|
||||
|
||||
cmd.append(vg_name)
|
||||
cmd.extend(pv_devices)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_extend(vg_name, extend_devices, extend_options):
|
||||
cmd = ['vgextend']
|
||||
cmd.extend(options_to_cli_args(extend_options))
|
||||
cmd.append(vg_name)
|
||||
cmd.extend(extend_devices)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def _vg_value_set(name, arguments, options):
|
||||
cmd = ['vgchange']
|
||||
cmd.extend(options_to_cli_args(options))
|
||||
cmd.append(name)
|
||||
cmd.extend(arguments)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_allocation_policy(vg_name, policy, policy_options):
|
||||
return _vg_value_set(vg_name, ['--alloc', policy], policy_options)
|
||||
|
||||
|
||||
def vg_max_pv(vg_name, number, max_options):
|
||||
return _vg_value_set(vg_name, ['--maxphysicalvolumes', str(number)],
|
||||
max_options)
|
||||
|
||||
|
||||
def vg_max_lv(vg_name, number, max_options):
|
||||
return _vg_value_set(vg_name, ['-l', str(number)], max_options)
|
||||
|
||||
|
||||
def vg_uuid_gen(vg_name, ignore, options):
|
||||
assert ignore is None
|
||||
return _vg_value_set(vg_name, ['--uuid'], options)
|
||||
|
||||
|
||||
def activate_deactivate(op, name, activate, control_flags, options):
|
||||
cmd = [op]
|
||||
cmd.extend(options_to_cli_args(options))
|
||||
|
||||
op = '-a'
|
||||
|
||||
if control_flags:
|
||||
# Autoactivation
|
||||
if (1 << 0) & control_flags:
|
||||
op += 'a'
|
||||
# Exclusive locking (Cluster)
|
||||
if (1 << 1) & control_flags:
|
||||
op += 'e'
|
||||
|
||||
# Local node activation
|
||||
if (1 << 2) & control_flags:
|
||||
op += 'l'
|
||||
|
||||
# Activation modes
|
||||
if (1 << 3) & control_flags:
|
||||
cmd.extend(['--activationmode', 'complete'])
|
||||
elif (1 << 4) & control_flags:
|
||||
cmd.extend(['--activationmode', 'partial'])
|
||||
|
||||
# Ignore activation skip
|
||||
if (1 << 5) & control_flags:
|
||||
cmd.append('--ignoreactivationskip')
|
||||
|
||||
if activate:
|
||||
op += 'y'
|
||||
else:
|
||||
op += 'n'
|
||||
|
||||
cmd.append(op)
|
||||
cmd.append(name)
|
||||
return call(cmd)
|
||||
|
||||
|
||||
def vg_retrieve(vg_specific):
|
||||
if vg_specific:
|
||||
assert isinstance(vg_specific, list)
|
||||
|
||||
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']
|
||||
|
||||
cmd = _dc('vgs', ['-o', ','.join(columns)])
|
||||
|
||||
if vg_specific:
|
||||
cmd.extend(vg_specific)
|
||||
|
||||
d = []
|
||||
rc, out, err = call(cmd)
|
||||
if rc == 0:
|
||||
d = parse_column_names(out, columns)
|
||||
|
||||
return d
|
||||
|
||||
|
||||
def lv_retrieve_with_segments():
|
||||
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', 'seg_pe_ranges', 'segtype', 'lv_parent',
|
||||
'lv_role', 'lv_layout']
|
||||
|
||||
cmd = _dc('lvs', ['-a', '-o', ','.join(columns)])
|
||||
rc, out, err = call(cmd)
|
||||
|
||||
d = []
|
||||
|
||||
if rc == 0:
|
||||
d = parse_column_names(out, columns)
|
||||
|
||||
return d
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pv_data = pv_retrieve_with_segs()
|
||||
|
||||
for p in pv_data:
|
||||
log_debug(str(p))
|
||||
30
daemons/lvmdbusd/fetch.py
Normal file
30
daemons/lvmdbusd/fetch.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from .pv import load_pvs
|
||||
from .vg import load_vgs
|
||||
from .lv import load_lvs
|
||||
from . import cfg
|
||||
|
||||
|
||||
def load(refresh=True, emit_signal=True, cache_refresh=True, log=True):
|
||||
num_total_changes = 0
|
||||
|
||||
# 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]
|
||||
|
||||
return num_total_changes
|
||||
170
daemons/lvmdbusd/job.py
Normal file
170
daemons/lvmdbusd/job.py
Normal file
@@ -0,0 +1,170 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from .automatedproperties import AutomatedProperties
|
||||
from .utils import job_obj_path_generate
|
||||
from . import cfg
|
||||
from .cfg import JOB_INTERFACE
|
||||
import dbus
|
||||
import threading
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
class JobState(object):
|
||||
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 = ''
|
||||
|
||||
# This is an lvm command that is just taking too long and doesn't
|
||||
# support background operation
|
||||
if self._request:
|
||||
# Faking the percentage when we don't have one
|
||||
self._percent = 1
|
||||
|
||||
@property
|
||||
def Percent(self):
|
||||
with self.rlock:
|
||||
return self._percent
|
||||
|
||||
@Percent.setter
|
||||
def Percent(self, value):
|
||||
with self.rlock:
|
||||
self._percent = value
|
||||
|
||||
@property
|
||||
def Complete(self):
|
||||
with self.rlock:
|
||||
if self._request:
|
||||
self._complete = self._request.is_done()
|
||||
if self._complete:
|
||||
self._percent = 100
|
||||
|
||||
return self._complete
|
||||
|
||||
@Complete.setter
|
||||
def Complete(self, value):
|
||||
with self.rlock:
|
||||
self._complete = value
|
||||
self._cond.notify_all()
|
||||
|
||||
@property
|
||||
def GetError(self):
|
||||
with self.rlock:
|
||||
if self.Complete:
|
||||
if self._request:
|
||||
(rc, error) = self._request.get_errors()
|
||||
return (rc, str(error))
|
||||
else:
|
||||
return (self._ec, self._stderr)
|
||||
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:
|
||||
if self._request:
|
||||
return self._request.result()
|
||||
return '/'
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
class Job(AutomatedProperties):
|
||||
_Percent_meta = ('y', JOB_INTERFACE)
|
||||
_Complete_meta = ('b', JOB_INTERFACE)
|
||||
_Result_meta = ('o', JOB_INTERFACE)
|
||||
_GetError_meta = ('(is)', JOB_INTERFACE)
|
||||
|
||||
def __init__(self, request, job_state=None):
|
||||
super(Job, self).__init__(job_obj_path_generate())
|
||||
self.set_interface(JOB_INTERFACE)
|
||||
|
||||
if job_state:
|
||||
self.state = job_state
|
||||
else:
|
||||
self.state = JobState(request)
|
||||
|
||||
@property
|
||||
def Percent(self):
|
||||
return self.state.Percent
|
||||
|
||||
@Percent.setter
|
||||
def Percent(self, value):
|
||||
self.state.Percent = value
|
||||
|
||||
@property
|
||||
def Complete(self):
|
||||
return self.state.Complete
|
||||
|
||||
@Complete.setter
|
||||
def Complete(self, value):
|
||||
self.state.Complete = value
|
||||
|
||||
@property
|
||||
def GetError(self):
|
||||
return self.state.GetError
|
||||
|
||||
def set_result(self, ec, msg):
|
||||
self.state.set_result(ec, msg)
|
||||
|
||||
@dbus.service.method(dbus_interface=JOB_INTERFACE)
|
||||
def Remove(self):
|
||||
if self.state.Complete:
|
||||
cfg.om.remove_object(self, True)
|
||||
self.state.dtor()
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
JOB_INTERFACE, 'Job is not complete!')
|
||||
|
||||
@dbus.service.method(dbus_interface=JOB_INTERFACE,
|
||||
in_signature='i',
|
||||
out_signature='b')
|
||||
def Wait(self, timeout):
|
||||
return self.state.Wait(timeout)
|
||||
|
||||
@property
|
||||
def Result(self):
|
||||
return self.state.Result
|
||||
|
||||
@property
|
||||
def lvm_id(self):
|
||||
return str(id(self))
|
||||
|
||||
@property
|
||||
def Uuid(self):
|
||||
import uuid
|
||||
return uuid.uuid1()
|
||||
85
daemons/lvmdbusd/loader.py
Normal file
85
daemons/lvmdbusd/loader.py
Normal file
@@ -0,0 +1,85 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from . import cfg
|
||||
|
||||
|
||||
def _compare_construction(o_state, new_state):
|
||||
# We need to check to see if the objects would get constructed
|
||||
# the same
|
||||
existing_ctor, existing_path = o_state.creation_signature()
|
||||
new_ctor, new_path = new_state.creation_signature()
|
||||
|
||||
# print("%s == %s and %s == %s" % (str(existing_ctor), str(new_ctor),
|
||||
# str(existing_path), str(new_path)))
|
||||
|
||||
return ((existing_ctor == new_ctor) and (existing_path == new_path))
|
||||
|
||||
|
||||
def common(retrieve, o_type, search_keys,
|
||||
object_path, refresh, emit_signal, cache_refresh):
|
||||
num_changes = 0
|
||||
existing_paths = []
|
||||
rc = []
|
||||
|
||||
if search_keys:
|
||||
assert isinstance(search_keys, list)
|
||||
|
||||
if cache_refresh:
|
||||
cfg.db.refresh()
|
||||
|
||||
objects = retrieve(search_keys, cache_refresh=False)
|
||||
|
||||
# If we are doing a refresh we need to know what we have in memory, what's
|
||||
# in lvm and add those that are new and remove those that are gone!
|
||||
if refresh:
|
||||
existing_paths = cfg.om.object_paths_by_type(o_type)
|
||||
|
||||
for o in objects:
|
||||
# Assume we need to add this one to dbus, unless we are refreshing
|
||||
# and it's already present
|
||||
return_object = True
|
||||
|
||||
if refresh:
|
||||
# We are refreshing all the PVs from LVM, if this one exists
|
||||
# we need to refresh our state.
|
||||
dbus_object = cfg.om.get_object_by_uuid_lvm_id(*o.identifiers())
|
||||
|
||||
if dbus_object:
|
||||
del existing_paths[dbus_object.dbus_object_path()]
|
||||
|
||||
# If the old object state and new object state wouldn't be
|
||||
# created with the same path and same object constructor we
|
||||
# need to remove the old object and construct the new one
|
||||
# instead!
|
||||
if not _compare_construction(dbus_object.state, o):
|
||||
# Remove existing and construct new one
|
||||
cfg.om.remove_object(dbus_object, emit_signal)
|
||||
dbus_object = o.create_dbus_object(None)
|
||||
cfg.om.register_object(dbus_object, emit_signal)
|
||||
num_changes += 1
|
||||
else:
|
||||
num_changes += dbus_object.refresh(object_state=o)
|
||||
return_object = False
|
||||
|
||||
if return_object:
|
||||
dbus_object = o.create_dbus_object(object_path)
|
||||
cfg.om.register_object(dbus_object, emit_signal)
|
||||
rc.append(dbus_object)
|
||||
|
||||
object_path = None
|
||||
|
||||
if refresh:
|
||||
for k in list(existing_paths.keys()):
|
||||
cfg.om.remove_object(cfg.om.get_object_by_path(k), True)
|
||||
num_changes += 1
|
||||
|
||||
num_changes += len(rc)
|
||||
|
||||
return rc, num_changes
|
||||
893
daemons/lvmdbusd/lv.py
Normal file
893
daemons/lvmdbusd/lv.py
Normal file
@@ -0,0 +1,893 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from .automatedproperties import AutomatedProperties
|
||||
|
||||
from . import utils
|
||||
from .utils import vg_obj_path_generate
|
||||
import dbus
|
||||
from . import cmdhandler
|
||||
from . import cfg
|
||||
from .cfg import LV_INTERFACE, THIN_POOL_INTERFACE, SNAPSHOT_INTERFACE, \
|
||||
LV_COMMON_INTERFACE, CACHE_POOL_INTERFACE, LV_CACHED
|
||||
from .request import RequestEntry
|
||||
from .utils import n, n32
|
||||
from .loader import common
|
||||
from .state import State
|
||||
from . import background
|
||||
from .utils import round_size
|
||||
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
|
||||
def lvs_state_retrieve(selection, cache_refresh=True):
|
||||
rc = []
|
||||
|
||||
if cache_refresh:
|
||||
cfg.db.refresh()
|
||||
|
||||
# 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']),
|
||||
l['vg_name'],
|
||||
l['vg_uuid'], l['pool_lv_uuid'],
|
||||
l['pool_lv'], l['origin_uuid'], l['origin'],
|
||||
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']))
|
||||
return rc
|
||||
|
||||
|
||||
def load_lvs(lv_name=None, object_path=None, refresh=False, emit_signal=False,
|
||||
cache_refresh=True):
|
||||
# noinspection PyUnresolvedReferences
|
||||
return common(
|
||||
lvs_state_retrieve,
|
||||
(LvCommon, Lv, LvThinPool, LvSnapShot),
|
||||
lv_name, object_path, refresh, emit_signal, cache_refresh)
|
||||
|
||||
|
||||
# noinspection PyPep8Naming,PyUnresolvedReferences,PyUnusedLocal
|
||||
class LvState(State):
|
||||
@staticmethod
|
||||
def _pv_devices(uuid):
|
||||
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_uuid_lvm_id(
|
||||
pv_uuid, pv_name, gen_new=False)
|
||||
rc.append((pv_obj, pv_segs))
|
||||
|
||||
return dbus.Array(rc, signature="(oa(tts))")
|
||||
|
||||
def vg_name_lookup(self):
|
||||
return cfg.om.get_object_by_path(self.Vg).Name
|
||||
|
||||
@property
|
||||
def lvm_id(self):
|
||||
return "%s/%s" % (self.vg_name_lookup(), self.Name)
|
||||
|
||||
def identifiers(self):
|
||||
return (self.Uuid, self.lvm_id)
|
||||
|
||||
def _get_hidden_lv(self):
|
||||
rc = dbus.Array([], "o")
|
||||
|
||||
vg_name = self.vg_name_lookup()
|
||||
|
||||
for l in cfg.db.hidden_lvs(self.Uuid):
|
||||
full_name = "%s/%s" % (vg_name, l[1])
|
||||
op = cfg.om.get_object_path_by_uuid_lvm_id(
|
||||
l[0], full_name, gen_new=False)
|
||||
assert op
|
||||
rc.append(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):
|
||||
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)
|
||||
else:
|
||||
self._segs.extend(set(segtypes))
|
||||
|
||||
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)
|
||||
|
||||
if PoolLv:
|
||||
gen = utils.lv_object_path_method(Name, (Attr, layout, role))
|
||||
|
||||
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_uuid_lvm_id(
|
||||
origin_uuid, '%s/%s' % (vg_name, OriginLv),
|
||||
vg_obj_path_generate)
|
||||
else:
|
||||
self.OriginLv = '/'
|
||||
|
||||
self.HiddenLvs = self._get_hidden_lv()
|
||||
|
||||
@property
|
||||
def SegType(self):
|
||||
return self._segs
|
||||
|
||||
def _object_path_create(self):
|
||||
return utils.lv_object_path_method(
|
||||
self.Name, (self.Attr, self.layout, self.role))
|
||||
|
||||
def _object_type_create(self):
|
||||
if self.Attr[0] == 't':
|
||||
return LvThinPool
|
||||
elif self.Attr[0] == 'C':
|
||||
if 'pool' in self.layout:
|
||||
return LvCachePool
|
||||
else:
|
||||
return LvCacheLv
|
||||
elif self.Name[0] == '[':
|
||||
return LvCommon
|
||||
elif self.OriginLv != '/':
|
||||
return LvSnapShot
|
||||
else:
|
||||
return Lv
|
||||
|
||||
def create_dbus_object(self, path):
|
||||
if not path:
|
||||
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()
|
||||
return obj_ctor(path, self)
|
||||
|
||||
def creation_signature(self):
|
||||
klass = self._object_type_create()
|
||||
path_method = self._object_path_create()
|
||||
return (klass, path_method)
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
@utils.dbus_property(LV_COMMON_INTERFACE, 'Uuid', 's')
|
||||
@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")
|
||||
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)
|
||||
_VolumeType_meta = ("(ss)", LV_COMMON_INTERFACE)
|
||||
_Permissions_meta = ("(ss)", LV_COMMON_INTERFACE)
|
||||
_AllocationPolicy_meta = ("(ss)", LV_COMMON_INTERFACE)
|
||||
_State_meta = ("(ss)", LV_COMMON_INTERFACE)
|
||||
_TargetType_meta = ("(ss)", LV_COMMON_INTERFACE)
|
||||
_Health_meta = ("(ss)", LV_COMMON_INTERFACE)
|
||||
_FixedMinor_meta = ('b', LV_COMMON_INTERFACE)
|
||||
_ZeroBlocks_meta = ('b', LV_COMMON_INTERFACE)
|
||||
_SkipActivation_meta = ('b', LV_COMMON_INTERFACE)
|
||||
|
||||
# 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
|
||||
|
||||
@property
|
||||
def VolumeType(self):
|
||||
type_map = {'C': 'Cache', 'm': 'mirrored',
|
||||
'M': 'Mirrored without initial sync', 'o': 'origin',
|
||||
'O': 'Origin with merging snapshot', 'r': 'raid',
|
||||
'R': 'Raid without initial sync', 's': 'snapshot',
|
||||
'S': 'merging Snapshot', 'p': 'pvmove',
|
||||
'v': 'virtual', 'i': 'mirror or raid image',
|
||||
'I': 'mirror or raid Image out-of-sync',
|
||||
'l': 'mirror log device', 'c': 'under conversion',
|
||||
'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]])
|
||||
|
||||
@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]])
|
||||
|
||||
@property
|
||||
def AllocationPolicy(self):
|
||||
type_map = {'a': 'anywhere', 'A': 'anywhere locked',
|
||||
'c': 'contiguous', 'C': 'contiguous locked',
|
||||
'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]])
|
||||
|
||||
@property
|
||||
def FixedMinor(self):
|
||||
return self.state.Attr[3] == 'm'
|
||||
|
||||
@property
|
||||
def State(self):
|
||||
type_map = {'a': 'active', 's': 'suspended', 'I': 'Invalid snapshot',
|
||||
'S': 'invalid Suspended snapshot',
|
||||
'm': 'snapshot merge failed',
|
||||
'M': 'suspended snapshot (M)erge failed',
|
||||
'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]])
|
||||
|
||||
@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]])
|
||||
|
||||
@property
|
||||
def ZeroBlocks(self):
|
||||
return 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]])
|
||||
|
||||
@property
|
||||
def SkipActivation(self):
|
||||
return self.state.Attr[9] == 'k'
|
||||
|
||||
def vg_name_lookup(self):
|
||||
return self.state.vg_name_lookup()
|
||||
|
||||
def lv_full_name(self):
|
||||
return "%s/%s" % (self.state.vg_name_lookup(), self.state.Name)
|
||||
|
||||
@property
|
||||
def identifiers(self):
|
||||
return self.state.identifiers
|
||||
|
||||
@property
|
||||
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'
|
||||
|
||||
@property
|
||||
def IsThinPool(self):
|
||||
return self.state.Attr[0] == 't'
|
||||
|
||||
@property
|
||||
def Active(self):
|
||||
return 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!')
|
||||
|
||||
|
||||
# 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)
|
||||
self.set_interface(LV_INTERFACE)
|
||||
self.state = object_state
|
||||
|
||||
@staticmethod
|
||||
def _remove(lv_uuid, lv_name, remove_options):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
||||
|
||||
if dbo:
|
||||
# Remove the LV, if successful then remove from the model
|
||||
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
|
||||
raise dbus.exceptions.DBusException(
|
||||
LV_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
LV_INTERFACE,
|
||||
'LV with uuid %s and name %s not present!' %
|
||||
(lv_uuid, lv_name))
|
||||
return '/'
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=LV_INTERFACE,
|
||||
in_signature='ia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Remove(self, tmo, remove_options, cb, cbe):
|
||||
r = RequestEntry(
|
||||
tmo, Lv._remove,
|
||||
(self.Uuid, self.lvm_id, remove_options),
|
||||
cb, cbe, False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _rename(lv_uuid, lv_name, new_name, rename_options):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
||||
|
||||
if dbo:
|
||||
# Rename the logical volume
|
||||
rc, out, err = cmdhandler.lv_rename(lv_name, new_name,
|
||||
rename_options)
|
||||
if rc == 0:
|
||||
cfg.load()
|
||||
else:
|
||||
# Need to work on error handling, need consistent
|
||||
raise dbus.exceptions.DBusException(
|
||||
LV_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
LV_INTERFACE,
|
||||
'LV with uuid %s and name %s not present!' %
|
||||
(lv_uuid, lv_name))
|
||||
return '/'
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=LV_INTERFACE,
|
||||
in_signature='sia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Rename(self, name, tmo, rename_options, cb, cbe):
|
||||
utils.validate_lv_name(LV_INTERFACE, self.vg_name_lookup(), name)
|
||||
|
||||
r = RequestEntry(
|
||||
tmo, Lv._rename,
|
||||
(self.Uuid, self.lvm_id, name, rename_options),
|
||||
cb, cbe, False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=LV_INTERFACE,
|
||||
in_signature='o(tt)a(ott)ia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Move(self, pv_src_obj, pv_source_range,
|
||||
pv_dests_and_ranges,
|
||||
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,
|
||||
snapshot_options):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
||||
|
||||
if dbo:
|
||||
# If you specify a size you get a 'thick' snapshot even if
|
||||
# it is a thin lv
|
||||
if not dbo.IsThinVolume:
|
||||
if optional_size == 0:
|
||||
space = dbo.SizeBytes / 80
|
||||
remainder = space % 512
|
||||
optional_size = space + 512 - remainder
|
||||
|
||||
rc, out, err = cmdhandler.vg_lv_snapshot(
|
||||
lv_name, snapshot_options, name, optional_size)
|
||||
if rc == 0:
|
||||
return_path = '/'
|
||||
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
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
LV_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
LV_INTERFACE,
|
||||
'LV with uuid %s and name %s not present!' %
|
||||
(lv_uuid, lv_name))
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=LV_INTERFACE,
|
||||
in_signature='stia{sv}',
|
||||
out_signature='(oo)',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Snapshot(self, name, optional_size, tmo,
|
||||
snapshot_options, cb, cbe):
|
||||
|
||||
utils.validate_lv_name(LV_INTERFACE, self.vg_name_lookup(), name)
|
||||
|
||||
r = RequestEntry(
|
||||
tmo, Lv._snap_shot,
|
||||
(self.Uuid, self.lvm_id, name,
|
||||
optional_size, snapshot_options), cb, cbe)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _resize(lv_uuid, lv_name, new_size_bytes, pv_dests_and_ranges,
|
||||
resize_options):
|
||||
# Make sure we have a dbus object representing it
|
||||
pv_dests = []
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
||||
|
||||
if dbo:
|
||||
# If we have PVs, verify them
|
||||
if len(pv_dests_and_ranges):
|
||||
for pr in pv_dests_and_ranges:
|
||||
pv_dbus_obj = cfg.om.get_object_by_path(pr[0])
|
||||
if not pv_dbus_obj:
|
||||
raise dbus.exceptions.DBusException(
|
||||
LV_INTERFACE,
|
||||
'PV Destination (%s) not found' % pr[0])
|
||||
|
||||
pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2]))
|
||||
|
||||
size_change = new_size_bytes - dbo.SizeBytes
|
||||
|
||||
rc, out, err = cmdhandler.lv_resize(dbo.lvm_id, size_change,
|
||||
pv_dests, resize_options)
|
||||
|
||||
if rc == 0:
|
||||
# Refresh what's changed
|
||||
cfg.load()
|
||||
return "/"
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
LV_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
LV_INTERFACE,
|
||||
'LV with uuid %s and name %s not present!' %
|
||||
(lv_uuid, lv_name))
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=LV_INTERFACE,
|
||||
in_signature='ta(ott)ia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Resize(self, new_size_bytes, pv_dests_and_ranges, tmo,
|
||||
resize_options, cb, cbe):
|
||||
"""
|
||||
Resize a LV
|
||||
:param new_size_bytes: The requested final size in bytes
|
||||
:param pv_dests_and_ranges: An array of pv object paths and src &
|
||||
dst. segment ranges
|
||||
:param tmo: -1 to wait forever, 0 to return job immediately, else
|
||||
number of seconds to wait for operation to complete
|
||||
before getting a job
|
||||
:param resize_options: key/value hash of options
|
||||
:param cb: Used by framework not client facing API
|
||||
:param cbe: Used by framework not client facing API
|
||||
:return: '/' if complete, else job object path
|
||||
"""
|
||||
r = RequestEntry(
|
||||
tmo, Lv._resize,
|
||||
(self.Uuid, self.lvm_id, round_size(new_size_bytes),
|
||||
pv_dests_and_ranges,
|
||||
resize_options), cb, cbe, return_tuple=False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _lv_activate_deactivate(uuid, lv_name, activate, control_flags,
|
||||
options):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, lv_name)
|
||||
|
||||
if dbo:
|
||||
rc, out, err = cmdhandler.activate_deactivate(
|
||||
'lvchange', lv_name, activate, control_flags, options)
|
||||
if rc == 0:
|
||||
dbo.refresh()
|
||||
return '/'
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
LV_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
LV_INTERFACE,
|
||||
'LV with uuid %s and name %s not present!' %
|
||||
(uuid, lv_name))
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=LV_INTERFACE,
|
||||
in_signature='tia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Activate(self, control_flags, tmo, activate_options, cb, cbe):
|
||||
r = RequestEntry(
|
||||
tmo, Lv._lv_activate_deactivate,
|
||||
(self.state.Uuid, self.state.lvm_id, True,
|
||||
control_flags, activate_options),
|
||||
cb, cbe, return_tuple=False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
@dbus.service.method(
|
||||
dbus_interface=LV_INTERFACE,
|
||||
in_signature='tia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Deactivate(self, control_flags, tmo, activate_options, cb, cbe):
|
||||
r = RequestEntry(
|
||||
tmo, Lv._lv_activate_deactivate,
|
||||
(self.state.Uuid, self.state.lvm_id, False,
|
||||
control_flags, activate_options),
|
||||
cb, cbe, return_tuple=False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _add_rm_tags(uuid, lv_name, tags_add, tags_del, tag_options):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, lv_name)
|
||||
|
||||
if dbo:
|
||||
|
||||
rc, out, err = cmdhandler.lv_tag(
|
||||
lv_name, tags_add, tags_del, tag_options)
|
||||
if rc == 0:
|
||||
dbo.refresh()
|
||||
return '/'
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
LV_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
LV_INTERFACE,
|
||||
'LV with uuid %s and name %s not present!' %
|
||||
(uuid, lv_name))
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=LV_INTERFACE,
|
||||
in_signature='asia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def TagsAdd(self, tags, tmo, tag_options, cb, cbe):
|
||||
|
||||
for t in tags:
|
||||
utils.validate_tag(LV_INTERFACE, t)
|
||||
|
||||
r = RequestEntry(
|
||||
tmo, Lv._add_rm_tags,
|
||||
(self.state.Uuid, self.state.lvm_id,
|
||||
tags, None, tag_options),
|
||||
cb, cbe, return_tuple=False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=LV_INTERFACE,
|
||||
in_signature='asia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def TagsDel(self, tags, tmo, tag_options, cb, cbe):
|
||||
|
||||
for t in tags:
|
||||
utils.validate_tag(LV_INTERFACE, t)
|
||||
|
||||
r = RequestEntry(
|
||||
tmo, Lv._add_rm_tags,
|
||||
(self.state.Uuid, self.state.lvm_id,
|
||||
None, tags, tag_options),
|
||||
cb, cbe, return_tuple=False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
class LvThinPool(Lv):
|
||||
_DataLv_meta = ("o", THIN_POOL_INTERFACE)
|
||||
_MetaDataLv_meta = ("o", THIN_POOL_INTERFACE)
|
||||
|
||||
def __init__(self, object_path, object_state):
|
||||
super(LvThinPool, self).__init__(object_path, object_state)
|
||||
self.set_interface(THIN_POOL_INTERFACE)
|
||||
self._data_lv, self._metadata_lv = self._get_data_meta()
|
||||
|
||||
@property
|
||||
def DataLv(self):
|
||||
return self._data_lv
|
||||
|
||||
@property
|
||||
def MetaDataLv(self):
|
||||
return self._metadata_lv
|
||||
|
||||
@staticmethod
|
||||
def _lv_create(lv_uuid, lv_name, name, size_bytes, create_options):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
||||
|
||||
lv_created = '/'
|
||||
|
||||
if dbo:
|
||||
rc, out, err = cmdhandler.lv_lv_create(
|
||||
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()
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
LV_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
LV_INTERFACE,
|
||||
'LV with uuid %s and name %s not present!' %
|
||||
(lv_uuid, lv_name))
|
||||
return lv_created
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=THIN_POOL_INTERFACE,
|
||||
in_signature='stia{sv}',
|
||||
out_signature='(oo)',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def LvCreate(self, name, size_bytes, tmo, create_options, cb, cbe):
|
||||
utils.validate_lv_name(THIN_POOL_INTERFACE, self.vg_name_lookup(), name)
|
||||
|
||||
r = RequestEntry(
|
||||
tmo, LvThinPool._lv_create,
|
||||
(self.Uuid, self.lvm_id, name,
|
||||
round_size(size_bytes), create_options), cb, cbe)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
|
||||
# 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 self._data_lv
|
||||
|
||||
@property
|
||||
def MetaDataLv(self):
|
||||
return self._metadata_lv
|
||||
|
||||
@staticmethod
|
||||
def _cache_lv(lv_uuid, lv_name, lv_object_path, cache_options):
|
||||
|
||||
# Make sure we have a dbus object representing cache pool
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
||||
|
||||
# Make sure we have dbus object representing lv to cache
|
||||
lv_to_cache = cfg.om.get_object_by_path(lv_object_path)
|
||||
|
||||
if dbo and lv_to_cache:
|
||||
fcn = lv_to_cache.lv_full_name()
|
||||
rc, out, err = cmdhandler.lv_cache_lv(
|
||||
dbo.lv_full_name(), fcn, cache_options)
|
||||
if rc == 0:
|
||||
# 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)
|
||||
cfg.load()
|
||||
|
||||
lv_converted = cfg.om.get_object_path_by_lvm_id(fcn)
|
||||
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
LV_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
else:
|
||||
msg = ""
|
||||
if not dbo:
|
||||
dbo += 'CachePool LV with uuid %s and name %s not present!' % \
|
||||
(lv_uuid, lv_name)
|
||||
|
||||
if not lv_to_cache:
|
||||
dbo += 'LV to cache with object path %s not present!' % \
|
||||
(lv_object_path)
|
||||
|
||||
raise dbus.exceptions.DBusException(LV_INTERFACE, msg)
|
||||
return lv_converted
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=CACHE_POOL_INTERFACE,
|
||||
in_signature='oia{sv}',
|
||||
out_signature='(oo)',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def CacheLv(self, lv_object, tmo, cache_options, cb, cbe):
|
||||
r = RequestEntry(
|
||||
tmo, LvCachePool._cache_lv,
|
||||
(self.Uuid, self.lvm_id, lv_object,
|
||||
cache_options), cb, cbe)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
class LvCacheLv(Lv):
|
||||
_CachePool_meta = ("o", LV_CACHED)
|
||||
|
||||
def __init__(self, object_path, object_state):
|
||||
super(LvCacheLv, self).__init__(object_path, object_state)
|
||||
self.set_interface(LV_CACHED)
|
||||
|
||||
@property
|
||||
def CachePool(self):
|
||||
return self.state.PoolLv
|
||||
|
||||
@staticmethod
|
||||
def _detach_lv(lv_uuid, lv_name, detach_options, destroy_cache):
|
||||
# Make sure we have a dbus object representing cache pool
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(lv_uuid, lv_name)
|
||||
|
||||
if dbo:
|
||||
|
||||
# Get current cache name
|
||||
cache_pool = cfg.om.get_object_by_path(dbo.CachePool)
|
||||
|
||||
rc, out, err = cmdhandler.lv_detach_cache(
|
||||
dbo.lv_full_name(), detach_options, destroy_cache)
|
||||
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)
|
||||
cfg.load()
|
||||
|
||||
uncached_lv_path = cfg.om.get_object_path_by_lvm_id(lv_name)
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
LV_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
LV_INTERFACE,
|
||||
'LV with uuid %s and name %s not present!' %
|
||||
(lv_uuid, lv_name))
|
||||
return uncached_lv_path
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=LV_CACHED,
|
||||
in_signature='bia{sv}',
|
||||
out_signature='(oo)',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def DetachCachePool(self, destroy_cache, tmo, detach_options, cb, cbe):
|
||||
r = RequestEntry(
|
||||
tmo, LvCacheLv._detach_lv,
|
||||
(self.Uuid, self.lvm_id, detach_options,
|
||||
destroy_cache), cb, cbe)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
class LvSnapShot(Lv):
|
||||
def __init__(self, object_path, object_state):
|
||||
super(LvSnapShot, self).__init__(object_path, object_state)
|
||||
self.set_interface(SNAPSHOT_INTERFACE)
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=SNAPSHOT_INTERFACE,
|
||||
in_signature='ia{sv}',
|
||||
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)
|
||||
184
daemons/lvmdbusd/lvm_shell_proxy.py
Executable file
184
daemons/lvmdbusd/lvm_shell_proxy.py
Executable file
@@ -0,0 +1,184 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Copyright 2015-2016, Vratislav Podzimek <vpodzime@redhat.com>
|
||||
|
||||
import subprocess
|
||||
import shlex
|
||||
from fcntl import fcntl, F_GETFL, F_SETFL
|
||||
from os import O_NONBLOCK
|
||||
import traceback
|
||||
import sys
|
||||
import re
|
||||
|
||||
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
|
||||
|
||||
SHELL_PROMPT = "lvm> "
|
||||
|
||||
|
||||
def _quote_arg(arg):
|
||||
if len(shlex.split(arg)) > 1:
|
||||
return '"%s"' % arg
|
||||
else:
|
||||
return arg
|
||||
|
||||
|
||||
class LVMShellProxy(object):
|
||||
def _read_until_prompt(self):
|
||||
prev_ec = None
|
||||
stdout = ""
|
||||
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
|
||||
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', '')
|
||||
|
||||
def _write_cmd(self, cmd):
|
||||
cmd_bytes = bytes(cmd, "utf-8")
|
||||
num_written = self.lvm_shell.stdin.write(cmd_bytes)
|
||||
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)
|
||||
|
||||
# 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)
|
||||
|
||||
# wait for the first prompt
|
||||
self._read_until_prompt()
|
||||
|
||||
# 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()
|
||||
|
||||
def call_lvm(self, argv, debug=False):
|
||||
# create the command string
|
||||
cmd = " ".join(_quote_arg(arg) for arg in argv)
|
||||
cmd += "\n"
|
||||
|
||||
# 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()
|
||||
|
||||
# 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
|
||||
|
||||
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))
|
||||
|
||||
return (rc, stdout, stderr)
|
||||
|
||||
def __del__(self):
|
||||
self.lvm_shell.terminate()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
shell = LVMShellProxy()
|
||||
in_line = "start"
|
||||
try:
|
||||
while in_line:
|
||||
in_line = input("lvm> ")
|
||||
if in_line:
|
||||
ret, out, err, = shell.call_lvm(in_line.split())
|
||||
print(("RET: %d" % ret))
|
||||
print(("OUT:\n%s" % out))
|
||||
print(("ERR:\n%s" % err))
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except EOFError:
|
||||
pass
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
finally:
|
||||
print()
|
||||
544
daemons/lvmdbusd/lvmdb.py
Executable file
544
daemons/lvmdbusd/lvmdb.py
Executable file
@@ -0,0 +1,544 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
|
||||
|
||||
class DataStore(object):
|
||||
def __init__(self, usejson=None):
|
||||
self.pvs = {}
|
||||
self.vgs = {}
|
||||
self.lvs = {}
|
||||
self.pv_lvs = {}
|
||||
self.lv_pvs = {}
|
||||
self.lvs_hidden = {}
|
||||
|
||||
self.pv_path_to_uuid = {}
|
||||
self.vg_name_to_uuid = {}
|
||||
self.lv_full_name_to_uuid = {}
|
||||
|
||||
self.lvs_in_vgs = {}
|
||||
self.pvs_in_vgs = {}
|
||||
|
||||
# self.refresh()
|
||||
self.num_refreshes = 0
|
||||
|
||||
if usejson is None:
|
||||
self.json = cmdhandler.supports_json()
|
||||
else:
|
||||
self.json = usejson
|
||||
|
||||
@staticmethod
|
||||
def _insert_record(table, key, record, allowed_multiple):
|
||||
if key in table:
|
||||
existing = table[key]
|
||||
|
||||
for rec_k, rec_v in record.items():
|
||||
if rec_k in allowed_multiple:
|
||||
# This column name allows us to store multiple value for
|
||||
# each type
|
||||
if not isinstance(existing[rec_k], list):
|
||||
existing_value = existing[rec_k]
|
||||
existing[rec_k] = [existing_value, rec_v]
|
||||
else:
|
||||
existing[rec_k].append(rec_v)
|
||||
else:
|
||||
# If something is not expected to have changing values
|
||||
# lets ensure that
|
||||
if existing[rec_k] != rec_v:
|
||||
raise RuntimeError(
|
||||
"existing[%s]=%s != %s" %
|
||||
(rec_k, str(existing[rec_k]),
|
||||
str(rec_v)))
|
||||
else:
|
||||
table[key] = record
|
||||
|
||||
@staticmethod
|
||||
def _parse_pvs(_pvs):
|
||||
pvs = sorted(_pvs, key=lambda pk: pk['pv_name'])
|
||||
|
||||
c_pvs = OrderedDict()
|
||||
c_lookup = {}
|
||||
c_pvs_in_vgs = {}
|
||||
|
||||
for p in pvs:
|
||||
DataStore._insert_record(
|
||||
c_pvs, p['pv_uuid'], p,
|
||||
['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
|
||||
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_vgs(_vgs):
|
||||
vgs = sorted(_vgs, key=lambda vk: vk['vg_name'])
|
||||
|
||||
c_vgs = OrderedDict()
|
||||
c_lookup = {}
|
||||
|
||||
for i in vgs:
|
||||
c_lookup[i['vg_name']] = i['vg_uuid']
|
||||
DataStore._insert_record(c_vgs, i['vg_uuid'], i, [])
|
||||
|
||||
return c_vgs, c_lookup
|
||||
|
||||
@staticmethod
|
||||
def _parse_vgs_json(_all):
|
||||
|
||||
tmp_vg = []
|
||||
for r in _all['report']:
|
||||
# Get the pv data for this VG.
|
||||
if 'vg' in r:
|
||||
tmp_vg.extend(r['vg'])
|
||||
|
||||
# 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:
|
||||
c_lvs_in_vgs[i['vg_uuid']] = []
|
||||
|
||||
c_lvs_in_vgs[
|
||||
i['vg_uuid']].append(
|
||||
(i['lv_name'],
|
||||
(i['lv_attr'], i['lv_layout'], i['lv_role']),
|
||||
i['lv_uuid']))
|
||||
|
||||
if i['lv_parent']:
|
||||
# Lookup what the parent refers too
|
||||
parent_name = i['lv_parent']
|
||||
full_parent_name = "%s/%s" % (i['vg_name'], parent_name)
|
||||
if full_parent_name not in c_lv_full_lookup:
|
||||
parent_name = '[%s]' % (parent_name)
|
||||
full_parent_name = "%s/%s" % (i['vg_name'], parent_name)
|
||||
|
||||
parent_uuid = c_lv_full_lookup[full_parent_name]
|
||||
|
||||
if parent_uuid not in c_lvs_hidden:
|
||||
c_lvs_hidden[parent_uuid] = []
|
||||
|
||||
c_lvs_hidden[parent_uuid].append(
|
||||
(i['lv_uuid'], i['lv_name']))
|
||||
|
||||
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):
|
||||
l = [l]
|
||||
return l
|
||||
|
||||
@staticmethod
|
||||
def _parse_seg_entry(se, segtype):
|
||||
if se:
|
||||
# print("_parse_seg_entry %s %s" % (str(se), str(segtype)))
|
||||
device, segs = se.split(":")
|
||||
start, end = segs.split('-')
|
||||
return (device, (start, end), segtype)
|
||||
else:
|
||||
return ("", (), segtype)
|
||||
|
||||
@staticmethod
|
||||
def _build_segments(l, seg_types):
|
||||
rc = []
|
||||
l = DataStore._make_list(l)
|
||||
s = DataStore._make_list(seg_types)
|
||||
|
||||
assert len(l) == len(s)
|
||||
ls = list(zip(l, s))
|
||||
|
||||
for i in ls:
|
||||
if ' ' in i[0]:
|
||||
tmp = i[0].split(' ')
|
||||
for t in tmp:
|
||||
rc.append(DataStore._parse_seg_entry(t, i[1]))
|
||||
else:
|
||||
rc.append(DataStore._parse_seg_entry(*i))
|
||||
return rc
|
||||
|
||||
@staticmethod
|
||||
def _pv_device_lv_entry(table, pv_device, lv_uuid, meta, lv_attr,
|
||||
segment_info):
|
||||
|
||||
if pv_device not in table:
|
||||
table[pv_device] = {}
|
||||
|
||||
if lv_uuid not in table[pv_device]:
|
||||
table[pv_device][lv_uuid] = {}
|
||||
table[pv_device][lv_uuid]['segs'] = [segment_info]
|
||||
table[pv_device][lv_uuid]['name'] = meta
|
||||
table[pv_device][lv_uuid]['meta'] = lv_attr
|
||||
else:
|
||||
table[pv_device][lv_uuid]['segs'].append(segment_info)
|
||||
|
||||
@staticmethod
|
||||
def _pv_device_lv_format(pv_device_lvs):
|
||||
rc = {}
|
||||
|
||||
for pv_device, pd in pv_device_lvs.items():
|
||||
lvs = []
|
||||
for lv_uuid, ld in sorted(pd.items()):
|
||||
lvs.append((lv_uuid, ld['name'], ld['meta'], ld['segs']))
|
||||
|
||||
rc[pv_device] = lvs
|
||||
return rc
|
||||
|
||||
@staticmethod
|
||||
def _lvs_device_pv_entry(table, lv_uuid, pv_device, pv_uuid, segment_info):
|
||||
if lv_uuid not in table:
|
||||
table[lv_uuid] = {}
|
||||
|
||||
if pv_device not in table[lv_uuid]:
|
||||
table[lv_uuid][pv_device] = {}
|
||||
table[lv_uuid][pv_device]['segs'] = [segment_info]
|
||||
table[lv_uuid][pv_device]['pv_uuid'] = pv_uuid
|
||||
else:
|
||||
table[lv_uuid][pv_device]['segs'].append(segment_info)
|
||||
|
||||
@staticmethod
|
||||
def _lvs_device_pv_format(lvs_device_pvs):
|
||||
rc = {}
|
||||
|
||||
for lv_uuid, ld in lvs_device_pvs.items():
|
||||
pvs = []
|
||||
for pv_device, pd in sorted(ld.items()):
|
||||
pvs.append((pd['pv_uuid'], pv_device, pd['segs']))
|
||||
|
||||
rc[lv_uuid] = pvs
|
||||
return rc
|
||||
|
||||
def _parse_pv_in_lvs(self):
|
||||
pv_device_lvs = {} # What LVs are stored on a PV
|
||||
lvs_device_pv = {} # Where LV data is stored
|
||||
|
||||
for i in self.lvs.values():
|
||||
segs = self._build_segments(i['seg_pe_ranges'], i['segtype'])
|
||||
for s in segs:
|
||||
# We are referring to physical device
|
||||
if '/dev/' in s[0]:
|
||||
device, r, seg_type = s
|
||||
|
||||
DataStore._pv_device_lv_entry(
|
||||
pv_device_lvs, device, i['lv_uuid'], i['lv_name'],
|
||||
(i['lv_attr'], i['lv_layout'], i['lv_role']),
|
||||
(r[0], r[1], seg_type))
|
||||
|
||||
# (pv_name, pv_segs, pv_uuid)
|
||||
DataStore._lvs_device_pv_entry(
|
||||
lvs_device_pv, i['lv_uuid'], device,
|
||||
self.pv_path_to_uuid[device], (r[0], r[1], seg_type))
|
||||
else:
|
||||
# TODO Handle the case where the segments refer to a LV
|
||||
# and not a PV
|
||||
pass
|
||||
# print("Handle this %s %s %s" % (s[0], s[1], s[2]))
|
||||
|
||||
# Convert form to needed result for consumption
|
||||
pv_device_lvs_result = DataStore._pv_device_lv_format(pv_device_lvs)
|
||||
lvs_device_pv_result = DataStore._lvs_device_pv_format(lvs_device_pv)
|
||||
|
||||
return pv_device_lvs_result, lvs_device_pv_result
|
||||
|
||||
def refresh(self, log=True):
|
||||
"""
|
||||
Go out and query lvm for the latest data in as few trips as possible
|
||||
:param log Add debug log entry/exit messages
|
||||
:return: None
|
||||
"""
|
||||
self.num_refreshes += 1
|
||||
if log:
|
||||
log_debug("lvmdb - refresh entry")
|
||||
|
||||
# Grab everything first then parse it
|
||||
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_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
|
||||
self.pv_path_to_uuid = _pvs_lookup
|
||||
self.vg_name_to_uuid = _vgs_lookup
|
||||
self.lv_full_name_to_uuid = _lvs_lookup
|
||||
|
||||
self.vgs = _vgs
|
||||
self.lvs = _lvs
|
||||
self.lvs_in_vgs = _lvs_in_vgs
|
||||
self.pvs_in_vgs = _pvs_in_vgs
|
||||
self.lvs_hidden = _lvs_hidden
|
||||
|
||||
# Create lookup table for which LV and segments are on each PV
|
||||
self.pv_lvs, self.lv_pvs = self._parse_pv_in_lvs()
|
||||
|
||||
if log:
|
||||
log_debug("lvmdb - refresh exit")
|
||||
|
||||
def fetch_pvs(self, pv_name):
|
||||
if not pv_name:
|
||||
return self.pvs.values()
|
||||
else:
|
||||
rc = []
|
||||
for s in pv_name:
|
||||
# Ths user could be using a symlink instead of the actual
|
||||
# block device, make sure we are using actual block device file
|
||||
# if the pv name isn't in the lookup
|
||||
if s not in self.pv_path_to_uuid:
|
||||
s = os.path.realpath(s)
|
||||
rc.append(self.pvs[self.pv_path_to_uuid[s]])
|
||||
return rc
|
||||
|
||||
def fetch_vgs(self, vg_name):
|
||||
if not vg_name:
|
||||
return self.vgs.values()
|
||||
else:
|
||||
rc = []
|
||||
for s in vg_name:
|
||||
rc.append(self.vgs[self.vg_name_to_uuid[s]])
|
||||
return rc
|
||||
|
||||
def fetch_lvs(self, lv_names):
|
||||
try:
|
||||
if not lv_names:
|
||||
return self.lvs.values()
|
||||
else:
|
||||
rc = []
|
||||
for s in lv_names:
|
||||
rc.append(self.lvs[self.lv_full_name_to_uuid[s]])
|
||||
return rc
|
||||
except KeyError as ke:
|
||||
log_error("Key %s not found!" % (str(lv_names)))
|
||||
log_error("lv name to uuid lookup")
|
||||
for keys in sorted(self.lv_full_name_to_uuid.keys()):
|
||||
log_error("%s" % (keys))
|
||||
log_error("lvs entries by uuid")
|
||||
for keys in sorted(self.lvs.keys()):
|
||||
log_error("%s" % (keys))
|
||||
raise ke
|
||||
|
||||
def pv_pe_segments(self, pv_uuid):
|
||||
pv = self.pvs[pv_uuid]
|
||||
return list(zip(pv['pvseg_start'], pv['pvseg_size']))
|
||||
|
||||
def pv_contained_lv(self, pv_device):
|
||||
rc = []
|
||||
if pv_device in self.pv_lvs:
|
||||
rc = self.pv_lvs[pv_device]
|
||||
return rc
|
||||
|
||||
def lv_contained_pv(self, lv_uuid):
|
||||
rc = []
|
||||
if lv_uuid in self.lv_pvs:
|
||||
rc = self.lv_pvs[lv_uuid]
|
||||
return rc
|
||||
|
||||
def lvs_in_vg(self, vg_uuid):
|
||||
# Return an array of
|
||||
# (lv_name, (lv_attr, lv_layout, lv_role), lv_uuid)
|
||||
rc = []
|
||||
if vg_uuid in self.lvs_in_vgs:
|
||||
rc = self.lvs_in_vgs[vg_uuid]
|
||||
return rc
|
||||
|
||||
def pvs_in_vg(self, vg_uuid):
|
||||
# Returns an array of (pv_name, pv_uuid)
|
||||
rc = []
|
||||
if vg_uuid in self.pvs_in_vgs:
|
||||
rc = self.pvs_in_vgs[vg_uuid]
|
||||
return rc
|
||||
|
||||
def hidden_lvs(self, lv_uuid):
|
||||
# For a specified LV, return a list of hidden lv_uuid, lv_name
|
||||
# for it
|
||||
rc = []
|
||||
if lv_uuid in self.lvs_hidden:
|
||||
rc = self.lvs_hidden[lv_uuid]
|
||||
return rc
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pp = prettyprint.PrettyPrinter(indent=4)
|
||||
|
||||
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("VGS")
|
||||
for v in ds.vgs.values():
|
||||
pp.pprint(v)
|
||||
|
||||
print("LVS")
|
||||
for v in ds.lvs.values():
|
||||
pp.pprint(v)
|
||||
|
||||
print("LVS in VG")
|
||||
for k, v in ds.lvs_in_vgs.items():
|
||||
print("VG uuid = %s" % (k))
|
||||
pp.pprint(v)
|
||||
|
||||
print("pv_in_lvs")
|
||||
for k, v in ds.pv_lvs.items():
|
||||
print("PV %s contains LVS:" % (k))
|
||||
pp.pprint(v)
|
||||
|
||||
for k, v in ds.lv_pvs.items():
|
||||
print("LV device = %s" % (k))
|
||||
pp.pprint(v)
|
||||
16
daemons/lvmdbusd/lvmdbusd
Executable file
16
daemons/lvmdbusd/lvmdbusd
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
import lvmdbusd
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(lvmdbusd.main())
|
||||
182
daemons/lvmdbusd/main.py
Normal file
182
daemons/lvmdbusd/main.py
Normal file
@@ -0,0 +1,182 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from . import cfg
|
||||
from . import objectmanager
|
||||
from . import utils
|
||||
from .cfg import BASE_INTERFACE, BASE_OBJ_PATH, MANAGER_OBJ_PATH
|
||||
import threading
|
||||
from . import cmdhandler
|
||||
import time
|
||||
import signal
|
||||
import dbus
|
||||
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
|
||||
import argparse
|
||||
import os
|
||||
from .refresh import handle_external_event, event_complete
|
||||
|
||||
|
||||
class Lvm(objectmanager.ObjectManager):
|
||||
def __init__(self, object_path):
|
||||
super(Lvm, self).__init__(object_path, BASE_INTERFACE)
|
||||
|
||||
|
||||
def _discard_pending_refreshes():
|
||||
# We just handled a refresh, if we have any in the queue they can be
|
||||
# removed because by definition they are older than the refresh we just did.
|
||||
# As we limit the number of refreshes getting into the queue
|
||||
# we should only ever have one to remove.
|
||||
requests = []
|
||||
while not cfg.worker_q.empty():
|
||||
try:
|
||||
r = cfg.worker_q.get(block=False)
|
||||
if r.method != handle_external_event:
|
||||
requests.append(r)
|
||||
else:
|
||||
# Make sure we make this event complete even though it didn't
|
||||
# run, otherwise no other events will get processed
|
||||
event_complete()
|
||||
break
|
||||
except queue.Empty:
|
||||
break
|
||||
|
||||
# Any requests we removed, but did not discard need to be re-queued
|
||||
for r in requests:
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
|
||||
def process_request():
|
||||
while cfg.run.value != 0:
|
||||
try:
|
||||
req = cfg.worker_q.get(True, 5)
|
||||
|
||||
start = cfg.db.num_refreshes
|
||||
|
||||
log_debug(
|
||||
"Running method: %s with args %s" %
|
||||
(str(req.method), str(req.arguments)))
|
||||
req.run_cmd()
|
||||
|
||||
end = cfg.db.num_refreshes
|
||||
|
||||
num_refreshes = end - start
|
||||
|
||||
if num_refreshes > 0:
|
||||
_discard_pending_refreshes()
|
||||
|
||||
if num_refreshes > 1:
|
||||
log_debug(
|
||||
"Inspect method %s for too many refreshes" %
|
||||
(str(req.method)))
|
||||
log_debug("Complete ")
|
||||
except queue.Empty:
|
||||
pass
|
||||
except Exception:
|
||||
st = traceback.format_exc()
|
||||
utils.log_error("process_request exception: \n%s" % st)
|
||||
|
||||
|
||||
def main():
|
||||
# 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("--nojson", action='store_false',
|
||||
help="Do not use LVM JSON output", default=None,
|
||||
dest='use_json')
|
||||
|
||||
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.DEBUG = args.debug
|
||||
|
||||
# List of threads that we start up
|
||||
thread_list = []
|
||||
|
||||
start = time.time()
|
||||
|
||||
# Install signal handlers
|
||||
for s in [signal.SIGHUP, signal.SIGINT]:
|
||||
try:
|
||||
signal.signal(s, utils.handler)
|
||||
except RuntimeError:
|
||||
pass
|
||||
|
||||
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
|
||||
dbus.mainloop.glib.threads_init()
|
||||
|
||||
if use_session:
|
||||
cfg.bus = dbus.SessionBus()
|
||||
else:
|
||||
cfg.bus = dbus.SystemBus()
|
||||
# The base name variable needs to exist for things to work.
|
||||
# noinspection PyUnusedLocal
|
||||
base_name = dbus.service.BusName(BASE_INTERFACE, cfg.bus)
|
||||
cfg.om = Lvm(BASE_OBJ_PATH)
|
||||
cfg.om.register_object(Manager(MANAGER_OBJ_PATH))
|
||||
|
||||
cfg.load = load
|
||||
|
||||
cfg.db = lvmdb.DataStore(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.
|
||||
thread_list.append(threading.Thread(target=process_request))
|
||||
|
||||
cfg.load(refresh=False, emit_signal=False)
|
||||
cfg.loop = GLib.MainLoop()
|
||||
|
||||
for process in thread_list:
|
||||
process.damon = True
|
||||
process.start()
|
||||
|
||||
end = time.time()
|
||||
log_debug(
|
||||
'Service ready! total time= %.2f, lvm time= %.2f 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()
|
||||
|
||||
for process in thread_list:
|
||||
process.join()
|
||||
except KeyboardInterrupt:
|
||||
utils.handler(signal.SIGINT, None)
|
||||
return 0
|
||||
246
daemons/lvmdbusd/manager.py
Normal file
246
daemons/lvmdbusd/manager.py
Normal file
@@ -0,0 +1,246 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from .automatedproperties import AutomatedProperties
|
||||
|
||||
from . import utils
|
||||
from .cfg import MANAGER_INTERFACE
|
||||
import dbus
|
||||
from . import cfg
|
||||
from . import cmdhandler
|
||||
from .fetch import load_pvs, load_vgs
|
||||
from .request import RequestEntry
|
||||
from .refresh import event_add
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
class Manager(AutomatedProperties):
|
||||
_Version_meta = ("t", MANAGER_INTERFACE)
|
||||
|
||||
def __init__(self, object_path):
|
||||
super(Manager, self).__init__(object_path)
|
||||
self.set_interface(MANAGER_INTERFACE)
|
||||
|
||||
@property
|
||||
def Version(self):
|
||||
return '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_uuid_lvm_id(
|
||||
device, device, None, False)
|
||||
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()
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
MANAGER_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
|
||||
return created_pv
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=MANAGER_INTERFACE,
|
||||
in_signature='sia{sv}',
|
||||
out_signature='(oo)',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def PvCreate(self, device, tmo, create_options, cb, cbe):
|
||||
utils.validate_device_path(MANAGER_INTERFACE, device)
|
||||
r = RequestEntry(
|
||||
tmo, Manager._pv_create,
|
||||
(device, create_options), cb, cbe)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _create_vg(name, pv_object_paths, create_options):
|
||||
pv_devices = []
|
||||
|
||||
for p in pv_object_paths:
|
||||
pv = cfg.om.get_object_by_path(p)
|
||||
if pv:
|
||||
pv_devices.append(pv.Name)
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
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)
|
||||
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,
|
||||
in_signature='saoia{sv}',
|
||||
out_signature='(oo)',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def VgCreate(self, name, pv_object_paths, tmo, create_options, cb, cbe):
|
||||
utils.validate_vg_name(MANAGER_INTERFACE, name)
|
||||
r = RequestEntry(
|
||||
tmo, Manager._create_vg,
|
||||
(name, pv_object_paths, create_options,),
|
||||
cb, cbe)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _refresh():
|
||||
utils.log_debug('Manager.Refresh - entry')
|
||||
|
||||
# This is a diagnostic and should not be run in normal operation, so
|
||||
# lets remove the log entries for refresh as it's implied.
|
||||
rc = cfg.load(log=False)
|
||||
|
||||
if rc != 0:
|
||||
utils.log_debug('Manager.Refresh - exit %d' % (rc),
|
||||
'bg_black', 'fg_light_red')
|
||||
else:
|
||||
utils.log_debug('Manager.Refresh - exit %d' % (rc))
|
||||
return rc
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=MANAGER_INTERFACE,
|
||||
out_signature='t',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Refresh(self, cb, cbe):
|
||||
"""
|
||||
Take all the objects we know about and go out and grab the latest
|
||||
more of a test method at the moment to make sure we are handling object
|
||||
paths correctly.
|
||||
|
||||
:param cb Callback for result
|
||||
:param cbe Callback for errors
|
||||
|
||||
Returns the number of changes, object add/remove/properties changed
|
||||
"""
|
||||
r = RequestEntry(-1, Manager._refresh, (), cb, cbe, False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=MANAGER_INTERFACE,
|
||||
in_signature='s',
|
||||
out_signature='o')
|
||||
def LookUpByLvmId(self, key):
|
||||
"""
|
||||
Given a lvm id in one of the forms:
|
||||
|
||||
/dev/sda
|
||||
some_vg
|
||||
some_vg/some_lv
|
||||
Oe1rPX-Pf0W-15E5-n41N-ZmtF-jXS0-Osg8fn
|
||||
|
||||
return the object path in O(1) time.
|
||||
|
||||
:param key: The lookup value
|
||||
:return: Return the object path. If object not found you will get '/'
|
||||
"""
|
||||
p = cfg.om.get_object_path_by_uuid_lvm_id(
|
||||
key, key, gen_new=False)
|
||||
if p:
|
||||
return p
|
||||
return '/'
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=MANAGER_INTERFACE,
|
||||
in_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)
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=MANAGER_INTERFACE,
|
||||
in_signature='s', out_signature='i')
|
||||
def ExternalEvent(self, command):
|
||||
|
||||
event_add((command,))
|
||||
return dbus.Int32(0)
|
||||
|
||||
@staticmethod
|
||||
def _pv_scan(activate, cache, device_path, major_minor, scan_options):
|
||||
|
||||
rc, out, err = cmdhandler.pv_scan(
|
||||
activate, cache, device_path,
|
||||
major_minor, scan_options)
|
||||
|
||||
if rc == 0:
|
||||
# This could potentially change the state quite a bit, so lets
|
||||
# update everything to be safe
|
||||
cfg.load()
|
||||
return '/'
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
MANAGER_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=MANAGER_INTERFACE,
|
||||
in_signature='bbasa(ii)ia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def PvScan(self, activate, cache, device_paths, major_minors,
|
||||
tmo, scan_options, cb, cbe):
|
||||
"""
|
||||
Scan all supported LVM block devices in the system for physical volumes
|
||||
NOTE: major_minors & device_paths only usable when cache == True
|
||||
:param activate: If True, activate any newly found LVs
|
||||
:param cache: If True, update lvmetad
|
||||
:param device_paths: Array of device paths or empty
|
||||
:param major_minors: Array of structures (major,minor)
|
||||
:param tmo: Timeout for operation
|
||||
:param scan_options: Additional options to pvscan
|
||||
:param cb: Not visible in API (used for async. callback)
|
||||
:param cbe: Not visible in API (used for async. error callback)
|
||||
:return: '/' if operation done, else job path
|
||||
"""
|
||||
for d in device_paths:
|
||||
utils.validate_device_path(MANAGER_INTERFACE, d)
|
||||
|
||||
r = RequestEntry(
|
||||
tmo, Manager._pv_scan,
|
||||
(activate, cache, device_paths, major_minors,
|
||||
scan_options), cb, cbe, False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@property
|
||||
def lvm_id(self):
|
||||
"""
|
||||
Intended to be overridden by classes that inherit
|
||||
"""
|
||||
return str(id(self))
|
||||
|
||||
@property
|
||||
def Uuid(self):
|
||||
"""
|
||||
Intended to be overridden by classes that inherit
|
||||
"""
|
||||
import uuid
|
||||
return uuid.uuid1()
|
||||
308
daemons/lvmdbusd/objectmanager.py
Normal file
308
daemons/lvmdbusd/objectmanager.py
Normal file
@@ -0,0 +1,308 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
import dbus
|
||||
import os
|
||||
from . import cfg
|
||||
from .utils import log_debug
|
||||
from .automatedproperties import AutomatedProperties
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
class ObjectManager(AutomatedProperties):
|
||||
"""
|
||||
Implements the org.freedesktop.DBus.ObjectManager interface
|
||||
"""
|
||||
|
||||
def __init__(self, object_path, interface):
|
||||
super(ObjectManager, self).__init__(object_path, interface)
|
||||
self.set_interface(interface)
|
||||
self._ap_o_path = object_path
|
||||
self._objects = {}
|
||||
self._id_to_object_path = {}
|
||||
self.rlock = threading.RLock()
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface="org.freedesktop.DBus.ObjectManager",
|
||||
out_signature='a{oa{sa{sv}}}')
|
||||
def GetManagedObjects(self):
|
||||
with self.rlock:
|
||||
rc = {}
|
||||
try:
|
||||
for k, v in list(self._objects.items()):
|
||||
path, props = v[0].emit_data()
|
||||
rc[path] = props
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stdout)
|
||||
sys.exit(1)
|
||||
return rc
|
||||
|
||||
def locked(self):
|
||||
"""
|
||||
If some external code need to run across a number of different
|
||||
calls into ObjectManager while blocking others they can use this method
|
||||
to lock others out.
|
||||
:return:
|
||||
"""
|
||||
return ObjectManagerLock(self.rlock)
|
||||
|
||||
@dbus.service.signal(
|
||||
dbus_interface="org.freedesktop.DBus.ObjectManager",
|
||||
signature='oa{sa{sv}}')
|
||||
def InterfacesAdded(self, object_path, int_name_prop_dict):
|
||||
log_debug(
|
||||
('SIGNAL: InterfacesAdded(%s, %s)' %
|
||||
(str(object_path), str(int_name_prop_dict))))
|
||||
|
||||
@dbus.service.signal(
|
||||
dbus_interface="org.freedesktop.DBus.ObjectManager",
|
||||
signature='oas')
|
||||
def InterfacesRemoved(self, object_path, interface_list):
|
||||
log_debug(('SIGNAL: InterfacesRemoved(%s, %s)' %
|
||||
(str(object_path), str(interface_list))))
|
||||
|
||||
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 uuid: The uuid for the asset
|
||||
:return:
|
||||
"""
|
||||
# Note: Only called internally, lock implied
|
||||
|
||||
# We could have a temp entry from the forward creation of a path
|
||||
self._lookup_remove(path)
|
||||
|
||||
self._objects[path] = (obj, lvm_id, uuid)
|
||||
self._id_to_object_path[lvm_id] = path
|
||||
|
||||
if uuid:
|
||||
self._id_to_object_path[uuid] = path
|
||||
|
||||
def _lookup_remove(self, obj_path):
|
||||
# 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]
|
||||
del self._objects[obj_path]
|
||||
|
||||
def lookup_update(self, dbus_obj, new_uuid, new_lvm_id):
|
||||
with self.rlock:
|
||||
obj_path = dbus_obj.dbus_object_path()
|
||||
self._lookup_remove(obj_path)
|
||||
self._lookup_add(
|
||||
dbus_obj, obj_path,
|
||||
new_lvm_id, new_uuid)
|
||||
|
||||
def object_paths_by_type(self, o_type):
|
||||
with self.rlock:
|
||||
rc = {}
|
||||
|
||||
for k, v in list(self._objects.items()):
|
||||
if isinstance(v[0], o_type):
|
||||
rc[k] = True
|
||||
return rc
|
||||
|
||||
def register_object(self, dbus_object, emit_signal=False):
|
||||
"""
|
||||
Given a dbus object add it to the collection
|
||||
:param dbus_object: Dbus object to register
|
||||
:param emit_signal: If true emit a signal for interfaces added
|
||||
"""
|
||||
with self.rlock:
|
||||
path, props = dbus_object.emit_data()
|
||||
|
||||
# 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
|
||||
self._lookup_add(dbus_object, path, dbus_object.lvm_id,
|
||||
dbus_object.Uuid)
|
||||
|
||||
if emit_signal:
|
||||
self.InterfacesAdded(path, props)
|
||||
|
||||
def remove_object(self, dbus_object, emit_signal=False):
|
||||
"""
|
||||
Given a dbus object, remove it from the collection and remove it
|
||||
from the dbus framework as well
|
||||
:param dbus_object: Dbus object to remove
|
||||
:param emit_signal: If true emit the interfaces removed signal
|
||||
"""
|
||||
with self.rlock:
|
||||
# Store off the object path and the interface first
|
||||
path = dbus_object.dbus_object_path()
|
||||
interfaces = dbus_object.interface()
|
||||
|
||||
# print 'UN-Registering object path %s for %s' % \
|
||||
# (path, dbus_object.lvm_id)
|
||||
|
||||
self._lookup_remove(path)
|
||||
|
||||
# Remove from dbus library
|
||||
dbus_object.remove_from_connection(cfg.bus, path)
|
||||
|
||||
# Optionally emit a signal
|
||||
if emit_signal:
|
||||
self.InterfacesRemoved(path, interfaces)
|
||||
|
||||
def get_object_by_path(self, path):
|
||||
"""
|
||||
Given a dbus path return the object registered for it
|
||||
:param path: The dbus path
|
||||
:return: The object
|
||||
"""
|
||||
with self.rlock:
|
||||
if path in self._objects:
|
||||
return self._objects[path][0]
|
||||
return None
|
||||
|
||||
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_uuid_lvm_id(uuid, lvm_id, None, False))
|
||||
|
||||
def get_object_by_lvm_id(self, lvm_id):
|
||||
"""
|
||||
Given an lvm identifier, return the object registered for it
|
||||
:param lvm_id: The lvm identifier
|
||||
"""
|
||||
with self.rlock:
|
||||
if lvm_id in self._id_to_object_path:
|
||||
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
|
||||
NOTE: Internal call, assumes under object manager lock
|
||||
:param path: Path to object we looked up
|
||||
:param uuid: lvm uuid to verify
|
||||
:param lvm_id: lvm_id used to find object
|
||||
:return: None
|
||||
"""
|
||||
# This gets called when we found an object based on lvm_id, ensure
|
||||
# uuid is correct too, as they can change
|
||||
if lvm_id != uuid:
|
||||
if 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):
|
||||
"""
|
||||
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:
|
||||
"""
|
||||
path = self._id_to_object_path[lvm_identifier]
|
||||
self._uuid_verify(path, uuid, lvm_identifier)
|
||||
return path
|
||||
|
||||
def get_object_path_by_uuid_lvm_id(self, uuid, lvm_id, path_create=None,
|
||||
gen_new=True):
|
||||
"""
|
||||
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
|
||||
"""
|
||||
with self.rlock:
|
||||
assert lvm_id
|
||||
assert uuid
|
||||
|
||||
if gen_new:
|
||||
assert path_create
|
||||
|
||||
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)
|
||||
else:
|
||||
if gen_new:
|
||||
path = path_create()
|
||||
self._lookup_add(None, path, lvm_id, uuid)
|
||||
|
||||
# pprint('get_object_path_by_lvm_id(%s, %s, %s, %s: return %s' %
|
||||
# (uuid, lvm_id, str(path_create), str(gen_new), path))
|
||||
|
||||
return path
|
||||
|
||||
|
||||
class ObjectManagerLock(object):
|
||||
"""
|
||||
The sole purpose of this class is to allow other code the ability to
|
||||
lock the object manager using a `with` statement, eg.
|
||||
|
||||
with cfg.om.locked():
|
||||
# Do stuff with object manager
|
||||
|
||||
This will ensure that the lock is always released (assuming this is done
|
||||
correctly)
|
||||
"""
|
||||
|
||||
def __init__(self, recursive_lock):
|
||||
self._lock = recursive_lock
|
||||
|
||||
def __enter__(self):
|
||||
# Acquire lock
|
||||
self._lock.acquire()
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
def __exit__(self, e_type, e_value, e_traceback):
|
||||
# Release lock
|
||||
self._lock.release()
|
||||
self._lock = None
|
||||
10
daemons/lvmdbusd/path.py.in
Normal file
10
daemons/lvmdbusd/path.py.in
Normal file
@@ -0,0 +1,10 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
LVM_BINARY = "@LVM_PATH@"
|
||||
282
daemons/lvmdbusd/pv.py
Normal file
282
daemons/lvmdbusd/pv.py
Normal file
@@ -0,0 +1,282 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from .automatedproperties import AutomatedProperties
|
||||
from . import utils
|
||||
from . import cfg
|
||||
import dbus
|
||||
from .cfg import PV_INTERFACE
|
||||
from . import cmdhandler
|
||||
from .utils import vg_obj_path_generate, n, pv_obj_path_generate, \
|
||||
lv_object_path_method
|
||||
from .loader import common
|
||||
from .request import RequestEntry
|
||||
from .state import State
|
||||
from .utils import round_size
|
||||
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
def pvs_state_retrieve(selection, cache_refresh=True):
|
||||
rc = []
|
||||
|
||||
if cache_refresh:
|
||||
cfg.db.refresh()
|
||||
|
||||
for p in cfg.db.fetch_pvs(selection):
|
||||
rc.append(
|
||||
PvState(
|
||||
p["pv_name"], p["pv_uuid"], p["pv_name"],
|
||||
p["pv_fmt"], n(p["pv_size"]), n(p["pv_free"]),
|
||||
n(p["pv_used"]), n(p["dev_size"]), n(p["pv_mda_size"]),
|
||||
n(p["pv_mda_free"]), int(p["pv_ba_start"]),
|
||||
n(p["pv_ba_size"]), n(p["pe_start"]),
|
||||
int(p["pv_pe_count"]), int(p["pv_pe_alloc_count"]),
|
||||
p["pv_attr"], p["pv_tags"], p["vg_name"], p["vg_uuid"]))
|
||||
return rc
|
||||
|
||||
|
||||
def load_pvs(device=None, object_path=None, refresh=False, emit_signal=False,
|
||||
cache_refresh=True):
|
||||
return common(
|
||||
pvs_state_retrieve, (Pv,), device, object_path, refresh,
|
||||
emit_signal, cache_refresh)
|
||||
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
class PvState(State):
|
||||
@property
|
||||
def lvm_id(self):
|
||||
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)):
|
||||
lv_uuid, lv_name, meta, segs = lv
|
||||
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_uuid_lvm_id(
|
||||
lv_uuid, full_name, path_create)
|
||||
|
||||
rc.append((lv_path, segs))
|
||||
return dbus.Array(rc, signature="(oa(tts))")
|
||||
|
||||
# noinspection PyUnusedLocal,PyPep8Naming
|
||||
def __init__(self, lvm_path, Uuid, Name,
|
||||
Fmt, SizeBytes, FreeBytes, UsedBytes, DevSizeBytes,
|
||||
MdaSizeBytes, MdaFreeBytes, BaStart, BaSizeBytes,
|
||||
PeStart, PeCount, PeAllocCount, attr, Tags, vg_name,
|
||||
vg_uuid):
|
||||
utils.init_class_from_arguments(self)
|
||||
self.pe_segments = cfg.db.pv_pe_segments(Uuid)
|
||||
|
||||
self.lv = self._lv_object_list(vg_name)
|
||||
|
||||
if vg_name:
|
||||
self.vg_path = cfg.om.get_object_path_by_uuid_lvm_id(
|
||||
vg_uuid, vg_name, vg_obj_path_generate)
|
||||
else:
|
||||
self.vg_path = '/'
|
||||
|
||||
def identifiers(self):
|
||||
return (self.Uuid, self.lvm_path)
|
||||
|
||||
def create_dbus_object(self, path):
|
||||
if not path:
|
||||
path = cfg.om.get_object_path_by_uuid_lvm_id(self.Uuid, self.Name,
|
||||
pv_obj_path_generate)
|
||||
return Pv(path, self)
|
||||
|
||||
# noinspection PyMethodMayBeStatic
|
||||
def creation_signature(self):
|
||||
return (Pv, pv_obj_path_generate)
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
@utils.dbus_property(PV_INTERFACE, 'Uuid', 's') # PV UUID/pv_uuid
|
||||
@utils.dbus_property(PV_INTERFACE, 'Name', 's') # PV/pv_name
|
||||
@utils.dbus_property(PV_INTERFACE, 'Fmt', 's') # Fmt/pv_fmt
|
||||
@utils.dbus_property(PV_INTERFACE, 'SizeBytes', 't') # PSize/pv_size
|
||||
@utils.dbus_property(PV_INTERFACE, 'FreeBytes', 't') # PFree/pv_free
|
||||
@utils.dbus_property(PV_INTERFACE, 'UsedBytes', 't') # Used/pv_used
|
||||
@utils.dbus_property(PV_INTERFACE, 'DevSizeBytes', 't') # DevSize/dev_size
|
||||
@utils.dbus_property(PV_INTERFACE, 'MdaSizeBytes', 't') # PMdaSize/pv_mda_size
|
||||
@utils.dbus_property(PV_INTERFACE, 'MdaFreeBytes', 't') # PMdaFree/pv_mda_free
|
||||
@utils.dbus_property(PV_INTERFACE, 'BaStart', 't') # BA start/pv_ba_start
|
||||
@utils.dbus_property(PV_INTERFACE, 'BaSizeBytes', 't') # BA size/pv_ba_size
|
||||
@utils.dbus_property(PV_INTERFACE, 'PeStart', 't') # 1st PE/pe_start
|
||||
@utils.dbus_property(PV_INTERFACE, 'PeCount', 't') # PE/pv_pe_count
|
||||
@utils.dbus_property(PV_INTERFACE, 'PeAllocCount', 't') # PE Allocation count
|
||||
class Pv(AutomatedProperties):
|
||||
# For properties that we need custom handlers we need these, otherwise
|
||||
# we won't get our introspection data
|
||||
_Tags_meta = ("as", PV_INTERFACE)
|
||||
_PeSegments_meta = ("a(tt)", PV_INTERFACE)
|
||||
_Exportable_meta = ("b", PV_INTERFACE)
|
||||
_Allocatable_meta = ("b", PV_INTERFACE)
|
||||
_Missing_meta = ("b", PV_INTERFACE)
|
||||
_Lv_meta = ("a(oa(tts))", PV_INTERFACE)
|
||||
_Vg_meta = ("o", PV_INTERFACE)
|
||||
|
||||
# noinspection PyUnusedLocal,PyPep8Naming
|
||||
def __init__(self, object_path, state_obj):
|
||||
super(Pv, self).__init__(object_path, pvs_state_retrieve)
|
||||
self.set_interface(PV_INTERFACE)
|
||||
self.state = state_obj
|
||||
|
||||
@staticmethod
|
||||
def _remove(pv_uuid, pv_name, remove_options):
|
||||
# Remove the PV, if successful then remove from the model
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(pv_uuid, pv_name)
|
||||
|
||||
if dbo:
|
||||
rc, out, err = cmdhandler.pv_remove(pv_name, remove_options)
|
||||
if rc == 0:
|
||||
cfg.om.remove_object(dbo, True)
|
||||
else:
|
||||
# Need to work on error handling, need consistent
|
||||
raise dbus.exceptions.DBusException(
|
||||
PV_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
PV_INTERFACE,
|
||||
'PV with uuid %s and name %s not present!' %
|
||||
(pv_uuid, pv_name))
|
||||
return '/'
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=PV_INTERFACE,
|
||||
in_signature='ia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Remove(self, tmo, remove_options, cb, cbe):
|
||||
r = RequestEntry(
|
||||
tmo, Pv._remove,
|
||||
(self.Uuid, self.lvm_id, remove_options),
|
||||
cb, cbe, return_tuple=False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _resize(pv_uuid, pv_name, new_size_bytes, resize_options):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(pv_uuid, pv_name)
|
||||
|
||||
if dbo:
|
||||
rc, out, err = cmdhandler.pv_resize(pv_name, new_size_bytes,
|
||||
resize_options)
|
||||
if rc == 0:
|
||||
dbo.refresh()
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
PV_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
PV_INTERFACE,
|
||||
'PV with uuid %s and name %s not present!' %
|
||||
(pv_uuid, pv_name))
|
||||
return '/'
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=PV_INTERFACE,
|
||||
in_signature='tia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def ReSize(self, new_size_bytes, tmo, resize_options, cb, cbe):
|
||||
r = RequestEntry(
|
||||
tmo, Pv._resize,
|
||||
(self.Uuid, self.lvm_id, round_size(new_size_bytes),
|
||||
resize_options), cb, cbe, False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _allocation_enabled(pv_uuid, pv_name, yes_no, allocation_options):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(pv_uuid, pv_name)
|
||||
|
||||
if dbo:
|
||||
rc, out, err = cmdhandler.pv_allocatable(
|
||||
pv_name, yes_no, allocation_options)
|
||||
if rc == 0:
|
||||
cfg.load()
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
PV_INTERFACE, 'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
PV_INTERFACE,
|
||||
'PV with uuid %s and name %s not present!' %
|
||||
(pv_uuid, pv_name))
|
||||
return '/'
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=PV_INTERFACE,
|
||||
in_signature='bia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def AllocationEnabled(self, yes, tmo, allocation_options, cb, cbe):
|
||||
r = RequestEntry(
|
||||
tmo, Pv._allocation_enabled,
|
||||
(self.Uuid, self.lvm_id,
|
||||
yes, allocation_options),
|
||||
cb, cbe, False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@property
|
||||
def Tags(self):
|
||||
return utils.parse_tags(self.state.Tags)
|
||||
|
||||
@property
|
||||
def PeSegments(self):
|
||||
if len(self.state.pe_segments):
|
||||
return self.state.pe_segments
|
||||
return dbus.Array([], '(tt)')
|
||||
|
||||
@property
|
||||
def Exportable(self):
|
||||
if self.state.attr[1] == 'x':
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def Allocatable(self):
|
||||
if self.state.attr[0] == 'a':
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def Missing(self):
|
||||
if self.state.attr[2] == 'm':
|
||||
return True
|
||||
return False
|
||||
|
||||
def object_path(self):
|
||||
return self._object_path
|
||||
|
||||
@property
|
||||
def lvm_id(self):
|
||||
return self.state.lvm_id
|
||||
|
||||
@property
|
||||
def identifiers(self):
|
||||
return self.state.identifiers()
|
||||
|
||||
@property
|
||||
def Lv(self):
|
||||
return self.state.lv
|
||||
|
||||
@property
|
||||
def Vg(self):
|
||||
return self.state.vg_path
|
||||
45
daemons/lvmdbusd/refresh.py
Normal file
45
daemons/lvmdbusd/refresh.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Try and minimize the refreshes we do.
|
||||
|
||||
import threading
|
||||
from .request import RequestEntry
|
||||
from . import cfg
|
||||
from . import utils
|
||||
|
||||
_rlock = threading.RLock()
|
||||
_count = 0
|
||||
|
||||
|
||||
def handle_external_event(command):
|
||||
utils.log_debug("External event: '%s'" % command)
|
||||
event_complete()
|
||||
cfg.load()
|
||||
|
||||
|
||||
def event_add(params):
|
||||
global _rlock
|
||||
global _count
|
||||
with _rlock:
|
||||
if _count == 0:
|
||||
_count += 1
|
||||
r = RequestEntry(
|
||||
-1, handle_external_event,
|
||||
params, None, None, False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
|
||||
def event_complete():
|
||||
global _rlock
|
||||
global _count
|
||||
with _rlock:
|
||||
if _count > 0:
|
||||
_count -= 1
|
||||
return _count
|
||||
151
daemons/lvmdbusd/request.py
Normal file
151
daemons/lvmdbusd/request.py
Normal file
@@ -0,0 +1,151 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import threading
|
||||
# noinspection PyUnresolvedReferences
|
||||
from gi.repository import GLib
|
||||
from .job import Job
|
||||
from . import cfg
|
||||
import traceback
|
||||
from .utils import log_error
|
||||
|
||||
|
||||
class RequestEntry(object):
|
||||
def __init__(self, tmo, method, arguments, cb, cb_error,
|
||||
return_tuple=True, job_state=None):
|
||||
self.tmo = tmo
|
||||
self.method = method
|
||||
self.arguments = arguments
|
||||
self.cb = cb
|
||||
self.cb_error = cb_error
|
||||
|
||||
self.timer_id = -1
|
||||
self.lock = threading.RLock()
|
||||
self.done = False
|
||||
self._result = None
|
||||
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
|
||||
pass
|
||||
elif tmo == 0:
|
||||
self._return_job()
|
||||
else:
|
||||
self.timer_id = GLib.timeout_add_seconds(
|
||||
tmo, RequestEntry._request_timeout, self)
|
||||
|
||||
@staticmethod
|
||||
def _request_timeout(r):
|
||||
"""
|
||||
Method which gets called when the timer runs out!
|
||||
:param r: RequestEntry which timed out
|
||||
:return: Nothing
|
||||
"""
|
||||
r.timer_expired()
|
||||
|
||||
def _return_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()))
|
||||
else:
|
||||
self.cb(self._job.dbus_object_path())
|
||||
|
||||
def run_cmd(self):
|
||||
try:
|
||||
result = self.method(*self.arguments)
|
||||
self.register_result(result)
|
||||
except Exception as e:
|
||||
# Use the request entry to return the result as the client may
|
||||
# have gotten a job by the time we hit an error
|
||||
# Lets get the stacktrace and set that to the error message
|
||||
st = traceback.format_exc()
|
||||
cfg.blackbox.dump()
|
||||
log_error("Exception returned to client: \n%s" % st)
|
||||
self.register_error(-1, str(e), e)
|
||||
|
||||
def is_done(self):
|
||||
with self.lock:
|
||||
rc = self.done
|
||||
return rc
|
||||
|
||||
def get_errors(self):
|
||||
with self.lock:
|
||||
return (self._rc, self._rc_error)
|
||||
|
||||
def result(self):
|
||||
with self.lock:
|
||||
if self.done:
|
||||
return self._result
|
||||
return '/'
|
||||
|
||||
def _reg_ending(self, result, error_rc=0, error_msg=None,
|
||||
error_exception=None):
|
||||
with self.lock:
|
||||
self.done = True
|
||||
if self.timer_id != -1:
|
||||
# Try to prevent the timer from firing
|
||||
GLib.source_remove(self.timer_id)
|
||||
|
||||
self._result = result
|
||||
self._rc = error_rc
|
||||
self._rc_error = error_msg
|
||||
|
||||
if not self._job:
|
||||
# We finished and there is no job, so return result or error
|
||||
# now!
|
||||
# Note: If we don't have a valid cb or cbe, this indicates a
|
||||
# request that doesn't need a response as we already returned
|
||||
# one before the request was processed.
|
||||
if error_rc == 0:
|
||||
if self.cb:
|
||||
if self._return_tuple:
|
||||
self.cb((result, '/'))
|
||||
else:
|
||||
self.cb(result)
|
||||
else:
|
||||
if self.cb_error:
|
||||
if not error_exception:
|
||||
if not error_msg:
|
||||
error_exception = Exception(
|
||||
"An error occurred, but no reason was "
|
||||
"given, see service logs!")
|
||||
else:
|
||||
error_exception = Exception(error_msg)
|
||||
|
||||
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.
|
||||
self._job.Complete = True
|
||||
self._job = None
|
||||
|
||||
def register_error(self, error_rc, error_message, error_exception):
|
||||
self._reg_ending(None, error_rc, error_message, error_exception)
|
||||
|
||||
def register_result(self, result):
|
||||
self._reg_ending(result)
|
||||
|
||||
def timer_expired(self):
|
||||
with self.lock:
|
||||
# Set the timer back to -1 as we will get a warning if we try
|
||||
# to remove a timer that doesn't exist
|
||||
self.timer_id = -1
|
||||
if not self.done:
|
||||
# Create dbus job object and return path to caller
|
||||
self._return_job()
|
||||
else:
|
||||
# The job is done, we have nothing to do
|
||||
pass
|
||||
|
||||
return False
|
||||
27
daemons/lvmdbusd/state.py
Normal file
27
daemons/lvmdbusd/state.py
Normal file
@@ -0,0 +1,27 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
|
||||
class State(object, metaclass=ABCMeta):
|
||||
@abstractmethod
|
||||
def lvm_id(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def identifiers(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def create_dbus_object(self, path):
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return '*****\n' + str(self.__dict__) + '\n******\n'
|
||||
54
daemons/lvmdbusd/udevwatch.py
Normal file
54
daemons/lvmdbusd/udevwatch.py
Normal file
@@ -0,0 +1,54 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import pyudev
|
||||
from .refresh import event_add
|
||||
from . import cfg
|
||||
|
||||
observer = None
|
||||
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
def filter_event(action, device):
|
||||
# Filter for events of interest and add a request object to be processed
|
||||
# when appropriate.
|
||||
refresh = False
|
||||
|
||||
if '.ID_FS_TYPE_NEW' in device:
|
||||
fs_type_new = device['.ID_FS_TYPE_NEW']
|
||||
|
||||
if 'LVM' in fs_type_new:
|
||||
refresh = True
|
||||
elif fs_type_new == '':
|
||||
# Check to see if the device was one we knew about
|
||||
if 'DEVNAME' in device:
|
||||
found = cfg.om.get_object_by_lvm_id(device['DEVNAME'])
|
||||
if found:
|
||||
refresh = True
|
||||
|
||||
if 'DM_LV_NAME' in device:
|
||||
refresh = True
|
||||
|
||||
if refresh:
|
||||
event_add(('udev',))
|
||||
|
||||
|
||||
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()
|
||||
|
||||
|
||||
def remove():
|
||||
global observer
|
||||
observer.stop()
|
||||
observer = None
|
||||
484
daemons/lvmdbusd/utils.py
Normal file
484
daemons/lvmdbusd/utils.py
Normal file
@@ -0,0 +1,484 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import xml.etree.ElementTree as Et
|
||||
import sys
|
||||
import inspect
|
||||
import ctypes
|
||||
import os
|
||||
import string
|
||||
|
||||
import dbus
|
||||
import dbus.service
|
||||
import dbus.mainloop.glib
|
||||
|
||||
try:
|
||||
from . import cfg
|
||||
except SystemError:
|
||||
import cfg
|
||||
|
||||
STDOUT_TTY = os.isatty(sys.stdout.fileno())
|
||||
|
||||
|
||||
def rtype(dbus_type):
|
||||
"""
|
||||
Decorator making sure that the decorated function returns a value of
|
||||
specified type.
|
||||
:param dbus_type: The specific dbus type to return value as
|
||||
"""
|
||||
|
||||
def decorator(fn):
|
||||
def decorated(*args, **kwargs):
|
||||
return dbus_type(fn(*args, **kwargs))
|
||||
|
||||
return decorated
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
# Field is expected to be a number, handle the corner cases when parsing
|
||||
@rtype(dbus.UInt64)
|
||||
def n(v):
|
||||
if not v:
|
||||
return 0
|
||||
return int(float(v))
|
||||
|
||||
|
||||
@rtype(dbus.UInt32)
|
||||
def n32(v):
|
||||
if not v:
|
||||
return 0
|
||||
return int(float(v))
|
||||
|
||||
|
||||
# noinspection PyProtectedMember
|
||||
def init_class_from_arguments(obj_instance):
|
||||
for k, v in list(sys._getframe(1).f_locals.items()):
|
||||
if k != 'self':
|
||||
nt = k
|
||||
|
||||
# If the current attribute has a value, but the incoming does
|
||||
# not, don't overwrite it. Otherwise the default values on the
|
||||
# property decorator don't work as expected.
|
||||
cur = getattr(obj_instance, nt, v)
|
||||
|
||||
# print 'Init class %s = %s' % (nt, str(v))
|
||||
if not (cur and len(str(cur)) and (v is None or len(str(v))) == 0):
|
||||
setattr(obj_instance, nt, v)
|
||||
|
||||
|
||||
def get_properties(f):
|
||||
"""
|
||||
Walks through an object instance or it's parent class(es) and determines
|
||||
which attributes are properties and if they were created to be used for
|
||||
dbus.
|
||||
:param f: Object to inspect
|
||||
:return: A dictionary of tuples with each tuple being:
|
||||
0 = An array of dicts with the keys being: p_t, p_name,
|
||||
p_access(type, name, access)
|
||||
1 = Hash of property names and current value
|
||||
"""
|
||||
interfaces = dict()
|
||||
|
||||
for c in inspect.getmro(f.__class__):
|
||||
|
||||
h = vars(c)
|
||||
for p, value in h.items():
|
||||
if isinstance(value, property):
|
||||
# We found a property, see if it has a metadata type
|
||||
key = attribute_type_name(p)
|
||||
if key in h:
|
||||
interface = h[key][1]
|
||||
|
||||
if interface not in interfaces:
|
||||
interfaces[interface] = ([], {})
|
||||
|
||||
access = ''
|
||||
if getattr(f.__class__, p).fget:
|
||||
access += 'read'
|
||||
if getattr(f.__class__, p).fset:
|
||||
access += 'write'
|
||||
|
||||
interfaces[interface][0].append(
|
||||
dict(
|
||||
p_t=getattr(f, key)[0],
|
||||
p_name=p,
|
||||
p_access=access))
|
||||
|
||||
interfaces[interface][1][p] = getattr(f, p)
|
||||
|
||||
return interfaces
|
||||
|
||||
|
||||
def get_object_property_diff(o_prop, n_prop):
|
||||
"""
|
||||
Walk through each object properties and report what has changed and with
|
||||
the new values
|
||||
:param o_prop: Old keys/values
|
||||
:param n_prop: New keys/values
|
||||
:return: hash of properties that have changed and their new value
|
||||
"""
|
||||
rc = {}
|
||||
|
||||
for intf_k, intf_v in o_prop.items():
|
||||
for k, v in list(intf_v[1].items()):
|
||||
# print('Comparing %s:%s to %s:%s' %
|
||||
# (k, o_prop[intf_k][1][k], k, str(n_prop[intf_k][1][k])))
|
||||
if o_prop[intf_k][1][k] != n_prop[intf_k][1][k]:
|
||||
new_value = n_prop[intf_k][1][k]
|
||||
|
||||
if intf_k not in rc:
|
||||
rc[intf_k] = dict()
|
||||
|
||||
rc[intf_k][k] = new_value
|
||||
return rc
|
||||
|
||||
|
||||
def add_properties(xml, interface, props):
|
||||
"""
|
||||
Given xml that describes the interface, add property values to the XML
|
||||
for the specified interface.
|
||||
:param xml: XML to edit
|
||||
:param interface: Interface to add the properties too
|
||||
:param props: Output from get_properties
|
||||
:return: updated XML string
|
||||
"""
|
||||
root = Et.fromstring(xml)
|
||||
|
||||
if props:
|
||||
|
||||
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))
|
||||
|
||||
return Et.tostring(root, encoding='utf8')
|
||||
return xml
|
||||
|
||||
|
||||
def attribute_type_name(name):
|
||||
"""
|
||||
Given the property name, return string of the attribute type
|
||||
:param name:
|
||||
:return:
|
||||
"""
|
||||
return "_%s_meta" % name
|
||||
|
||||
|
||||
_type_map = dict(
|
||||
s=dbus.String,
|
||||
o=dbus.ObjectPath,
|
||||
t=dbus.UInt64,
|
||||
x=dbus.Int64,
|
||||
u=dbus.UInt32,
|
||||
i=dbus.Int32,
|
||||
n=dbus.Int16,
|
||||
q=dbus.UInt16,
|
||||
d=dbus.Double,
|
||||
y=dbus.Byte,
|
||||
b=dbus.Boolean)
|
||||
|
||||
|
||||
def _pass_through(v):
|
||||
"""
|
||||
If we have something which is not a simple type we return the original
|
||||
value un-wrapped.
|
||||
:param v:
|
||||
:return:
|
||||
"""
|
||||
return v
|
||||
|
||||
|
||||
def _dbus_type(t, value):
|
||||
return _type_map.get(t, _pass_through)(value)
|
||||
|
||||
|
||||
def dbus_property(interface_name, name, dbus_type, doc=None):
|
||||
"""
|
||||
Creates the get/set properties for the given name. It assumes that the
|
||||
actual attribute is '_' + name and the attribute metadata is stuffed in
|
||||
_name_type.
|
||||
|
||||
There is probably a better way todo this.
|
||||
:param interface_name: Dbus interface this property is associated with
|
||||
:param name: Name of property
|
||||
:param dbus_type: dbus string type eg. s,t,i,x
|
||||
:param doc: Python __doc__ for the property
|
||||
:return:
|
||||
"""
|
||||
attribute_name = '_' + name
|
||||
|
||||
def getter(self):
|
||||
t = getattr(self, attribute_name + '_meta')[0]
|
||||
return _dbus_type(t, getattr(self.state, attribute_name[1:]))
|
||||
|
||||
prop = property(getter, None, None, doc)
|
||||
|
||||
def decorator(cls):
|
||||
setattr(cls, attribute_name + '_meta', (dbus_type, interface_name))
|
||||
setattr(cls, name, prop)
|
||||
return cls
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
def parse_tags(tags):
|
||||
if len(tags):
|
||||
if ',' in tags:
|
||||
return tags.split(',')
|
||||
return sorted([tags])
|
||||
return dbus.Array([], signature='s')
|
||||
|
||||
|
||||
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 and attributes:
|
||||
print(color(msg, *attributes))
|
||||
else:
|
||||
print(msg)
|
||||
|
||||
cfg.stdout_lock.release()
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
# Serializes access to stdout to prevent interleaved output
|
||||
# @param msg Message to output to stdout
|
||||
# @return None
|
||||
def log_debug(msg, *attributes):
|
||||
if cfg.DEBUG:
|
||||
_common_log(msg, *attributes)
|
||||
|
||||
|
||||
def log_error(msg, *attributes):
|
||||
_common_log(msg, *attributes)
|
||||
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
def handler(signum, frame):
|
||||
cfg.run.value = 0
|
||||
log_debug('Signal handler called with signal %d' % signum)
|
||||
if cfg.loop is not None:
|
||||
cfg.loop.quit()
|
||||
|
||||
|
||||
def pv_obj_path_generate():
|
||||
return cfg.PV_OBJ_PATH + "/%d" % next(cfg.pv_id)
|
||||
|
||||
|
||||
def vg_obj_path_generate():
|
||||
return cfg.VG_OBJ_PATH + "/%d" % next(cfg.vg_id)
|
||||
|
||||
|
||||
def lv_object_path_method(name, meta):
|
||||
if name[0] == '[':
|
||||
return _hidden_lv_obj_path_generate
|
||||
elif meta[0][0] == 't':
|
||||
return _thin_pool_obj_path_generate
|
||||
elif meta[0][0] == 'C' and 'pool' in meta[1]:
|
||||
return _cache_pool_obj_path_generate
|
||||
|
||||
return _lv_obj_path_generate
|
||||
|
||||
|
||||
# Note: None of the individual LV path generate functions should be called
|
||||
# directly, they should only be dispatched through lv_object_path_method
|
||||
|
||||
def _lv_obj_path_generate():
|
||||
return cfg.LV_OBJ_PATH + "/%d" % next(cfg.lv_id)
|
||||
|
||||
|
||||
def _thin_pool_obj_path_generate():
|
||||
return cfg.THIN_POOL_PATH + "/%d" % next(cfg.thin_id)
|
||||
|
||||
|
||||
def _cache_pool_obj_path_generate():
|
||||
return cfg.CACHE_POOL_PATH + "/%d" % next(cfg.cache_pool_id)
|
||||
|
||||
|
||||
def _hidden_lv_obj_path_generate():
|
||||
return cfg.HIDDEN_LV_PATH + "/%d" % next(cfg.hidden_lv)
|
||||
|
||||
|
||||
def job_obj_path_generate():
|
||||
return cfg.JOB_OBJ_PATH + "/%d" % next(cfg.job_id)
|
||||
|
||||
|
||||
def color(text, *user_styles):
|
||||
styles = {
|
||||
# styles
|
||||
'reset': '\033[0m',
|
||||
'bold': '\033[01m',
|
||||
'disabled': '\033[02m',
|
||||
'underline': '\033[04m',
|
||||
'reverse': '\033[07m',
|
||||
'strike_through': '\033[09m',
|
||||
'invisible': '\033[08m',
|
||||
# text colors
|
||||
'fg_black': '\033[30m',
|
||||
'fg_red': '\033[31m',
|
||||
'fg_green': '\033[32m',
|
||||
'fg_orange': '\033[33m',
|
||||
'fg_blue': '\033[34m',
|
||||
'fg_purple': '\033[35m',
|
||||
'fg_cyan': '\033[36m',
|
||||
'fg_light_grey': '\033[37m',
|
||||
'fg_dark_grey': '\033[90m',
|
||||
'fg_light_red': '\033[91m',
|
||||
'fg_light_green': '\033[92m',
|
||||
'fg_yellow': '\033[93m',
|
||||
'fg_light_blue': '\033[94m',
|
||||
'fg_pink': '\033[95m',
|
||||
'fg_light_cyan': '\033[96m',
|
||||
# background colors
|
||||
'bg_black': '\033[40m',
|
||||
'bg_red': '\033[41m',
|
||||
'bg_green': '\033[42m',
|
||||
'bg_orange': '\033[43m',
|
||||
'bg_blue': '\033[44m',
|
||||
'bg_purple': '\033[45m',
|
||||
'bg_cyan': '\033[46m',
|
||||
'bg_light_grey': '\033[47m'
|
||||
}
|
||||
|
||||
color_text = ''
|
||||
for style in user_styles:
|
||||
try:
|
||||
color_text += styles[style]
|
||||
except KeyError:
|
||||
return 'def color: parameter {} does not exist'.format(style)
|
||||
color_text += text
|
||||
return '\033[0m{0}\033[0m'.format(color_text)
|
||||
|
||||
|
||||
def pv_range_append(cmd, device, start, end):
|
||||
if (start, end) == (0, 0):
|
||||
cmd.append(device)
|
||||
else:
|
||||
if start != 0 and end == 0:
|
||||
cmd.append("%s:%d-" % (device, start))
|
||||
else:
|
||||
cmd.append(
|
||||
"%s:%d-%d" %
|
||||
(device, start, end))
|
||||
|
||||
|
||||
def pv_dest_ranges(cmd, pv_dest_range_list):
|
||||
if len(pv_dest_range_list):
|
||||
for i in pv_dest_range_list:
|
||||
pv_range_append(cmd, *i)
|
||||
|
||||
|
||||
def round_size(size_bytes):
|
||||
bs = 512
|
||||
remainder = size_bytes % bs
|
||||
if not remainder:
|
||||
return size_bytes
|
||||
return size_bytes + bs - remainder
|
||||
|
||||
|
||||
_ALLOWABLE_CH = string.ascii_letters + string.digits + '#+-.:=@_\/%'
|
||||
_ALLOWABLE_CH_SET = set(_ALLOWABLE_CH)
|
||||
|
||||
_ALLOWABLE_VG_LV_CH = string.ascii_letters + string.digits + '.-_+'
|
||||
_ALLOWABLE_VG_LV_CH_SET = set(_ALLOWABLE_VG_LV_CH)
|
||||
_LV_NAME_RESERVED = ("_cdata", "_cmeta", "_corig", "_mimage", "_mlog",
|
||||
"_pmspare", "_rimage", "_rmeta", "_tdata", "_tmeta", "_vorigin")
|
||||
|
||||
# Tags can have the characters, based on the code
|
||||
# a-zA-Z0-9._-+/=!:&#
|
||||
_ALLOWABLE_TAG_CH = string.ascii_letters + string.digits + "._-+/=!:&#"
|
||||
_ALLOWABLE_TAG_CH_SET = set(_ALLOWABLE_TAG_CH)
|
||||
|
||||
|
||||
def _allowable_tag(tag_name):
|
||||
# LVM should impose a length restriction
|
||||
return set(tag_name) <= _ALLOWABLE_TAG_CH_SET
|
||||
|
||||
|
||||
def _allowable_vg_name(vg_name):
|
||||
if vg_name is None:
|
||||
raise ValueError("VG name is None or empty")
|
||||
|
||||
vg_len = len(vg_name)
|
||||
if vg_len == 0 or vg_len > 127:
|
||||
raise ValueError("VG name (%s) length (%d) not in the domain 1..127" %
|
||||
(vg_name, vg_len))
|
||||
|
||||
if not set(vg_name) <= _ALLOWABLE_VG_LV_CH_SET:
|
||||
raise ValueError("VG name (%s) contains invalid character, "
|
||||
"allowable set(%s)" % (vg_name, _ALLOWABLE_VG_LV_CH))
|
||||
|
||||
if vg_name == "." or vg_name == "..":
|
||||
raise ValueError('VG name (%s) cannot be "." or ".."' % (vg_name))
|
||||
|
||||
|
||||
def _allowable_lv_name(vg_name, lv_name):
|
||||
|
||||
if lv_name is None:
|
||||
raise ValueError("LV name is None or empty")
|
||||
|
||||
lv_len = len(lv_name)
|
||||
|
||||
# This length is derived from empirical testing
|
||||
if lv_len == 0 or (len(vg_name) + lv_len) > 125:
|
||||
raise ValueError("LV name (%s) length (%d) + VG name length "
|
||||
"not in the domain 1..125" % (lv_name, lv_len))
|
||||
|
||||
if not set(lv_name) <= _ALLOWABLE_VG_LV_CH_SET:
|
||||
raise ValueError("LV name (%s) contains invalid character, "
|
||||
"allowable (%s)" % (lv_name, _ALLOWABLE_VG_LV_CH))
|
||||
|
||||
if any(x in lv_name for x in _LV_NAME_RESERVED):
|
||||
raise ValueError("LV name (%s) contains a reserved word, "
|
||||
"reserved set(%s)" % (lv_name, str(_LV_NAME_RESERVED)))
|
||||
|
||||
if lv_name.startswith("snapshot") or lv_name.startswith("pvmove"):
|
||||
raise ValueError("LV name (%s) starts with a reserved word, "
|
||||
"reserved set(%s)" % (lv_name, str(["snapshot", "pvmove"])))
|
||||
|
||||
if lv_name[0] == '-':
|
||||
raise ValueError("LV name (%s) cannot start with a '-' "
|
||||
"character" % lv_name)
|
||||
|
||||
|
||||
def validate_device_path(interface, device):
|
||||
if not set(device) <= _ALLOWABLE_CH_SET:
|
||||
raise dbus.exceptions.DBusException(
|
||||
interface, 'Device path (%s) has invalid characters, '
|
||||
'allowable (%s)' % (device, _ALLOWABLE_CH))
|
||||
|
||||
|
||||
def validate_vg_name(interface, vg_name):
|
||||
try:
|
||||
_allowable_vg_name(vg_name)
|
||||
except ValueError as ve:
|
||||
raise dbus.exceptions.DBusException(
|
||||
interface, str(ve))
|
||||
|
||||
|
||||
def validate_lv_name(interface, vg_name, lv_name):
|
||||
try:
|
||||
_allowable_lv_name(vg_name, lv_name)
|
||||
except ValueError as ve:
|
||||
raise dbus.exceptions.DBusException(
|
||||
interface, str(ve))
|
||||
|
||||
|
||||
def validate_tag(interface, tag):
|
||||
if not _allowable_tag(tag):
|
||||
raise dbus.exceptions.DBusException(
|
||||
interface, 'tag (%s) contains invalid character, allowable set(%s)'
|
||||
% (tag, _ALLOWABLE_TAG_CH))
|
||||
959
daemons/lvmdbusd/vg.py
Normal file
959
daemons/lvmdbusd/vg.py
Normal file
@@ -0,0 +1,959 @@
|
||||
# Copyright (C) 2015-2016 Red Hat, Inc. All rights reserved.
|
||||
#
|
||||
# This copyrighted material is made available to anyone wishing to use,
|
||||
# modify, copy, or redistribute it subject to the terms and conditions
|
||||
# of the GNU General Public License v.2.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from .automatedproperties import AutomatedProperties
|
||||
|
||||
from . import utils
|
||||
from .utils import pv_obj_path_generate, vg_obj_path_generate, n
|
||||
import dbus
|
||||
from . import cfg
|
||||
from .cfg import VG_INTERFACE
|
||||
from . import cmdhandler
|
||||
from .request import RequestEntry
|
||||
from .loader import common
|
||||
from .state import State
|
||||
from . import background
|
||||
from .utils import round_size
|
||||
from .job import JobState
|
||||
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
def vgs_state_retrieve(selection, cache_refresh=True):
|
||||
rc = []
|
||||
|
||||
if cache_refresh:
|
||||
cfg.db.refresh()
|
||||
|
||||
for v in cfg.db.fetch_vgs(selection):
|
||||
rc.append(
|
||||
VgState(
|
||||
v['vg_uuid'], v['vg_name'], v['vg_fmt'], n(v['vg_size']),
|
||||
n(v['vg_free']), v['vg_sysid'], n(v['vg_extent_size']),
|
||||
n(v['vg_extent_count']), n(v['vg_free_count']),
|
||||
v['vg_profile'], n(v['max_lv']), n(v['max_pv']),
|
||||
n(v['pv_count']), n(v['lv_count']), n(v['snap_count']),
|
||||
n(v['vg_seqno']), n(v['vg_mda_count']),
|
||||
n(v['vg_mda_free']), n(v['vg_mda_size']),
|
||||
n(v['vg_mda_used_count']), v['vg_attr'], v['vg_tags']))
|
||||
return rc
|
||||
|
||||
|
||||
def load_vgs(vg_specific=None, object_path=None, refresh=False,
|
||||
emit_signal=False, cache_refresh=True):
|
||||
return common(vgs_state_retrieve, (Vg,), vg_specific, object_path, refresh,
|
||||
emit_signal, cache_refresh)
|
||||
|
||||
|
||||
# noinspection PyPep8Naming,PyUnresolvedReferences,PyUnusedLocal
|
||||
class VgState(State):
|
||||
@property
|
||||
def lvm_id(self):
|
||||
return self.Name
|
||||
|
||||
def identifiers(self):
|
||||
return (self.Uuid, self.Name)
|
||||
|
||||
def _lv_paths_build(self):
|
||||
rc = []
|
||||
for lv in cfg.db.lvs_in_vg(self.Uuid):
|
||||
(lv_name, meta, lv_uuid) = lv
|
||||
full_name = "%s/%s" % (self.Name, lv_name)
|
||||
|
||||
gen = utils.lv_object_path_method(lv_name, meta)
|
||||
|
||||
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')
|
||||
|
||||
def _pv_paths_build(self):
|
||||
rc = []
|
||||
for p in cfg.db.pvs_in_vg(self.Uuid):
|
||||
(pv_name, pv_uuid) = p
|
||||
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')
|
||||
|
||||
def __init__(self, Uuid, Name, Fmt,
|
||||
SizeBytes, FreeBytes, SysId, ExtentSizeBytes,
|
||||
ExtentCount, FreeCount, Profile, MaxLv, MaxPv, PvCount,
|
||||
LvCount, SnapCount, Seqno, MdaCount, MdaFree,
|
||||
MdaSizeBytes, MdaUsedCount, attr, tags):
|
||||
utils.init_class_from_arguments(self)
|
||||
self.Pvs = self._pv_paths_build()
|
||||
self.Lvs = self._lv_paths_build()
|
||||
|
||||
def create_dbus_object(self, path):
|
||||
if not path:
|
||||
path = cfg.om.get_object_path_by_uuid_lvm_id(
|
||||
self.Uuid, self.Name, vg_obj_path_generate)
|
||||
return Vg(path, self)
|
||||
|
||||
# noinspection PyMethodMayBeStatic
|
||||
def creation_signature(self):
|
||||
return (Vg, vg_obj_path_generate)
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
@utils.dbus_property(VG_INTERFACE, 'Uuid', 's')
|
||||
@utils.dbus_property(VG_INTERFACE, 'Name', 's')
|
||||
@utils.dbus_property(VG_INTERFACE, 'Fmt', 's')
|
||||
@utils.dbus_property(VG_INTERFACE, 'SizeBytes', 't', 0)
|
||||
@utils.dbus_property(VG_INTERFACE, 'FreeBytes', 't', 0)
|
||||
@utils.dbus_property(VG_INTERFACE, 'SysId', 's')
|
||||
@utils.dbus_property(VG_INTERFACE, 'ExtentSizeBytes', 't')
|
||||
@utils.dbus_property(VG_INTERFACE, 'ExtentCount', 't')
|
||||
@utils.dbus_property(VG_INTERFACE, 'FreeCount', 't')
|
||||
@utils.dbus_property(VG_INTERFACE, 'Profile', 's')
|
||||
@utils.dbus_property(VG_INTERFACE, 'MaxLv', 't')
|
||||
@utils.dbus_property(VG_INTERFACE, 'MaxPv', 't')
|
||||
@utils.dbus_property(VG_INTERFACE, 'PvCount', 't')
|
||||
@utils.dbus_property(VG_INTERFACE, 'LvCount', 't')
|
||||
@utils.dbus_property(VG_INTERFACE, 'SnapCount', 't')
|
||||
@utils.dbus_property(VG_INTERFACE, 'Seqno', 't')
|
||||
@utils.dbus_property(VG_INTERFACE, 'MdaCount', 't')
|
||||
@utils.dbus_property(VG_INTERFACE, 'MdaFree', 't')
|
||||
@utils.dbus_property(VG_INTERFACE, 'MdaSizeBytes', 't')
|
||||
@utils.dbus_property(VG_INTERFACE, 'MdaUsedCount', 't')
|
||||
class Vg(AutomatedProperties):
|
||||
_Tags_meta = ("as", VG_INTERFACE)
|
||||
_Pvs_meta = ("ao", VG_INTERFACE)
|
||||
_Lvs_meta = ("ao", VG_INTERFACE)
|
||||
_Writeable_meta = ("b", VG_INTERFACE)
|
||||
_Readable_meta = ("b", VG_INTERFACE)
|
||||
_Resizeable_meta = ("b", VG_INTERFACE)
|
||||
_Exportable_meta = ('b', VG_INTERFACE)
|
||||
_Partial_meta = ('b', VG_INTERFACE)
|
||||
_AllocContiguous_meta = ('b', VG_INTERFACE)
|
||||
_AllocCling_meta = ('b', VG_INTERFACE)
|
||||
_AllocNormal_meta = ('b', VG_INTERFACE)
|
||||
_AllocAnywhere_meta = ('b', VG_INTERFACE)
|
||||
_Clustered_meta = ('b', VG_INTERFACE)
|
||||
|
||||
# noinspection PyUnusedLocal,PyPep8Naming
|
||||
def __init__(self, object_path, object_state):
|
||||
super(Vg, self).__init__(object_path, vgs_state_retrieve)
|
||||
self.set_interface(VG_INTERFACE)
|
||||
self._object_path = object_path
|
||||
self.state = object_state
|
||||
|
||||
@staticmethod
|
||||
def fetch_new_lv(vg_name, lv_name):
|
||||
cfg.load()
|
||||
return cfg.om.get_object_by_lvm_id("%s/%s" % (vg_name, lv_name))
|
||||
|
||||
@staticmethod
|
||||
def _rename(uuid, vg_name, new_name, rename_options):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||
|
||||
if dbo:
|
||||
rc, out, err = cmdhandler.vg_rename(vg_name, new_name,
|
||||
rename_options)
|
||||
if rc == 0:
|
||||
cfg.load()
|
||||
else:
|
||||
# Need to work on error handling, need consistent
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'VG with uuid %s and name %s not present!' %
|
||||
(uuid, vg_name))
|
||||
return '/'
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='sia{sv}', out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Rename(self, name, tmo, rename_options, cb, cbe):
|
||||
utils.validate_vg_name(VG_INTERFACE, name)
|
||||
r = RequestEntry(tmo, Vg._rename,
|
||||
(self.state.Uuid, self.state.lvm_id, name,
|
||||
rename_options), cb, cbe, False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _remove(uuid, vg_name, remove_options):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||
|
||||
if dbo:
|
||||
# Remove the VG, if successful then remove from the model
|
||||
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(
|
||||
VG_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'VG with uuid %s and name %s not present!' %
|
||||
(uuid, vg_name))
|
||||
return '/'
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='ia{sv}', out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Remove(self, tmo, remove_options, cb, cbe):
|
||||
r = RequestEntry(tmo, Vg._remove,
|
||||
(self.state.Uuid, self.state.lvm_id, remove_options),
|
||||
cb, cbe, False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _change(uuid, vg_name, change_options):
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||
|
||||
if dbo:
|
||||
rc, out, err = cmdhandler.vg_change(change_options, vg_name)
|
||||
|
||||
# To use an example with d-feet (Method input)
|
||||
# {"activate": __import__('gi.repository.GLib', globals(),
|
||||
# locals(), ['Variant']).Variant("s", "n")}
|
||||
|
||||
if rc == 0:
|
||||
cfg.load()
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'VG with uuid %s and name %s not present!' %
|
||||
(uuid, vg_name))
|
||||
return '/'
|
||||
|
||||
# TODO: This should be broken into a number of different methods
|
||||
# instead of having one method that takes a hash for parameters. Some of
|
||||
# the changes that vgchange does works on entire system, not just a
|
||||
# specfic vg, thus that should be in the Manager interface.
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='ia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Change(self, tmo, change_options, cb, cbe):
|
||||
r = RequestEntry(tmo, Vg._change,
|
||||
(self.state.Uuid, self.state.lvm_id, change_options),
|
||||
cb, cbe, False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _reduce(uuid, vg_name, missing, pv_object_paths, reduce_options):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||
|
||||
if dbo:
|
||||
pv_devices = []
|
||||
|
||||
# If pv_object_paths is not empty, then get the device paths
|
||||
if pv_object_paths and len(pv_object_paths) > 0:
|
||||
for pv_op in pv_object_paths:
|
||||
pv = cfg.om.get_object_by_path(pv_op)
|
||||
if pv:
|
||||
pv_devices.append(pv.lvm_id)
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'PV Object path not found = %s!' % pv_op)
|
||||
|
||||
rc, out, err = cmdhandler.vg_reduce(vg_name, missing, pv_devices,
|
||||
reduce_options)
|
||||
if rc == 0:
|
||||
cfg.load()
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE, 'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'VG with uuid %s and name %s not present!' %
|
||||
(uuid, vg_name))
|
||||
return '/'
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='baoia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Reduce(self, missing, pv_object_paths, tmo, reduce_options, cb, cbe):
|
||||
r = RequestEntry(tmo, Vg._reduce,
|
||||
(self.state.Uuid, self.state.lvm_id, missing,
|
||||
pv_object_paths, reduce_options), cb, cbe, False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _extend(uuid, vg_name, pv_object_paths, extend_options):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||
|
||||
if dbo:
|
||||
extend_devices = []
|
||||
|
||||
for i in pv_object_paths:
|
||||
pv = cfg.om.get_object_by_path(i)
|
||||
if pv:
|
||||
extend_devices.append(pv.lvm_id)
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE, 'PV Object path not found = %s!' % i)
|
||||
|
||||
if len(extend_devices):
|
||||
rc, out, err = cmdhandler.vg_extend(vg_name, extend_devices,
|
||||
extend_options)
|
||||
if rc == 0:
|
||||
cfg.load()
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE, 'No pv_object_paths provided!')
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'VG with uuid %s and name %s not present!' %
|
||||
(uuid, vg_name))
|
||||
return '/'
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='aoia{sv}', out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Extend(self, pv_object_paths, tmo, extend_options, cb, cbe):
|
||||
r = RequestEntry(tmo, Vg._extend,
|
||||
(self.state.Uuid, self.state.lvm_id, pv_object_paths,
|
||||
extend_options),
|
||||
cb, cbe, False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='o(tt)a(ott)ia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Move(self, pv_src_obj, pv_source_range, pv_dests_and_ranges,
|
||||
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,
|
||||
create_options):
|
||||
# Make sure we have a dbus object representing it
|
||||
pv_dests = []
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||
|
||||
if dbo:
|
||||
if len(pv_dests_and_ranges):
|
||||
for pr in pv_dests_and_ranges:
|
||||
pv_dbus_obj = cfg.om.get_object_by_path(pr[0])
|
||||
if not pv_dbus_obj:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'PV Destination (%s) not found' % pr[0])
|
||||
|
||||
pv_dests.append((pv_dbus_obj.lvm_id, pr[1], pr[2]))
|
||||
|
||||
rc, out, err = cmdhandler.vg_lv_create(
|
||||
vg_name, create_options, name, size_bytes, pv_dests)
|
||||
|
||||
if rc == 0:
|
||||
return Vg.fetch_new_lv(vg_name, name)
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'VG with uuid %s and name %s not present!' %
|
||||
(uuid, vg_name))
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='sta(ott)ia{sv}',
|
||||
out_signature='(oo)',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def LvCreate(self, name, size_bytes, pv_dests_and_ranges,
|
||||
tmo, create_options, cb, cbe):
|
||||
"""
|
||||
This one it for the advanced users that want to roll their own
|
||||
:param name: Name of the LV
|
||||
:param size_bytes: Size of LV in bytes
|
||||
:param pv_dests_and_ranges: Optional array of PV object paths and
|
||||
ranges
|
||||
:param tmo: -1 == Wait forever, 0 == return job immediately, > 0 ==
|
||||
willing to wait that number of seconds before
|
||||
getting a job
|
||||
:param create_options: hash of key/value pairs
|
||||
:param cb: Internal, not accessible by dbus API user
|
||||
:param cbe: Internal, not accessible by dbus API user
|
||||
:return: (oo) First object path is newly created object, second is
|
||||
job object path if created. Each == '/' when it doesn't
|
||||
apply.
|
||||
"""
|
||||
utils.validate_lv_name(VG_INTERFACE, self.Name, name)
|
||||
r = RequestEntry(tmo, Vg._lv_create,
|
||||
(self.state.Uuid, self.state.lvm_id,
|
||||
name, round_size(size_bytes), pv_dests_and_ranges,
|
||||
create_options), cb, cbe)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _lv_create_linear(uuid, vg_name, name, size_bytes,
|
||||
thin_pool, create_options):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||
|
||||
if dbo:
|
||||
rc, out, err = cmdhandler.vg_lv_create_linear(
|
||||
vg_name, create_options, name, size_bytes, thin_pool)
|
||||
|
||||
if rc == 0:
|
||||
created_lv = Vg.fetch_new_lv(vg_name, name)
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'VG with uuid %s and name %s not present!' %
|
||||
(uuid, vg_name))
|
||||
|
||||
return created_lv
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='stbia{sv}',
|
||||
out_signature='(oo)',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def LvCreateLinear(self, name, size_bytes,
|
||||
thin_pool, tmo, create_options, cb, cbe):
|
||||
utils.validate_lv_name(VG_INTERFACE, self.Name, name)
|
||||
r = RequestEntry(tmo, Vg._lv_create_linear,
|
||||
(self.state.Uuid, self.state.lvm_id,
|
||||
name, round_size(size_bytes), thin_pool,
|
||||
create_options), cb, cbe)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _lv_create_striped(uuid, vg_name, name, size_bytes, num_stripes,
|
||||
stripe_size_kb, thin_pool, create_options):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||
|
||||
if dbo:
|
||||
rc, out, err = cmdhandler.vg_lv_create_striped(
|
||||
vg_name, create_options, name, size_bytes,
|
||||
num_stripes, stripe_size_kb, thin_pool)
|
||||
if rc == 0:
|
||||
created_lv = Vg.fetch_new_lv(vg_name, name)
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE, 'VG with uuid %s and name %s not present!' %
|
||||
(uuid, vg_name))
|
||||
|
||||
return created_lv
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='stuubia{sv}',
|
||||
out_signature='(oo)',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def LvCreateStriped(self, name, size_bytes, num_stripes,
|
||||
stripe_size_kb, thin_pool, tmo, create_options,
|
||||
cb, cbe):
|
||||
utils.validate_lv_name(VG_INTERFACE, self.Name, name)
|
||||
r = RequestEntry(
|
||||
tmo, Vg._lv_create_striped,
|
||||
(self.state.Uuid, self.state.lvm_id, name,
|
||||
round_size(size_bytes), num_stripes, stripe_size_kb,
|
||||
thin_pool, create_options),
|
||||
cb, cbe)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _lv_create_mirror(uuid, vg_name, name, size_bytes,
|
||||
num_copies, create_options):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||
|
||||
if dbo:
|
||||
rc, out, err = cmdhandler.vg_lv_create_mirror(
|
||||
vg_name, create_options, name, size_bytes, num_copies)
|
||||
if rc == 0:
|
||||
created_lv = Vg.fetch_new_lv(vg_name, name)
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'VG with uuid %s and name %s not present!' %
|
||||
(uuid, vg_name))
|
||||
|
||||
return created_lv
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='stuia{sv}',
|
||||
out_signature='(oo)',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def LvCreateMirror(self, name, size_bytes, num_copies,
|
||||
tmo, create_options, cb, cbe):
|
||||
utils.validate_lv_name(VG_INTERFACE, self.Name, name)
|
||||
r = RequestEntry(
|
||||
tmo, Vg._lv_create_mirror,
|
||||
(self.state.Uuid, self.state.lvm_id, name,
|
||||
round_size(size_bytes), num_copies,
|
||||
create_options), cb, cbe)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _lv_create_raid(uuid, vg_name, name, raid_type, size_bytes,
|
||||
num_stripes, stripe_size_kb, create_options):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||
|
||||
if dbo:
|
||||
rc, out, err = cmdhandler.vg_lv_create_raid(
|
||||
vg_name, create_options, name, raid_type, size_bytes,
|
||||
num_stripes, stripe_size_kb)
|
||||
if rc == 0:
|
||||
created_lv = Vg.fetch_new_lv(vg_name, name)
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'VG with uuid %s and name %s not present!' %
|
||||
(uuid, vg_name))
|
||||
|
||||
return created_lv
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='sstuuia{sv}',
|
||||
out_signature='(oo)',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def LvCreateRaid(self, name, raid_type, size_bytes,
|
||||
num_stripes, stripe_size_kb, tmo,
|
||||
create_options, cb, cbe):
|
||||
utils.validate_lv_name(VG_INTERFACE, self.Name, name)
|
||||
r = RequestEntry(tmo, Vg._lv_create_raid,
|
||||
(self.state.Uuid, self.state.lvm_id, name,
|
||||
raid_type, round_size(size_bytes), num_stripes,
|
||||
stripe_size_kb, create_options), cb, cbe)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _create_pool(uuid, vg_name, meta_data_lv, data_lv,
|
||||
create_options, create_method):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||
|
||||
# Retrieve the full names for the metadata and data lv
|
||||
md = cfg.om.get_object_by_path(meta_data_lv)
|
||||
data = cfg.om.get_object_by_path(data_lv)
|
||||
|
||||
if dbo and md and data:
|
||||
|
||||
new_name = data.Name
|
||||
|
||||
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)
|
||||
|
||||
cache_pool_lv = Vg.fetch_new_lv(vg_name, new_name)
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
|
||||
else:
|
||||
msg = ""
|
||||
|
||||
if not dbo:
|
||||
msg += 'VG with uuid %s and name %s not present!' % \
|
||||
(uuid, vg_name)
|
||||
|
||||
if not md:
|
||||
msg += 'Meta data LV with object path %s not present!' % \
|
||||
(meta_data_lv)
|
||||
|
||||
if not data_lv:
|
||||
msg += 'Data LV with object path %s not present!' % \
|
||||
(meta_data_lv)
|
||||
|
||||
raise dbus.exceptions.DBusException(VG_INTERFACE, msg)
|
||||
|
||||
return cache_pool_lv
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='ooia{sv}',
|
||||
out_signature='(oo)',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def CreateCachePool(self, meta_data_lv, data_lv, tmo, create_options,
|
||||
cb, cbe):
|
||||
r = RequestEntry(
|
||||
tmo, Vg._create_pool,
|
||||
(self.state.Uuid, self.state.lvm_id, meta_data_lv,
|
||||
data_lv, create_options, cmdhandler.vg_create_cache_pool), cb, cbe)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='ooia{sv}',
|
||||
out_signature='(oo)',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def CreateThinPool(self, meta_data_lv, data_lv, tmo, create_options,
|
||||
cb, cbe):
|
||||
r = RequestEntry(
|
||||
tmo, Vg._create_pool,
|
||||
(self.state.Uuid, self.state.lvm_id, meta_data_lv,
|
||||
data_lv, create_options, cmdhandler.vg_create_thin_pool), cb, cbe)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _pv_add_rm_tags(uuid, vg_name, pv_object_paths, tags_add,
|
||||
tags_del, tag_options):
|
||||
pv_devices = []
|
||||
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||
|
||||
if dbo:
|
||||
# Check for existence of pv object paths
|
||||
for p in pv_object_paths:
|
||||
pv = cfg.om.get_object_by_path(p)
|
||||
if pv:
|
||||
pv_devices.append(pv.Name)
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE, 'PV object path = %s not found' % p)
|
||||
|
||||
rc, out, err = cmdhandler.pv_tag(
|
||||
pv_devices, tags_add, tags_del, tag_options)
|
||||
if rc == 0:
|
||||
cfg.load()
|
||||
return '/'
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'VG with uuid %s and name %s not present!' %
|
||||
(uuid, vg_name))
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='aoasia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def PvTagsAdd(self, pvs, tags, tmo, tag_options, cb, cbe):
|
||||
|
||||
for t in tags:
|
||||
utils.validate_tag(VG_INTERFACE, t)
|
||||
|
||||
r = RequestEntry(tmo, Vg._pv_add_rm_tags,
|
||||
(self.state.Uuid, self.state.lvm_id,
|
||||
pvs, tags, None, tag_options),
|
||||
cb, cbe, return_tuple=False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='aoasia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def PvTagsDel(self, pvs, tags, tmo, tag_options, cb, cbe):
|
||||
|
||||
for t in tags:
|
||||
utils.validate_tag(VG_INTERFACE, t)
|
||||
|
||||
r = RequestEntry(
|
||||
tmo, Vg._pv_add_rm_tags,
|
||||
(self.state.Uuid, self.state.lvm_id,
|
||||
pvs, None, tags, tag_options),
|
||||
cb, cbe, return_tuple=False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _vg_add_rm_tags(uuid, vg_name, tags_add, tags_del, tag_options):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||
|
||||
if dbo:
|
||||
|
||||
rc, out, err = cmdhandler.vg_tag(
|
||||
vg_name, tags_add, tags_del, tag_options)
|
||||
if rc == 0:
|
||||
dbo.refresh()
|
||||
return '/'
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'VG with uuid %s and name %s not present!' %
|
||||
(uuid, vg_name))
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='asia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def TagsAdd(self, tags, tmo, tag_options, cb, cbe):
|
||||
|
||||
for t in tags:
|
||||
utils.validate_tag(VG_INTERFACE, t)
|
||||
|
||||
r = RequestEntry(tmo, Vg._vg_add_rm_tags,
|
||||
(self.state.Uuid, self.state.lvm_id,
|
||||
tags, None, tag_options),
|
||||
cb, cbe, return_tuple=False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='asia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def TagsDel(self, tags, tmo, tag_options, cb, cbe):
|
||||
|
||||
for t in tags:
|
||||
utils.validate_tag(VG_INTERFACE, t)
|
||||
|
||||
r = RequestEntry(tmo, Vg._vg_add_rm_tags,
|
||||
(self.state.Uuid, self.state.lvm_id,
|
||||
None, tags, tag_options),
|
||||
cb, cbe, return_tuple=False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _vg_change_set(uuid, vg_name, method, value, options):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||
|
||||
if dbo:
|
||||
rc, out, err = method(vg_name, value, options)
|
||||
if rc == 0:
|
||||
dbo.refresh()
|
||||
return '/'
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'VG with uuid %s and name %s not present!' %
|
||||
(uuid, vg_name))
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='sia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def AllocationPolicySet(self, policy, tmo, policy_options, cb, cbe):
|
||||
r = RequestEntry(tmo, Vg._vg_change_set,
|
||||
(self.state.Uuid, self.state.lvm_id,
|
||||
cmdhandler.vg_allocation_policy,
|
||||
policy, policy_options),
|
||||
cb, cbe, return_tuple=False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='tia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def MaxPvSet(self, number, tmo, max_options, cb, cbe):
|
||||
r = RequestEntry(tmo, Vg._vg_change_set,
|
||||
(self.state.Uuid, self.state.lvm_id,
|
||||
cmdhandler.vg_max_pv, number, max_options),
|
||||
cb, cbe, return_tuple=False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='ia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def UuidGenerate(self, tmo, options, cb, cbe):
|
||||
r = RequestEntry(tmo, Vg._vg_change_set,
|
||||
(self.state.Uuid, self.state.lvm_id,
|
||||
cmdhandler.vg_uuid_gen, None, options),
|
||||
cb, cbe, return_tuple=False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
def _attribute(self, pos, ch):
|
||||
if self.state.attr[pos] == ch:
|
||||
return True
|
||||
return False
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='tia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def MaxLvSet(self, number, tmo, max_options, cb, cbe):
|
||||
r = RequestEntry(tmo, Vg._vg_change_set,
|
||||
(self.state.Uuid, self.state.lvm_id,
|
||||
cmdhandler.vg_max_lv, number, max_options),
|
||||
cb, cbe, return_tuple=False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@staticmethod
|
||||
def _vg_activate_deactivate(uuid, vg_name, activate, control_flags,
|
||||
options):
|
||||
# Make sure we have a dbus object representing it
|
||||
dbo = cfg.om.get_object_by_uuid_lvm_id(uuid, vg_name)
|
||||
|
||||
if dbo:
|
||||
rc, out, err = cmdhandler.activate_deactivate(
|
||||
'vgchange', vg_name, activate, control_flags, options)
|
||||
if rc == 0:
|
||||
cfg.load()
|
||||
return '/'
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'Exit code %s, stderr = %s' % (str(rc), err))
|
||||
else:
|
||||
raise dbus.exceptions.DBusException(
|
||||
VG_INTERFACE,
|
||||
'VG with uuid %s and name %s not present!' %
|
||||
(uuid, vg_name))
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='tia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Activate(self, control_flags, tmo, activate_options, cb, cbe):
|
||||
r = RequestEntry(tmo, Vg._vg_activate_deactivate,
|
||||
(self.state.Uuid, self.state.lvm_id, True,
|
||||
control_flags, activate_options),
|
||||
cb, cbe, return_tuple=False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@dbus.service.method(
|
||||
dbus_interface=VG_INTERFACE,
|
||||
in_signature='tia{sv}',
|
||||
out_signature='o',
|
||||
async_callbacks=('cb', 'cbe'))
|
||||
def Deactivate(self, control_flags, tmo, activate_options, cb, cbe):
|
||||
r = RequestEntry(tmo, Vg._vg_activate_deactivate,
|
||||
(self.state.Uuid, self.state.lvm_id, False,
|
||||
control_flags, activate_options),
|
||||
cb, cbe, return_tuple=False)
|
||||
cfg.worker_q.put(r)
|
||||
|
||||
@property
|
||||
def Tags(self):
|
||||
return utils.parse_tags(self.state.tags)
|
||||
|
||||
@property
|
||||
def Pvs(self):
|
||||
return self.state.Pvs
|
||||
|
||||
@property
|
||||
def Lvs(self):
|
||||
return self.state.Lvs
|
||||
|
||||
@property
|
||||
def lvm_id(self):
|
||||
return self.state.lvm_id
|
||||
|
||||
@property
|
||||
def Writeable(self):
|
||||
return self._attribute(0, 'w')
|
||||
|
||||
@property
|
||||
def Readable(self):
|
||||
return self._attribute(0, 'r')
|
||||
|
||||
@property
|
||||
def Resizeable(self):
|
||||
return self._attribute(1, 'z')
|
||||
|
||||
@property
|
||||
def Exportable(self):
|
||||
return self._attribute(2, 'x')
|
||||
|
||||
@property
|
||||
def Partial(self):
|
||||
return self._attribute(3, 'p')
|
||||
|
||||
@property
|
||||
def AllocContiguous(self):
|
||||
return self._attribute(4, 'c')
|
||||
|
||||
@property
|
||||
def AllocCling(self):
|
||||
return self._attribute(4, 'l')
|
||||
|
||||
@property
|
||||
def AllocNormal(self):
|
||||
return self._attribute(4, 'n')
|
||||
|
||||
@property
|
||||
def AllocAnywhere(self):
|
||||
return self._attribute(4, 'a')
|
||||
|
||||
@property
|
||||
def Clustered(self):
|
||||
return self._attribute(5, 'c')
|
||||
@@ -37,11 +37,12 @@ int main(int argc, char **argv)
|
||||
printf("lvmetactl dump\n");
|
||||
printf("lvmetactl pv_list\n");
|
||||
printf("lvmetactl vg_list\n");
|
||||
printf("lvmetactl get_global_info\n");
|
||||
printf("lvmetactl vg_lookup_name <name>\n");
|
||||
printf("lvmetactl vg_lookup_uuid <uuid>\n");
|
||||
printf("lvmetactl pv_lookup_uuid <uuid>\n");
|
||||
printf("lvmetactl set_global_invalid 0|1\n");
|
||||
printf("lvmetactl get_global_invalid\n");
|
||||
printf("lvmetactl set_global_disable 0|1\n");
|
||||
printf("lvmetactl set_vg_version <uuid> <name> <version>\n");
|
||||
printf("lvmetactl vg_lock_type <uuid>\n");
|
||||
return -1;
|
||||
@@ -54,18 +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);
|
||||
|
||||
@@ -79,14 +94,26 @@ 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);
|
||||
|
||||
} else if (!strcmp(cmd, "get_global_invalid")) {
|
||||
reply = daemon_send_simple(h, "get_global_info",
|
||||
} else if (!strcmp(cmd, "set_global_disable")) {
|
||||
if (argc < 3) {
|
||||
printf("set_global_disable 0|1\n");
|
||||
return -1;
|
||||
}
|
||||
val = atoi(argv[2]);
|
||||
|
||||
reply = daemon_send_simple(h, "set_global_info",
|
||||
"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);
|
||||
printf("%s\n", reply.buffer.mem);
|
||||
print_reply(reply);
|
||||
|
||||
} else if (!strcmp(cmd, "set_vg_version")) {
|
||||
if (argc < 5) {
|
||||
@@ -108,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");
|
||||
@@ -138,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);
|
||||
|
||||
@@ -151,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);
|
||||
|
||||
@@ -167,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); */
|
||||
|
||||
@@ -193,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,6 +19,13 @@
|
||||
|
||||
#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;
|
||||
|
||||
/* Different types of replies we may get from lvmetad. */
|
||||
|
||||
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);
|
||||
|
||||
@@ -306,7 +306,13 @@ static const char *_syslog_num_to_name(int num)
|
||||
static uint64_t monotime(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
|
||||
log_error("clock_gettime failed to get timestamp %s.",
|
||||
strerror(errno));
|
||||
ts.tv_sec = 0;
|
||||
}
|
||||
|
||||
return ts.tv_sec;
|
||||
}
|
||||
|
||||
@@ -317,7 +323,7 @@ static void log_save_line(int len, char *line,
|
||||
unsigned int w = *wrap;
|
||||
int i;
|
||||
|
||||
if (len < LOG_DUMP_SIZE - p) {
|
||||
if (len < (int) (LOG_DUMP_SIZE - p)) {
|
||||
memcpy(log_buf + p, line, len);
|
||||
p += len;
|
||||
|
||||
@@ -1007,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);
|
||||
|
||||
@@ -1016,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;
|
||||
}
|
||||
@@ -1234,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
|
||||
@@ -1253,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;
|
||||
|
||||
@@ -1277,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",
|
||||
@@ -2269,6 +2280,7 @@ static void *lockspace_thread_main(void *arg_in)
|
||||
struct action *act_op_free = NULL;
|
||||
struct list_head tmp_act;
|
||||
struct list_head act_close;
|
||||
char tmp_name[MAX_NAME+1];
|
||||
int free_vg = 0;
|
||||
int drop_vg = 0;
|
||||
int error = 0;
|
||||
@@ -2634,6 +2646,10 @@ out_act:
|
||||
ls->drop_vg = drop_vg;
|
||||
if (ls->lm_type == LD_LM_DLM && !strcmp(ls->name, gl_lsname_dlm))
|
||||
global_dlm_lockspace_exists = 0;
|
||||
/* Avoid a name collision of the same lockspace is added again before this thread is cleaned up. */
|
||||
memset(tmp_name, 0, sizeof(tmp_name));
|
||||
snprintf(tmp_name, MAX_NAME, "REM:%s", ls->name);
|
||||
memcpy(ls->name, tmp_name, MAX_NAME);
|
||||
pthread_mutex_unlock(&lockspaces_mutex);
|
||||
|
||||
/* worker_thread will join this thread, and free the ls */
|
||||
@@ -3113,6 +3129,8 @@ static int for_each_lockspace(int do_stop, int do_free, int do_force)
|
||||
|
||||
/* FIXME: will free_vg ever not be set? */
|
||||
|
||||
log_debug("free ls %s", ls->name);
|
||||
|
||||
if (ls->free_vg) {
|
||||
/* In future we may need to free ls->actions here */
|
||||
free_ls_resources(ls);
|
||||
@@ -3333,7 +3351,10 @@ static void *worker_thread_main(void *arg_in)
|
||||
|
||||
while (1) {
|
||||
pthread_mutex_lock(&worker_mutex);
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
if (clock_gettime(CLOCK_REALTIME, &ts)) {
|
||||
log_error("clock_gettime failed.");
|
||||
ts.tv_sec = ts.tv_nsec = 0;
|
||||
}
|
||||
ts.tv_sec += delay_sec;
|
||||
rv = 0;
|
||||
act = NULL;
|
||||
@@ -3365,6 +3386,11 @@ static void *worker_thread_main(void *arg_in)
|
||||
int run_sanlock = lm_is_running_sanlock();
|
||||
int run_dlm = lm_is_running_dlm();
|
||||
|
||||
if (daemon_test) {
|
||||
run_sanlock = gl_use_sanlock;
|
||||
run_dlm = gl_use_dlm;
|
||||
}
|
||||
|
||||
if (run_sanlock && run_dlm)
|
||||
act->result = -EXFULL;
|
||||
else if (!run_sanlock && !run_dlm)
|
||||
@@ -5153,13 +5179,17 @@ static void adopt_locks(void)
|
||||
* This is expected for at least one of them.
|
||||
*/
|
||||
|
||||
rv = lm_get_lockspaces_dlm(&ls_found);
|
||||
if ((rv < 0) && (rv != -ECONNREFUSED))
|
||||
goto fail;
|
||||
if (lm_support_dlm()) {
|
||||
rv = lm_get_lockspaces_dlm(&ls_found);
|
||||
if ((rv < 0) && (rv != -ECONNREFUSED))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rv = lm_get_lockspaces_sanlock(&ls_found);
|
||||
if ((rv < 0) && (rv != -ECONNREFUSED))
|
||||
goto fail;
|
||||
if (lm_support_sanlock()) {
|
||||
rv = lm_get_lockspaces_sanlock(&ls_found);
|
||||
if ((rv < 0) && (rv != -ECONNREFUSED))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (list_empty(&ls_found)) {
|
||||
log_debug("No lockspaces found to adopt");
|
||||
|
||||
@@ -206,7 +206,7 @@ int lm_data_size_sanlock(void)
|
||||
#define VG_LOCK_BEGIN UINT64_C(66)
|
||||
#define LV_LOCK_BEGIN UINT64_C(67)
|
||||
|
||||
static unsigned int daemon_test_lv_count;
|
||||
static uint64_t daemon_test_lv_count;
|
||||
|
||||
static int lock_lv_name_from_args(char *vg_args, char *lock_lv_name)
|
||||
{
|
||||
@@ -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);
|
||||
@@ -1416,14 +1416,12 @@ int lm_lock_sanlock(struct lockspace *ls, struct resource *r, int ld_mode,
|
||||
if (adopt)
|
||||
flags |= SANLK_ACQUIRE_ORPHAN_ONLY;
|
||||
|
||||
#ifdef SANLOCK_HAS_ACQUIRE_OWNER_NOWAIT
|
||||
/*
|
||||
* Don't block waiting for a failed lease to expire since it causes
|
||||
* sanlock_acquire to block for a long time, which would prevent this
|
||||
* thread from processing other lock requests.
|
||||
*/
|
||||
flags |= SANLK_ACQUIRE_OWNER_NOWAIT;
|
||||
#endif
|
||||
|
||||
memset(&opt, 0, sizeof(opt));
|
||||
sprintf(opt.owner_name, "%s", "lvmlockd");
|
||||
@@ -1498,7 +1496,6 @@ int lm_lock_sanlock(struct lockspace *ls, struct resource *r, int ld_mode,
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
#ifdef SANLOCK_HAS_ACQUIRE_OWNER_NOWAIT
|
||||
if (rv == SANLK_ACQUIRE_OWNED_RETRY) {
|
||||
/*
|
||||
* The lock is held by a failed host, and will eventually
|
||||
@@ -1517,7 +1514,7 @@ int lm_lock_sanlock(struct lockspace *ls, struct resource *r, int ld_mode,
|
||||
*retry = 0;
|
||||
return -EAGAIN;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (rv < 0) {
|
||||
log_error("S %s R %s lock_san acquire error %d",
|
||||
ls->name, r->name, rv);
|
||||
|
||||
@@ -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
|
||||
---------
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
@top_srcdir@/lib/misc/util.h
|
||||
@top_srcdir@/lib/mm/memlock.h
|
||||
@top_srcdir@/lib/mm/xlate.h
|
||||
@top_srcdir@/lib/notify/lvmnotify.h
|
||||
@top_srcdir@/lib/properties/prop_common.h
|
||||
@top_srcdir@/lib/report/properties.h
|
||||
@top_srcdir@/lib/report/report.h
|
||||
|
||||
@@ -631,6 +631,9 @@
|
||||
/* The path to 'modprobe', if available. */
|
||||
#undef MODPROBE_CMD
|
||||
|
||||
/* Define to 1 to include code that uses dbus notification. */
|
||||
#undef NOTIFYDBUS_SUPPORT
|
||||
|
||||
/* Define to 1 to enable O_DIRECT support. */
|
||||
#undef O_DIRECT_SUPPORT
|
||||
|
||||
|
||||
@@ -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 \
|
||||
@@ -116,6 +117,7 @@ SOURCES =\
|
||||
misc/lvm-wrappers.c \
|
||||
misc/lvm-percent.c \
|
||||
mm/memlock.c \
|
||||
notify/lvmnotify.c \
|
||||
properties/prop_common.c \
|
||||
report/properties.c \
|
||||
report/report.c \
|
||||
@@ -215,6 +217,7 @@ ifeq ($(MAKECMDGOALS),distclean)
|
||||
format_pool \
|
||||
snapshot \
|
||||
mirror \
|
||||
notify \
|
||||
raid \
|
||||
replicator \
|
||||
thin \
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
@@ -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)
|
||||
{
|
||||
@@ -603,7 +592,24 @@ int module_present(struct cmd_context *cmd, const char *target_name)
|
||||
#ifdef MODPROBE_CMD
|
||||
char module[128];
|
||||
const char *argv[] = { MODPROBE_CMD, module, NULL };
|
||||
#endif
|
||||
struct stat st;
|
||||
char path[PATH_MAX];
|
||||
int i = dm_snprintf(path, (sizeof(path) - 1), "%smodule/dm_%s",
|
||||
dm_sysfs_dir(), target_name);
|
||||
|
||||
if (i > 0) {
|
||||
while (path[--i] != '/') /* stop on dm_ */
|
||||
if (path[i] == '-')
|
||||
path[i] = '_'; /* replace '-' with '_' */
|
||||
|
||||
if ((lstat(path, &st) == 0) && S_ISDIR(st.st_mode)) {
|
||||
log_debug_activation("Module directory %s exists.", path);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MODPROBE_CMD
|
||||
if (dm_snprintf(module, sizeof(module), "dm-%s", target_name) < 0) {
|
||||
log_error("module_present module name too long: %s",
|
||||
target_name);
|
||||
@@ -615,25 +621,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,
|
||||
@@ -660,16 +674,44 @@ static int _lv_info(struct cmd_context *cmd, const struct logical_volume *lv,
|
||||
/* New thin-pool has no layer, but -tpool suffix needs to be queried */
|
||||
if (!use_layer && lv_is_new_thin_pool(lv)) {
|
||||
/* Check if there isn't existing old thin pool mapping in the table */
|
||||
if (!dev_manager_info(cmd->mem, lv, NULL, 0, 0, &dminfo, NULL, NULL))
|
||||
if (!dev_manager_info(cmd, lv, NULL, 0, 0, &dminfo, NULL, NULL))
|
||||
return_0;
|
||||
if (!dminfo.exists)
|
||||
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->mem, lv,
|
||||
if (!dev_manager_info(cmd, lv,
|
||||
(use_layer) ? lv_layer(lv) : NULL,
|
||||
with_open_count, with_read_ahead,
|
||||
&dminfo, (info) ? &info->read_ahead : NULL,
|
||||
@@ -763,7 +805,8 @@ int lv_info_with_seg_status(struct cmd_context *cmd, const struct logical_volume
|
||||
#define OPEN_COUNT_CHECK_RETRIES 25
|
||||
#define OPEN_COUNT_CHECK_USLEEP_DELAY 200000
|
||||
|
||||
int lv_check_not_in_use(const struct logical_volume *lv)
|
||||
/* Only report error if error_if_used is set */
|
||||
int lv_check_not_in_use(const struct logical_volume *lv, int error_if_used)
|
||||
{
|
||||
struct lvinfo info;
|
||||
unsigned int open_count_check_retries;
|
||||
@@ -774,14 +817,22 @@ int lv_check_not_in_use(const struct logical_volume *lv)
|
||||
/* If sysfs is not used, use open_count information only. */
|
||||
if (dm_sysfs_dir()) {
|
||||
if (dm_device_has_holders(info.major, info.minor)) {
|
||||
log_error("Logical volume %s is used by another device.",
|
||||
display_lvname(lv));
|
||||
if (error_if_used)
|
||||
log_error("Logical volume %s is used by another device.",
|
||||
display_lvname(lv));
|
||||
else
|
||||
log_debug_activation("Logical volume %s is used by another device.",
|
||||
display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dm_device_has_mounted_fs(info.major, info.minor)) {
|
||||
log_error("Logical volume %s contains a filesystem in use.",
|
||||
display_lvname(lv));
|
||||
if (error_if_used)
|
||||
log_error("Logical volume %s contains a filesystem in use.",
|
||||
display_lvname(lv));
|
||||
else
|
||||
log_debug_activation("Logical volume %s contains a filesystem in use.",
|
||||
display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -789,8 +840,10 @@ int lv_check_not_in_use(const struct logical_volume *lv)
|
||||
open_count_check_retries = retry_deactivation() ? OPEN_COUNT_CHECK_RETRIES : 1;
|
||||
while (info.open_count > 0 && open_count_check_retries--) {
|
||||
if (!open_count_check_retries) {
|
||||
log_error("Logical volume %s in use.",
|
||||
display_lvname(lv));
|
||||
if (error_if_used)
|
||||
log_error("Logical volume %s in use.", display_lvname(lv));
|
||||
else
|
||||
log_debug_activation("Logical volume %s in use.", display_lvname(lv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1067,19 +1120,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)))
|
||||
@@ -1160,13 +1223,13 @@ int lv_thin_pool_transaction_id(const struct logical_volume *lv,
|
||||
if (!lv_info(lv->vg->cmd, lv, 1, NULL, 0, 0))
|
||||
return 0;
|
||||
|
||||
log_debug_activation("Checking thin percent for LV %s.",
|
||||
log_debug_activation("Checking thin-pool transaction id for LV %s.",
|
||||
display_lvname(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;
|
||||
@@ -1203,8 +1266,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;
|
||||
@@ -1329,7 +1394,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;
|
||||
}
|
||||
@@ -1391,9 +1456,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;
|
||||
|
||||
@@ -1827,7 +1892,7 @@ static int _lv_suspend(struct cmd_context *cmd, const char *lvid_s,
|
||||
const struct logical_volume *lv_pre_to_free = NULL;
|
||||
struct logical_volume *lv_pre_tmp;
|
||||
struct seg_list *sl;
|
||||
struct lv_segment *snap_seg;
|
||||
struct lv_segment *snap_seg;
|
||||
struct lvinfo info;
|
||||
int r = 0, lockfs = 0, flush_required = 0;
|
||||
struct detached_lv_data detached;
|
||||
@@ -1933,6 +1998,16 @@ static int _lv_suspend(struct cmd_context *cmd, const char *lvid_s,
|
||||
}
|
||||
}
|
||||
|
||||
/* Flush is ATM required for the tested cases
|
||||
* NOTE: Mirror repair requires noflush for proper repair!
|
||||
* TODO: Relax this limiting condition further */
|
||||
if (!flush_required &&
|
||||
(lv_is_pvmove(lv) ||
|
||||
(!lv_is_mirror(lv) && !lv_is_thin_pool(lv) && !lv_is_thin_volume(lv)))) {
|
||||
log_debug("Requiring flush for LV %s.", display_lvname(lv));
|
||||
flush_required = 1;
|
||||
}
|
||||
|
||||
if (!monitor_dev_for_events(cmd, lv, laopts, 0))
|
||||
/* FIXME Consider aborting here */
|
||||
stack;
|
||||
@@ -2053,6 +2128,7 @@ static int _lv_resume(struct cmd_context *cmd, const char *lvid_s,
|
||||
}
|
||||
|
||||
laopts->read_only = _passes_readonly_filter(cmd, lv);
|
||||
laopts->resuming = 1;
|
||||
|
||||
if (!_lv_activate_lv(lv, laopts))
|
||||
goto_out;
|
||||
@@ -2107,7 +2183,7 @@ static int _lv_has_open_snapshots(const struct logical_volume *lv)
|
||||
int r = 0;
|
||||
|
||||
dm_list_iterate_items_gen(snap_seg, &lv->snapshot_segs, origin_list)
|
||||
if (!lv_check_not_in_use(snap_seg->cow))
|
||||
if (!lv_check_not_in_use(snap_seg->cow, 1))
|
||||
r++;
|
||||
|
||||
if (r)
|
||||
@@ -2161,7 +2237,7 @@ int lv_deactivate(struct cmd_context *cmd, const char *lvid_s, const struct logi
|
||||
|
||||
if (lv_is_visible(lv) || lv_is_virtual_origin(lv) ||
|
||||
lv_is_merging_thin_snapshot(lv)) {
|
||||
if (!lv_check_not_in_use(lv))
|
||||
if (!lv_check_not_in_use(lv, 1))
|
||||
goto_out;
|
||||
|
||||
if (lv_is_origin(lv) && _lv_has_open_snapshots(lv))
|
||||
@@ -2243,7 +2319,7 @@ static int _lv_activate(struct cmd_context *cmd, const char *lvid_s,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((!lv->vg->cmd->partial_activation) && (lv->status & PARTIAL_LV)) {
|
||||
if ((!lv->vg->cmd->partial_activation) && lv_is_partial(lv)) {
|
||||
if (!lv_is_raid_type(lv) || !partial_raid_lv_supports_degraded_activation(lv)) {
|
||||
log_error("Refusing activation of partial LV %s. "
|
||||
"Use '--activationmode partial' to override.",
|
||||
@@ -2299,7 +2375,7 @@ static int _lv_activate(struct cmd_context *cmd, const char *lvid_s,
|
||||
if (info.exists && !info.suspended && info.live_table &&
|
||||
(info.read_only == read_only_lv(lv, laopts))) {
|
||||
r = 1;
|
||||
log_debug_activation("Volume is already active.");
|
||||
log_debug_activation("LV %s is already active.", display_lvname(lv));
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ struct lv_activate_opts {
|
||||
* set of flags to avoid any scanning in udev. These udev
|
||||
* flags are persistent in udev db for any spurious event
|
||||
* that follows. */
|
||||
unsigned resuming; /* Set when resuming after a suspend. */
|
||||
};
|
||||
|
||||
void set_activation(int activation, int silent);
|
||||
@@ -91,6 +92,9 @@ 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,
|
||||
@@ -146,7 +150,7 @@ int lv_info_with_seg_status(struct cmd_context *cmd, const struct logical_volume
|
||||
struct lv_with_info_and_seg_status *status,
|
||||
int with_open_count, int with_read_ahead);
|
||||
|
||||
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);
|
||||
|
||||
/*
|
||||
* Returns 1 if activate_lv has been set: 1 = activate; 0 = don't.
|
||||
@@ -239,4 +243,28 @@ int device_is_usable(struct device *dev, struct dev_usable_check_params check);
|
||||
*/
|
||||
void fs_unlock(void);
|
||||
|
||||
#define TARGET_NAME_CACHE "cache"
|
||||
#define TARGET_NAME_ERROR "error"
|
||||
#define TARGET_NAME_ERROR_OLD "erro" /* Truncated in older kernels */
|
||||
#define TARGET_NAME_LINEAR "linear"
|
||||
#define TARGET_NAME_MIRROR "mirror"
|
||||
#define TARGET_NAME_RAID "raid"
|
||||
#define TARGET_NAME_SNAPSHOT "snapshot"
|
||||
#define TARGET_NAME_SNAPSHOT_MERGE "snapshot-merge"
|
||||
#define TARGET_NAME_SNAPSHOT_ORIGIN "snapshot-origin"
|
||||
#define TARGET_NAME_STRIPED "striped"
|
||||
#define TARGET_NAME_THIN "thin"
|
||||
#define TARGET_NAME_THIN_POOL "thin-pool"
|
||||
#define TARGET_NAME_ZERO "zero"
|
||||
|
||||
#define MODULE_NAME_CLUSTERED_MIRROR "clog"
|
||||
#define MODULE_NAME_CACHE TARGET_NAME_CACHE
|
||||
#define MODULE_NAME_ERROR TARGET_NAME_ERROR
|
||||
#define MODULE_NAME_LOG_CLUSTERED "log-clustered"
|
||||
#define MODULE_NAME_LOG_USERSPACE "log-userspace"
|
||||
#define MODULE_NAME_MIRROR TARGET_NAME_MIRROR
|
||||
#define MODULE_NAME_SNAPSHOT TARGET_NAME_SNAPSHOT
|
||||
#define MODULE_NAME_RAID TARGET_NAME_RAID
|
||||
#define MODULE_NAME_ZERO TARGET_NAME_ZERO
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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,17 +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, "cache") && strcmp(target_name, "thin-pool"))
|
||||
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)) {
|
||||
@@ -190,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:
|
||||
@@ -197,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;
|
||||
@@ -207,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))
|
||||
@@ -314,7 +331,7 @@ static int _ignore_blocked_mirror_devices(struct device *dev,
|
||||
if (!(tmp_dev = dev_create_file(buf, NULL, NULL, 0)))
|
||||
goto_out;
|
||||
|
||||
tmp_dev->dev = MKDEV((dev_t)sm->logs[0].major, sm->logs[0].minor);
|
||||
tmp_dev->dev = MKDEV((dev_t)sm->logs[0].major, (dev_t)sm->logs[0].minor);
|
||||
if (device_is_usable(tmp_dev, (struct dev_usable_check_params)
|
||||
{ .check_empty = 1,
|
||||
.check_blocked = 1,
|
||||
@@ -335,13 +352,8 @@ static int _ignore_blocked_mirror_devices(struct device *dev,
|
||||
* We avoid another system call if we can, but if a device is
|
||||
* dead, we have no choice but to look up the table too.
|
||||
*/
|
||||
if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
|
||||
goto_out;
|
||||
|
||||
if (!dm_task_set_major_minor(dmt, MAJOR(dev->dev), MINOR(dev->dev), 1))
|
||||
goto_out;
|
||||
|
||||
if (activation_checks() && !dm_task_enable_checks(dmt))
|
||||
if (!(dmt = _setup_task(NULL, NULL, NULL, DM_DEVICE_TABLE,
|
||||
MAJOR(dev->dev), MINOR(dev->dev), 0, 1)))
|
||||
goto_out;
|
||||
|
||||
if (!dm_task_run(dmt))
|
||||
@@ -350,8 +362,9 @@ static int _ignore_blocked_mirror_devices(struct device *dev,
|
||||
do {
|
||||
next = dm_get_next_target(dmt, next, &s, &l,
|
||||
&target_type, ¶ms);
|
||||
if ((s == start) && (l == length)) {
|
||||
if (strcmp(target_type, "mirror"))
|
||||
if ((s == start) && (l == length) &&
|
||||
target_type && params) {
|
||||
if (strcmp(target_type, TARGET_NAME_MIRROR))
|
||||
goto_out;
|
||||
|
||||
if (((p = strstr(params, " block_on_error")) &&
|
||||
@@ -381,14 +394,9 @@ static int _device_is_suspended(int major, int minor)
|
||||
struct dm_info info;
|
||||
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 (activation_checks() && !dm_task_enable_checks(dmt))
|
||||
goto_out;
|
||||
if (!(dmt = _setup_task(NULL, NULL, NULL, DM_DEVICE_INFO,
|
||||
major, minor, 0, 0)))
|
||||
return_0;
|
||||
|
||||
if (!dm_task_run(dmt) ||
|
||||
!dm_task_get_info(dmt, &info)) {
|
||||
@@ -411,15 +419,10 @@ static int _ignore_suspended_snapshot_component(struct device *dev)
|
||||
int major1, minor1, major2, minor2;
|
||||
int r = 0;
|
||||
|
||||
if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
|
||||
if (!(dmt = _setup_task(NULL, NULL, NULL, DM_DEVICE_TABLE,
|
||||
MAJOR(dev->dev), MINOR(dev->dev), 0, 1)))
|
||||
return_0;
|
||||
|
||||
if (!dm_task_set_major_minor(dmt, MAJOR(dev->dev), MINOR(dev->dev), 1))
|
||||
goto_out;
|
||||
|
||||
if (activation_checks() && !dm_task_enable_checks(dmt))
|
||||
goto_out;
|
||||
|
||||
if (!dm_task_run(dmt)) {
|
||||
log_error("Failed to get state of snapshot or snapshot origin device");
|
||||
goto out;
|
||||
@@ -427,13 +430,13 @@ static int _ignore_suspended_snapshot_component(struct device *dev)
|
||||
|
||||
do {
|
||||
next = dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
|
||||
if (!target_type || !strcmp(target_type, "snapshot")) {
|
||||
if (!target_type || !strcmp(target_type, TARGET_NAME_SNAPSHOT)) {
|
||||
if (!params || sscanf(params, "%d:%d %d:%d", &major1, &minor1, &major2, &minor2) != 4) {
|
||||
log_error("Incorrect snapshot table found");
|
||||
goto_out;
|
||||
}
|
||||
r = r || _device_is_suspended(major1, minor1) || _device_is_suspended(major2, minor2);
|
||||
} else if (!strcmp(target_type, "snapshot-origin")) {
|
||||
} else if (!strcmp(target_type, TARGET_NAME_SNAPSHOT_ORIGIN)) {
|
||||
if (!params || sscanf(params, "%d:%d", &major1, &minor1) != 2) {
|
||||
log_error("Incorrect snapshot-origin table found");
|
||||
goto_out;
|
||||
@@ -463,32 +466,26 @@ static int _ignore_unusable_thins(struct device *dev)
|
||||
if (!(mem = dm_pool_create("unusable_thins", 128)))
|
||||
return_0;
|
||||
|
||||
if (!(dmt = dm_task_create(DM_DEVICE_TABLE)))
|
||||
goto_out;
|
||||
if (!dm_task_no_open_count(dmt))
|
||||
goto_out;
|
||||
if (!dm_task_set_major_minor(dmt, MAJOR(dev->dev), MINOR(dev->dev), 1))
|
||||
if (!(dmt = _setup_task(NULL, NULL, NULL, DM_DEVICE_TABLE,
|
||||
MAJOR(dev->dev), MINOR(dev->dev), 0, 1)))
|
||||
goto_out;
|
||||
|
||||
if (!dm_task_run(dmt)) {
|
||||
log_error("Failed to get state of mapped device.");
|
||||
goto out;
|
||||
}
|
||||
dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
|
||||
if (!params || sscanf(params, "%d:%d", &minor, &major) != 2) {
|
||||
if (!params || sscanf(params, "%d:%d", &major, &minor) != 2) {
|
||||
log_error("Failed to get thin-pool major:minor for thin device %d:%d.",
|
||||
(int)MAJOR(dev->dev), (int)MINOR(dev->dev));
|
||||
goto out;
|
||||
}
|
||||
dm_task_destroy(dmt);
|
||||
|
||||
if (!(dmt = dm_task_create(DM_DEVICE_STATUS)))
|
||||
goto_out;
|
||||
if (!dm_task_no_flush(dmt))
|
||||
log_warn("Can't set no_flush.");
|
||||
if (!dm_task_no_open_count(dmt))
|
||||
goto_out;
|
||||
if (!dm_task_set_major_minor(dmt, minor, major, 1))
|
||||
if (!(dmt = _setup_task(NULL, NULL, NULL, DM_DEVICE_STATUS,
|
||||
major, minor, 0, 0)))
|
||||
goto_out;
|
||||
|
||||
if (!dm_task_run(dmt)) {
|
||||
log_error("Failed to get state of mapped device.");
|
||||
goto out;
|
||||
@@ -542,15 +539,10 @@ int device_is_usable(struct device *dev, struct dev_usable_check_params check)
|
||||
int only_error_target = 1;
|
||||
int r = 0;
|
||||
|
||||
if (!(dmt = dm_task_create(DM_DEVICE_STATUS)))
|
||||
if (!(dmt = _setup_task(NULL, NULL, NULL, DM_DEVICE_STATUS,
|
||||
MAJOR(dev->dev), MINOR(dev->dev), 0, 0)))
|
||||
return_0;
|
||||
|
||||
if (!dm_task_set_major_minor(dmt, MAJOR(dev->dev), MINOR(dev->dev), 1))
|
||||
goto_out;
|
||||
|
||||
if (activation_checks() && !dm_task_enable_checks(dmt))
|
||||
goto_out;
|
||||
|
||||
if (!dm_task_run(dmt)) {
|
||||
log_error("Failed to get state of mapped device");
|
||||
goto out;
|
||||
@@ -601,7 +593,7 @@ int device_is_usable(struct device *dev, struct dev_usable_check_params check)
|
||||
next = dm_get_next_target(dmt, next, &start, &length,
|
||||
&target_type, ¶ms);
|
||||
|
||||
if (check.check_blocked && target_type && !strcmp(target_type, "mirror")) {
|
||||
if (check.check_blocked && target_type && !strcmp(target_type, TARGET_NAME_MIRROR)) {
|
||||
if (ignore_lvm_mirrors()) {
|
||||
log_debug_activation("%s: Scanning mirror devices is disabled.", dev_name(dev));
|
||||
goto out;
|
||||
@@ -636,20 +628,20 @@ int device_is_usable(struct device *dev, struct dev_usable_check_params check)
|
||||
* in a stack - use proper dm tree to check this instead.
|
||||
*/
|
||||
if (check.check_suspended && target_type &&
|
||||
(!strcmp(target_type, "snapshot") || !strcmp(target_type, "snapshot-origin")) &&
|
||||
(!strcmp(target_type, TARGET_NAME_SNAPSHOT) || !strcmp(target_type, TARGET_NAME_SNAPSHOT_ORIGIN)) &&
|
||||
_ignore_suspended_snapshot_component(dev)) {
|
||||
log_debug_activation("%s: %s device %s not usable.", dev_name(dev), target_type, name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* TODO: extend check struct ? */
|
||||
if (target_type && !strcmp(target_type, "thin") &&
|
||||
if (target_type && !strcmp(target_type, TARGET_NAME_THIN) &&
|
||||
!_ignore_unusable_thins(dev)) {
|
||||
log_debug_activation("%s: %s device %s not usable.", dev_name(dev), target_type, name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (target_type && strcmp(target_type, "error"))
|
||||
if (target_type && strcmp(target_type, TARGET_NAME_ERROR))
|
||||
only_error_target = 0;
|
||||
} while (next);
|
||||
|
||||
@@ -671,7 +663,32 @@ int device_is_usable(struct device *dev, struct dev_usable_check_params check)
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _info(const char *dlid, int with_open_count, int with_read_ahead,
|
||||
/*
|
||||
* If active LVs were activated by a version of LVM2 before 2.02.00 we must
|
||||
* perform additional checks to find them because they do not have the LVM-
|
||||
* prefix on their dm uuids.
|
||||
* As of 2.02.150, we've chosen to disable this compatibility arbitrarily if
|
||||
* we're running kernel version 3 or above.
|
||||
*/
|
||||
#define MIN_KERNEL_MAJOR 3
|
||||
|
||||
static int _original_uuid_format_check_required(struct cmd_context *cmd)
|
||||
{
|
||||
static int _kernel_major = 0;
|
||||
|
||||
if (!_kernel_major) {
|
||||
if ((sscanf(cmd->kernel_vsn, "%d", &_kernel_major) == 1) &&
|
||||
(_kernel_major >= MIN_KERNEL_MAJOR))
|
||||
log_debug_activation("Skipping checks for old devices without " UUID_PREFIX
|
||||
" dm uuid prefix (kernel vsn %d >= %d).", _kernel_major, MIN_KERNEL_MAJOR);
|
||||
else
|
||||
_kernel_major = -1;
|
||||
}
|
||||
|
||||
return (_kernel_major == -1);
|
||||
}
|
||||
|
||||
static int _info(struct cmd_context *cmd, const char *dlid, int with_open_count, int with_read_ahead,
|
||||
struct dm_info *dminfo, uint32_t *read_ahead,
|
||||
struct lv_seg_status *seg_status)
|
||||
{
|
||||
@@ -700,6 +717,10 @@ static int _info(const char *dlid, int with_open_count, int with_read_ahead,
|
||||
}
|
||||
}
|
||||
|
||||
/* Must we still check for the pre-2.02.00 dm uuid format? */
|
||||
if (!_original_uuid_format_check_required(cmd))
|
||||
return r;
|
||||
|
||||
/* Check for dlid before UUID_PREFIX was added */
|
||||
if ((r = _info_run(seg_status ? STATUS : INFO, NULL, dlid + sizeof(UUID_PREFIX) - 1,
|
||||
dminfo, read_ahead, seg_status, with_open_count,
|
||||
@@ -714,7 +735,7 @@ static int _info_by_dev(uint32_t major, uint32_t minor, struct dm_info *info)
|
||||
return _info_run(INFO, NULL, NULL, info, NULL, 0, 0, 0, major, minor);
|
||||
}
|
||||
|
||||
int dev_manager_info(struct dm_pool *mem, const struct logical_volume *lv,
|
||||
int dev_manager_info(struct cmd_context *cmd, const struct logical_volume *lv,
|
||||
const char *layer,
|
||||
int with_open_count, int with_read_ahead,
|
||||
struct dm_info *dminfo, uint32_t *read_ahead,
|
||||
@@ -723,19 +744,19 @@ int dev_manager_info(struct dm_pool *mem, const struct logical_volume *lv,
|
||||
char *dlid, *name;
|
||||
int r;
|
||||
|
||||
if (!(name = dm_build_dm_name(mem, lv->vg->name, lv->name, layer)))
|
||||
if (!(name = dm_build_dm_name(cmd->mem, lv->vg->name, lv->name, layer)))
|
||||
return_0;
|
||||
|
||||
if (!(dlid = build_dm_uuid(mem, lv, layer))) {
|
||||
if (!(dlid = build_dm_uuid(cmd->mem, lv, layer))) {
|
||||
r = 0;
|
||||
goto_out;
|
||||
}
|
||||
|
||||
log_debug_activation("Getting device info for %s [%s]", name, dlid);
|
||||
r = _info(dlid, with_open_count, with_read_ahead,
|
||||
r = _info(cmd, dlid, with_open_count, with_read_ahead,
|
||||
dminfo, read_ahead, seg_status);
|
||||
out:
|
||||
dm_pool_free(mem, name);
|
||||
dm_pool_free(cmd->mem, name);
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -753,11 +774,11 @@ static const struct dm_info *_cached_dm_info(struct dm_pool *mem,
|
||||
return_NULL;
|
||||
|
||||
if (!(dnode = dm_tree_find_node_by_uuid(dtree, dlid)))
|
||||
goto_out;
|
||||
goto out;
|
||||
|
||||
if (!(dinfo = dm_tree_node_get_info(dnode))) {
|
||||
log_error("Failed to get info from tree node for %s.",
|
||||
display_lvname(lv));
|
||||
log_warn("WARNING: Cannot get info from tree node for %s.",
|
||||
display_lvname(lv));
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -784,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))
|
||||
@@ -793,13 +814,28 @@ int lv_has_target_type(struct dm_pool *mem, const struct logical_volume *lv,
|
||||
if (!dm_task_get_info(dmt, &info) || !info.exists)
|
||||
goto_out;
|
||||
|
||||
/* If there is a preloaded table, use that in preference. */
|
||||
if (info.inactive_table) {
|
||||
dm_task_destroy(dmt);
|
||||
|
||||
if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_STATUS, 0, 0, 0, 0)))
|
||||
goto_bad;
|
||||
|
||||
if (!dm_task_query_inactive_table(dmt))
|
||||
goto_out;
|
||||
|
||||
if (!dm_task_run(dmt))
|
||||
goto_out;
|
||||
|
||||
if (!dm_task_get_info(dmt, &info) || !info.exists || !info.inactive_table)
|
||||
goto_out;
|
||||
}
|
||||
|
||||
do {
|
||||
next = dm_get_next_target(dmt, next, &start, &length,
|
||||
&type, ¶ms);
|
||||
if (type && strncmp(type, target_type,
|
||||
strlen(target_type)) == 0) {
|
||||
if (info.live_table)
|
||||
r = 1;
|
||||
if (type && !strncmp(type, target_type, strlen(target_type))) {
|
||||
r = 1;
|
||||
break;
|
||||
}
|
||||
} while (next);
|
||||
@@ -812,6 +848,67 @@ bad:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _thin_lv_has_device_id(struct dm_pool *mem, const struct logical_volume *lv,
|
||||
const char *layer, unsigned device_id)
|
||||
{
|
||||
char *dlid;
|
||||
struct dm_task *dmt;
|
||||
struct dm_info info;
|
||||
void *next = NULL;
|
||||
uint64_t start, length;
|
||||
char *type = NULL;
|
||||
char *params = NULL;
|
||||
unsigned id = ~0;
|
||||
|
||||
if (!(dlid = build_dm_uuid(mem, lv, layer)))
|
||||
return_0;
|
||||
|
||||
if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_TABLE, 0, 0, 0, 1)))
|
||||
goto_bad;
|
||||
|
||||
if (!dm_task_run(dmt))
|
||||
goto_out;
|
||||
|
||||
if (!dm_task_get_info(dmt, &info) || !info.exists)
|
||||
goto_out;
|
||||
|
||||
/* If there is a preloaded table, use that in preference. */
|
||||
if (info.inactive_table) {
|
||||
dm_task_destroy(dmt);
|
||||
|
||||
if (!(dmt = _setup_task(NULL, dlid, 0, DM_DEVICE_TABLE, 0, 0, 0, 1)))
|
||||
goto_bad;
|
||||
|
||||
if (!dm_task_query_inactive_table(dmt))
|
||||
goto_out;
|
||||
|
||||
if (!dm_task_run(dmt))
|
||||
goto_out;
|
||||
|
||||
if (!dm_task_get_info(dmt, &info) || !info.exists || !info.inactive_table)
|
||||
goto_out;
|
||||
}
|
||||
|
||||
(void) dm_get_next_target(dmt, next, &start, &length, &type, ¶ms);
|
||||
|
||||
if (!type || strcmp(type, TARGET_NAME_THIN))
|
||||
goto_out;
|
||||
|
||||
if (!params || sscanf(params, "%*u:%*u %u", &id) != 1)
|
||||
goto_out;
|
||||
|
||||
log_debug_activation("%soaded thin volume %s with id %u is %smatching id %u.",
|
||||
info.inactive_table ? "Prel" : "L",
|
||||
display_lvname(lv), id,
|
||||
(device_id != id) ? "not " : "", device_id);
|
||||
out:
|
||||
dm_task_destroy(dmt);
|
||||
bad:
|
||||
dm_pool_free(mem, dlid);
|
||||
|
||||
return (device_id == id);
|
||||
}
|
||||
|
||||
int add_linear_area_to_dtree(struct dm_tree_node *node, uint64_t size, uint32_t extent_size, int use_linear_target, const char *vgname, const char *lvname)
|
||||
{
|
||||
uint32_t page_size;
|
||||
@@ -881,22 +978,19 @@ static int _percent_run(struct dev_manager *dm, const char *name,
|
||||
char *params = NULL;
|
||||
const struct dm_list *segh = lv ? &lv->segments : NULL;
|
||||
struct lv_segment *seg = NULL;
|
||||
struct segment_type *segtype;
|
||||
int first_time = 1;
|
||||
dm_percent_t percent = DM_PERCENT_INVALID;
|
||||
|
||||
uint64_t total_numerator = 0, total_denominator = 0;
|
||||
struct segment_type *segtype;
|
||||
|
||||
*overall_percent = percent;
|
||||
|
||||
if (!(dmt = _setup_task(name, dlid, event_nr,
|
||||
wait ? DM_DEVICE_WAITEVENT : DM_DEVICE_STATUS, 0, 0, 0)))
|
||||
if (!(segtype = get_segtype_from_string(dm->cmd, target_type)))
|
||||
return_0;
|
||||
|
||||
/* No freeze on overfilled thin-pool, read existing slightly outdated data */
|
||||
if (lv && lv_is_thin_pool(lv) &&
|
||||
!dm_task_no_flush(dmt))
|
||||
log_warn("Can't set no_flush flag."); /* Non fatal */
|
||||
if (!(dmt = _setup_task(name, dlid, event_nr,
|
||||
wait ? DM_DEVICE_WAITEVENT : DM_DEVICE_STATUS, 0, 0, 0, 0)))
|
||||
return_0;
|
||||
|
||||
if (!dm_task_run(dmt))
|
||||
goto_out;
|
||||
@@ -923,9 +1017,6 @@ static int _percent_run(struct dev_manager *dm, const char *name,
|
||||
if (!type || !params)
|
||||
continue;
|
||||
|
||||
if (!(segtype = get_segtype_from_string(dm->cmd, target_type)))
|
||||
continue;
|
||||
|
||||
if (strcmp(type, target_type)) {
|
||||
/* If kernel's type isn't an exact match is it compatible? */
|
||||
if (!segtype->ops->target_status_compatible ||
|
||||
@@ -983,7 +1074,8 @@ static int _percent(struct dev_manager *dm, const char *name, const char *dlid,
|
||||
if (_percent_run(dm, NULL, dlid, target_type, wait, lv, percent,
|
||||
event_nr, fail_if_percent_unsupported))
|
||||
return 1;
|
||||
else if (_percent_run(dm, NULL, dlid + sizeof(UUID_PREFIX) - 1,
|
||||
else if (_original_uuid_format_check_required(dm->cmd) &&
|
||||
_percent_run(dm, NULL, dlid + sizeof(UUID_PREFIX) - 1,
|
||||
target_type, wait, lv, percent,
|
||||
event_nr, fail_if_percent_unsupported))
|
||||
return 1;
|
||||
@@ -1014,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))
|
||||
@@ -1157,7 +1249,7 @@ int dev_manager_snapshot_percent(struct dev_manager *dm,
|
||||
/*
|
||||
* Try and get some info on this device.
|
||||
*/
|
||||
if (!_percent(dm, name, dlid, "snapshot", 0, NULL, percent,
|
||||
if (!_percent(dm, name, dlid, TARGET_NAME_SNAPSHOT, 0, NULL, percent,
|
||||
NULL, fail_if_percent_unsupported))
|
||||
return_0;
|
||||
|
||||
@@ -1210,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))
|
||||
@@ -1269,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))
|
||||
@@ -1304,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))
|
||||
@@ -1315,7 +1407,7 @@ int dev_manager_cache_status(struct dev_manager *dm,
|
||||
|
||||
dm_get_next_target(dmt, NULL, &start, &length, &type, ¶ms);
|
||||
|
||||
if (!type || strcmp(type, "cache")) {
|
||||
if (!type || strcmp(type, TARGET_NAME_CACHE)) {
|
||||
log_error("Expected cache segment type but got %s instead",
|
||||
type ? type : "NULL");
|
||||
goto out;
|
||||
@@ -1326,17 +1418,23 @@ int dev_manager_cache_status(struct dev_manager *dm,
|
||||
* ->target_percent() API is able to transfer only a single value.
|
||||
* Needs to be able to pass whole structure.
|
||||
*/
|
||||
if (!dm_get_status_cache(dm->mem, params, &((*status)->cache)))
|
||||
if (!dm_get_status_cache(dm->mem, params, &c))
|
||||
goto_out;
|
||||
|
||||
c = (*status)->cache;
|
||||
(*status)->cache = c;
|
||||
(*status)->mem = dm->mem; /* User has to destroy this mem pool later */
|
||||
(*status)->data_usage = dm_make_percent(c->used_blocks,
|
||||
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);
|
||||
if (c->fail || c->error) {
|
||||
(*status)->data_usage =
|
||||
(*status)->metadata_usage =
|
||||
(*status)->dirty_usage = DM_PERCENT_INVALID;
|
||||
} else {
|
||||
(*status)->data_usage = dm_make_percent(c->used_blocks,
|
||||
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);
|
||||
}
|
||||
r = 1;
|
||||
out:
|
||||
dm_task_destroy(dmt);
|
||||
@@ -1347,7 +1445,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;
|
||||
@@ -1361,12 +1459,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;
|
||||
|
||||
@@ -1403,7 +1498,7 @@ int dev_manager_thin_pool_percent(struct dev_manager *dm,
|
||||
return_0;
|
||||
|
||||
log_debug_activation("Getting device status percentage for %s", name);
|
||||
if (!(_percent(dm, name, dlid, "thin-pool", 0,
|
||||
if (!(_percent(dm, name, dlid, TARGET_NAME_THIN_POOL, 0,
|
||||
(metadata) ? lv : NULL, percent, NULL, 1)))
|
||||
return_0;
|
||||
|
||||
@@ -1426,7 +1521,7 @@ int dev_manager_thin_percent(struct dev_manager *dm,
|
||||
return_0;
|
||||
|
||||
log_debug_activation("Getting device status percentage for %s", name);
|
||||
if (!(_percent(dm, name, dlid, "thin", 0,
|
||||
if (!(_percent(dm, name, dlid, TARGET_NAME_THIN, 0,
|
||||
(mapped) ? NULL : lv, percent, NULL, 1)))
|
||||
return_0;
|
||||
|
||||
@@ -1448,7 +1543,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))
|
||||
@@ -1464,7 +1559,7 @@ int dev_manager_thin_device_id(struct dev_manager *dm,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!target_type || strcmp(target_type, "thin")) {
|
||||
if (!target_type || strcmp(target_type, TARGET_NAME_THIN)) {
|
||||
log_error("Unexpected target type %s found for thin %s.",
|
||||
target_type, display_lvname(lv));
|
||||
goto out;
|
||||
@@ -1504,6 +1599,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;
|
||||
@@ -1515,7 +1616,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);
|
||||
@@ -1655,7 +1756,7 @@ static int _add_dev_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
return_0;
|
||||
|
||||
log_debug_activation("Getting device info for %s [%s]", name, dlid);
|
||||
if (!_info(dlid, 1, 0, &info, NULL, NULL)) {
|
||||
if (!_info(dm->cmd, dlid, 1, 0, &info, NULL, NULL)) {
|
||||
log_error("Failed to get info for %s [%s].", name, dlid);
|
||||
return 0;
|
||||
}
|
||||
@@ -1668,7 +1769,7 @@ static int _add_dev_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
/*
|
||||
* FIXME compare info.major with lv->major if multiple major support
|
||||
*/
|
||||
if (info.exists && (info.minor != lv->minor)) {
|
||||
if (info.exists && ((int) info.minor != lv->minor)) {
|
||||
log_error("Volume %s (%" PRIu32 ":%" PRIu32")"
|
||||
" differs from already active device "
|
||||
"(%" PRIu32 ":%" PRIu32")",
|
||||
@@ -1839,22 +1940,22 @@ static int _pool_callback(struct dm_tree_node *node,
|
||||
return 0;
|
||||
}
|
||||
/* let's assume there is no problem to read 64 bytes */
|
||||
if (read(fd, buf, sizeof(buf)) < sizeof(buf)) {
|
||||
if (read(fd, buf, sizeof(buf)) < (int)sizeof(buf)) {
|
||||
log_sys_error("read", argv[args]);
|
||||
if (close(fd))
|
||||
log_sys_error("close", argv[args]);
|
||||
return 0;
|
||||
}
|
||||
for (ret = 0; ret < DM_ARRAY_SIZE(buf); ++ret)
|
||||
for (ret = 0; ret < (int) DM_ARRAY_SIZE(buf); ++ret)
|
||||
if (buf[ret])
|
||||
break;
|
||||
|
||||
if (close(fd))
|
||||
log_sys_error("close", argv[args]);
|
||||
|
||||
if (ret == DM_ARRAY_SIZE(buf)) {
|
||||
log_debug("%s skipped, detect empty disk header on %s.",
|
||||
argv[0], argv[args]);
|
||||
if (ret == (int) DM_ARRAY_SIZE(buf)) {
|
||||
log_debug_activation("%s skipped, detect empty disk header on %s.",
|
||||
argv[0], argv[args]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -1914,7 +2015,7 @@ static int _pool_register_callback(struct dev_manager *dm,
|
||||
data->global = "thin";
|
||||
} else if (lv_is_cache(lv)) { /* cache pool */
|
||||
data->pool_lv = first_seg(lv)->pool_lv;
|
||||
data->skip_zero = dm->activation;
|
||||
data->skip_zero = 1; /* cheap read-error detection */
|
||||
data->exec = global_cache_check_executable_CFG;
|
||||
data->opts = global_cache_check_options_CFG;
|
||||
data->global = "cache";
|
||||
@@ -2059,6 +2160,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;
|
||||
@@ -2111,7 +2213,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;
|
||||
}
|
||||
@@ -2179,7 +2281,7 @@ static char *_add_error_device(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
return_NULL;
|
||||
|
||||
log_debug_activation("Getting device info for %s [%s]", name, dlid);
|
||||
if (!_info(dlid, 1, 0, &info, NULL, NULL)) {
|
||||
if (!_info(dm->cmd, dlid, 1, 0, &info, NULL, NULL)) {
|
||||
log_error("Failed to get info for %s [%s].", name, dlid);
|
||||
return 0;
|
||||
}
|
||||
@@ -2208,7 +2310,7 @@ 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;
|
||||
|
||||
if (!strcmp(dm->cmd->stripe_filler, "error")) {
|
||||
if (!strcmp(dm->cmd->stripe_filler, TARGET_NAME_ERROR)) {
|
||||
/*
|
||||
* FIXME, the tree pointer is first field of dm_tree_node, but
|
||||
* we don't have the struct definition available.
|
||||
@@ -2283,9 +2385,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)))
|
||||
@@ -2613,7 +2719,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;
|
||||
@@ -2646,6 +2752,7 @@ static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
uint32_t read_ahead = lv->read_ahead;
|
||||
uint32_t read_ahead_flags = UINT32_C(0);
|
||||
int save_pending_delete = dm->track_pending_delete;
|
||||
int merge_in_progress = 0;
|
||||
|
||||
/* LV with pending delete is never put new into a table */
|
||||
if (lv_is_pending_delete(lv) && !_cached_dm_info(dm->mem, dtree, lv, NULL))
|
||||
@@ -2666,23 +2773,51 @@ static int _add_new_lv_to_dtree(struct dev_manager *dm, struct dm_tree *dtree,
|
||||
if (!layer && lv_is_merging_origin(lv)) {
|
||||
seg = find_snapshot(lv);
|
||||
/*
|
||||
* Clear merge attributes if merge isn't currently possible:
|
||||
* Prevent merge if merge isn't currently possible:
|
||||
* either origin or merging snapshot are open
|
||||
* - but use "snapshot-merge" if it is already in use
|
||||
* - for old snaps use "snapshot-merge" if it is already in use
|
||||
* - open_count is always retrieved (as of dm-ioctl 4.7.0)
|
||||
* so just use the tree's existing nodes' info
|
||||
*/
|
||||
/* An activating merging origin won't have a node in the tree yet */
|
||||
if (((dinfo = _cached_dm_info(dm->mem, dtree, lv, NULL)) &&
|
||||
dinfo->open_count) ||
|
||||
((dinfo = _cached_dm_info(dm->mem, dtree,
|
||||
seg_is_thin_volume(seg) ?
|
||||
seg->lv : seg->cow, NULL)) &&
|
||||
dinfo->open_count)) {
|
||||
if (seg_is_thin_volume(seg) ||
|
||||
/* FIXME Is there anything simpler to check for instead? */
|
||||
!lv_has_target_type(dm->mem, lv, NULL, "snapshot-merge"))
|
||||
if ((dinfo = _cached_dm_info(dm->mem, dtree, lv, NULL))) {
|
||||
/* Merging origin LV is present, check if mergins is already running. */
|
||||
if ((seg_is_thin_volume(seg) && _thin_lv_has_device_id(dm->mem, lv, NULL, seg->device_id)) ||
|
||||
(!seg_is_thin_volume(seg) && lv_has_target_type(dm->mem, lv, NULL, TARGET_NAME_SNAPSHOT_MERGE))) {
|
||||
log_debug_activation("Merging of snapshot volume %s to origin %s is in progress.",
|
||||
display_lvname(seg->lv), display_lvname(seg->lv));
|
||||
merge_in_progress = 1; /* Merge is already running */
|
||||
} /* Merge is not yet running, so check if it can be started */
|
||||
else if (laopts->resuming) {
|
||||
log_debug_activation("Postponing pending snapshot merge for origin %s, "
|
||||
"merge was not started before suspend.",
|
||||
display_lvname(lv));
|
||||
laopts->no_merging = 1; /* Cannot be reloaded in suspend */
|
||||
} /* Non-resuming merge requires origin to be unused */
|
||||
else if (dinfo->open_count) {
|
||||
log_debug_activation("Postponing pending snapshot merge for origin %s, "
|
||||
"origin volume is opened.",
|
||||
display_lvname(lv));
|
||||
laopts->no_merging = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* If merge would be still undecided, look as snapshot */
|
||||
if (!merge_in_progress && !laopts->no_merging &&
|
||||
(dinfo = _cached_dm_info(dm->mem, dtree,
|
||||
seg_is_thin_volume(seg) ?
|
||||
seg->lv : seg->cow, NULL))) {
|
||||
if (seg_is_thin_volume(seg)) {
|
||||
/* Active thin snapshot prevents merge */
|
||||
log_debug_activation("Postponing pending snapshot merge for origin volume %s, "
|
||||
"merging thin snapshot volume %s is active.",
|
||||
display_lvname(lv), display_lvname(seg->lv));
|
||||
laopts->no_merging = 1;
|
||||
} else if (dinfo->open_count) {
|
||||
log_debug_activation("Postponing pending snapshot merge for origin volume %s, "
|
||||
"merging snapshot volume %s is opened.",
|
||||
display_lvname(lv), display_lvname(seg->lv));
|
||||
laopts->no_merging = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2864,7 +2999,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;
|
||||
@@ -2980,6 +3115,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;
|
||||
|
||||
@@ -3015,7 +3151,7 @@ static int _tree_action(struct dev_manager *dm, const struct logical_volume *lv,
|
||||
break;
|
||||
case SUSPEND:
|
||||
dm_tree_skip_lockfs(root);
|
||||
if (!dm->flush_required && !lv_is_pvmove(lv))
|
||||
if (!dm->flush_required)
|
||||
dm_tree_use_no_flush_suspend(root);
|
||||
/* Fall through */
|
||||
case SUSPEND_WITH_LOCKFS:
|
||||
@@ -3034,14 +3170,15 @@ static int _tree_action(struct dev_manager *dm, const struct logical_volume *lv,
|
||||
if (!dm_tree_preload_children(root, dlid, DLID_SIZE))
|
||||
goto_out;
|
||||
|
||||
//if (action == PRELOAD) { log_debug("SLEEP"); sleep(7); }
|
||||
|
||||
if ((dm_tree_node_size_changed(root) < 0))
|
||||
dm->flush_required = 1;
|
||||
|
||||
/* Currently keep the code require flush for any
|
||||
* non 'thin pool/volume, mirror' or with any size change */
|
||||
if (!lv_is_thin_volume(lv) &&
|
||||
!lv_is_thin_pool(lv) &&
|
||||
(!lv_is_mirror(lv) || dm_tree_node_size_changed(root)))
|
||||
* non 'thin pool/volume' and size increase */
|
||||
else if (!lv_is_thin_volume(lv) &&
|
||||
!lv_is_thin_pool(lv) &&
|
||||
dm_tree_node_size_changed(root))
|
||||
dm->flush_required = 1;
|
||||
|
||||
if (action == ACTIVATE) {
|
||||
@@ -3086,6 +3223,8 @@ int dev_manager_activate(struct dev_manager *dm, const struct logical_volume *lv
|
||||
int dev_manager_preload(struct dev_manager *dm, const struct logical_volume *lv,
|
||||
struct lv_activate_opts *laopts, int *flush_required)
|
||||
{
|
||||
dm->flush_required = *flush_required;
|
||||
|
||||
if (!_tree_action(dm, lv, laopts, PRELOAD))
|
||||
return_0;
|
||||
|
||||
|
||||
@@ -45,11 +45,12 @@ void dev_manager_exit(void);
|
||||
* (eg, an origin is created before its snapshot, but is not
|
||||
* unsuspended until the snapshot is also created.)
|
||||
*/
|
||||
int dev_manager_info(struct dm_pool *mem, const struct logical_volume *lv,
|
||||
int dev_manager_info(struct cmd_context *cmd, const struct logical_volume *lv,
|
||||
const char *layer,
|
||||
int with_open_count, int with_read_ahead,
|
||||
struct dm_info *dminfo, uint32_t *read_ahead,
|
||||
struct lv_seg_status *seg_status);
|
||||
|
||||
int dev_manager_snapshot_percent(struct dev_manager *dm,
|
||||
const struct logical_volume *lv,
|
||||
dm_percent_t *percent);
|
||||
@@ -68,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);
|
||||
|
||||
904
lib/cache/lvmcache.c
vendored
904
lib/cache/lvmcache.c
vendored
File diff suppressed because it is too large
Load Diff
23
lib/cache/lvmcache.h
vendored
23
lib/cache/lvmcache.h
vendored
@@ -102,15 +102,16 @@ 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,
|
||||
unsigned *scan_done_once, uint64_t *label_sector);
|
||||
const char *lvmcache_pvid_from_devname(struct cmd_context *cmd,
|
||||
const char *dev_name);
|
||||
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);
|
||||
|
||||
@@ -156,6 +157,11 @@ int lvmcache_add_mda(struct lvmcache_info *info, struct device *dev,
|
||||
int lvmcache_add_da(struct lvmcache_info *info, uint64_t start, uint64_t size);
|
||||
int lvmcache_add_ba(struct lvmcache_info *info, uint64_t start, uint64_t size);
|
||||
|
||||
void lvmcache_set_ext_version(struct lvmcache_info *info, uint32_t version);
|
||||
uint32_t lvmcache_ext_version(struct lvmcache_info *info);
|
||||
void lvmcache_set_ext_flags(struct lvmcache_info *info, uint32_t flags);
|
||||
uint32_t lvmcache_ext_flags(struct lvmcache_info *info);
|
||||
|
||||
const struct format_type *lvmcache_fmt(struct lvmcache_info *info);
|
||||
struct label *lvmcache_get_label(struct lvmcache_info *info);
|
||||
|
||||
@@ -188,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);
|
||||
|
||||
@@ -204,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
|
||||
|
||||
1758
lib/cache/lvmetad.c
vendored
1758
lib/cache/lvmetad.c
vendored
File diff suppressed because it is too large
Load Diff
103
lib/cache/lvmetad.h
vendored
103
lib/cache/lvmetad.h
vendored
@@ -28,54 +28,35 @@ typedef int (*activation_handler) (struct cmd_context *cmd,
|
||||
enum activation_change activate);
|
||||
|
||||
#ifdef LVMETAD_SUPPORT
|
||||
/*
|
||||
* Sets up a global handle for our process.
|
||||
*/
|
||||
void lvmetad_init(struct cmd_context *);
|
||||
|
||||
/*
|
||||
* Override the use of lvmetad for retrieving scan results and metadata.
|
||||
* lvmetad_connect: connect to lvmetad
|
||||
* lvmetad_disconnect: disconnect from lvmetad
|
||||
* lvmetad_make_unused: disconnect from lvmetad and refresh cmd filter
|
||||
* lvmetad_used: check if lvmetad is being used (i.e. is connected)
|
||||
*/
|
||||
void lvmetad_set_active(struct cmd_context *, int);
|
||||
int lvmetad_connect(struct cmd_context *cmd);
|
||||
void lvmetad_disconnect(void);
|
||||
void lvmetad_make_unused(struct cmd_context *cmd);
|
||||
int lvmetad_used(void);
|
||||
|
||||
|
||||
/*
|
||||
* Configure the socket that lvmetad_init will use to connect to the daemon.
|
||||
*/
|
||||
void lvmetad_set_socket(const char *);
|
||||
|
||||
/*
|
||||
* Check whether lvmetad is used.
|
||||
*/
|
||||
int lvmetad_used(void);
|
||||
|
||||
/*
|
||||
* Check if lvmetad socket is present (either the one set by lvmetad_set_socket
|
||||
* or the default one if not set). For example, this may be used before calling
|
||||
* lvmetad_active() check that does connect to the socket - this would produce
|
||||
* various connection errors if the socket is not present.
|
||||
* or the default one if not set).
|
||||
*/
|
||||
int lvmetad_socket_present(void);
|
||||
|
||||
/*
|
||||
* Check whether lvmetad is active (where active means both that it is running
|
||||
* and that we have a working connection with it). It opens new connection
|
||||
* with lvmetad in the process when lvmetad is supposed to be used and the
|
||||
* connection is not open yet.
|
||||
* Check if lvmetad pidfile is present, indicating that the lvmetad
|
||||
* process is running or not.
|
||||
*/
|
||||
int lvmetad_active(void);
|
||||
|
||||
/*
|
||||
* Connect to lvmetad unless the connection is already open or lvmetad is
|
||||
* not configured to be used. If we fail to connect, print a warning.
|
||||
*/
|
||||
void lvmetad_connect_or_warn(void);
|
||||
|
||||
/*
|
||||
* Drop connection to lvmetad. A subsequent lvmetad_connect_or_warn or
|
||||
* lvmetad_active will re-establish the connection (possibly at a
|
||||
* different socket path).
|
||||
*/
|
||||
void lvmetad_disconnect(void);
|
||||
int lvmetad_pidfile_present(void);
|
||||
|
||||
/*
|
||||
* Set the "lvmetad validity token" (currently only consists of the lvmetad
|
||||
@@ -97,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
|
||||
@@ -114,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
|
||||
@@ -161,45 +146,55 @@ 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 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);
|
||||
int lvmetad_token_matches(struct cmd_context *cmd);
|
||||
|
||||
int lvmetad_vg_is_foreign(struct cmd_context *cmd, const char *vgname, const char *vgid);
|
||||
|
||||
int lvmetad_is_disabled(struct cmd_context *cmd, const char **reason);
|
||||
void lvmetad_set_disabled(struct cmd_context *cmd, const char *reason);
|
||||
void lvmetad_clear_disabled(struct cmd_context *cmd);
|
||||
|
||||
# else /* LVMETAD_SUPPORT */
|
||||
|
||||
# define lvmetad_init(cmd) do { } while (0)
|
||||
# define lvmetad_disconnect() do { } while (0)
|
||||
# define lvmetad_set_active(cmd, a) do { } while (0)
|
||||
# define lvmetad_connect(cmd) (0)
|
||||
# define lvmetad_make_unused(cmd) do { } while (0)
|
||||
# define lvmetad_used() (0)
|
||||
# define lvmetad_set_socket(a) do { } while (0)
|
||||
# define lvmetad_used() (0)
|
||||
# define lvmetad_socket_present() (0)
|
||||
# define lvmetad_active() (0)
|
||||
# define lvmetad_connect_or_warn() do { } while (0)
|
||||
# define lvmetad_pidfile_present() (0)
|
||||
# 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) (0)
|
||||
# define lvmetad_pvscan_foreign_vgs(cmd, handler) (0)
|
||||
# define lvmetad_vg_clear_outdated_pvs(vg) (1)
|
||||
# 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)
|
||||
# define lvmetad_token_matches(cmd) (1)
|
||||
# define lvmetad_is_disabled(cmd, reason) (0)
|
||||
# define lvmetad_set_disabled(cmd, reason) do { } while (0)
|
||||
# define lvmetad_clear_disabled(cmd) do { } while (0)
|
||||
|
||||
# endif /* LVMETAD_SUPPORT */
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -208,11 +207,16 @@ static int _target_present(struct cmd_context *cmd,
|
||||
uint32_t maj;
|
||||
uint32_t min;
|
||||
unsigned cache_feature;
|
||||
unsigned cache_alias;
|
||||
const char feature[12];
|
||||
const char module[12]; /* check dm-%s */
|
||||
const char *aliasing;
|
||||
} _features[] = {
|
||||
{ 1, 3, CACHE_FEATURE_POLICY_MQ, "policy_mq", "cache-mq" },
|
||||
{ 1, 8, CACHE_FEATURE_POLICY_SMQ, "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" },
|
||||
};
|
||||
static const char _lvmconf[] = "global/cache_disabled_features";
|
||||
static unsigned _attrs = 0;
|
||||
@@ -230,10 +234,8 @@ static int _target_present(struct cmd_context *cmd,
|
||||
if (!_cache_checked) {
|
||||
_cache_checked = 1;
|
||||
|
||||
if (!(_cache_present = target_present(cmd, "cache", 1)))
|
||||
return 0;
|
||||
|
||||
if (!target_version("cache", &maj, &min, &patchlevel))
|
||||
if (!(_cache_present = target_present_version(cmd, TARGET_NAME_CACHE, 1,
|
||||
&maj, &min, &patchlevel)))
|
||||
return_0;
|
||||
|
||||
if ((maj < 1) ||
|
||||
@@ -245,13 +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] || module_present(cmd, _features[i].module)))
|
||||
_attrs |= _features[i].cache_feature;
|
||||
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);
|
||||
}
|
||||
@@ -294,7 +300,7 @@ static int _modules_needed(struct dm_pool *mem,
|
||||
const struct lv_segment *seg __attribute__((unused)),
|
||||
struct dm_list *modules)
|
||||
{
|
||||
if (!str_list_add(mem, modules, "cache")) {
|
||||
if (!str_list_add(mem, modules, MODULE_NAME_CACHE)) {
|
||||
log_error("String list allocation failed for cache module.");
|
||||
return 0;
|
||||
}
|
||||
@@ -348,12 +354,12 @@ static int _cache_text_import(struct lv_segment *seg,
|
||||
|
||||
seg->lv->status |= strstr(seg->lv->name, "_corig") ? LV_PENDING_DELETE : 0;
|
||||
|
||||
if (!attach_pool_lv(seg, pool_lv, NULL, NULL))
|
||||
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;
|
||||
}
|
||||
@@ -392,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.");
|
||||
@@ -399,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;
|
||||
@@ -410,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,
|
||||
|
||||
@@ -278,6 +278,8 @@ static int _parse_debug_classes(struct cmd_context *cmd)
|
||||
debug_classes |= LOG_CLASS_LOCKING;
|
||||
else if (!strcasecmp(cv->v.str, "lvmpolld"))
|
||||
debug_classes |= LOG_CLASS_LVMPOLLD;
|
||||
else if (!strcasecmp(cv->v.str, "dbus"))
|
||||
debug_classes |= LOG_CLASS_DBUS;
|
||||
else
|
||||
log_verbose("Unrecognised value for log/debug_classes: %s", cv->v.str);
|
||||
}
|
||||
@@ -315,7 +317,7 @@ static void _init_logging(struct cmd_context *cmd)
|
||||
init_silent(cmd->default_settings.silent);
|
||||
|
||||
/* Verbose level for tty output */
|
||||
cmd->default_settings.verbose = find_config_tree_bool(cmd, log_verbose_CFG, NULL);
|
||||
cmd->default_settings.verbose = find_config_tree_int(cmd, log_verbose_CFG, NULL);
|
||||
init_verbose(cmd->default_settings.verbose + VERBOSE_BASE_LEVEL);
|
||||
|
||||
/* Log message formatting */
|
||||
@@ -363,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);
|
||||
@@ -675,6 +677,9 @@ static int _process_config(struct cmd_context *cmd)
|
||||
if (!process_profilable_config(cmd))
|
||||
return_0;
|
||||
|
||||
if (find_config_tree_bool(cmd, report_two_word_unknown_device_CFG, NULL))
|
||||
init_unknown_device_name("unknown device");
|
||||
|
||||
init_detect_internal_vg_cache_corruption
|
||||
(find_config_tree_bool(cmd, global_detect_internal_vg_cache_corruption_CFG, NULL));
|
||||
|
||||
@@ -1048,7 +1053,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)
|
||||
{
|
||||
@@ -1073,6 +1078,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))) {
|
||||
@@ -1154,7 +1166,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
|
||||
@@ -1168,7 +1180,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
|
||||
*
|
||||
@@ -1668,6 +1680,26 @@ static int _reopen_stream(FILE *stream, int fd, const char *mode, const char *na
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* init_connections();
|
||||
* _init_lvmetad();
|
||||
* lvmetad_disconnect(); (close previous connection)
|
||||
* lvmetad_set_socket(); (set path from config)
|
||||
* lvmetad_set_token(); (set token from filter config)
|
||||
* if (find_config(use_lvmetad))
|
||||
* lvmetad_connect();
|
||||
*
|
||||
* If lvmetad_connect() is successful, lvmetad_used() will
|
||||
* return 1.
|
||||
*
|
||||
* If the config has use_lvmetad=0, then lvmetad_connect()
|
||||
* will not be called, and lvmetad_used() will return 0.
|
||||
*
|
||||
* Other code should use lvmetad_used() to check if the
|
||||
* command is using lvmetad.
|
||||
*
|
||||
*/
|
||||
|
||||
static int _init_lvmetad(struct cmd_context *cmd)
|
||||
{
|
||||
const struct dm_config_node *cn;
|
||||
@@ -1690,13 +1722,29 @@ static int _init_lvmetad(struct cmd_context *cmd)
|
||||
|
||||
if (find_config_tree_int(cmd, global_locking_type_CFG, NULL) == 3 &&
|
||||
find_config_tree_bool(cmd, global_use_lvmetad_CFG, NULL)) {
|
||||
log_warn("WARNING: configuration setting use_lvmetad overridden to 0 due to locking_type 3. "
|
||||
"Clustered environment not supported by lvmetad yet.");
|
||||
lvmetad_set_active(NULL, 0);
|
||||
} else
|
||||
lvmetad_set_active(NULL, find_config_tree_bool(cmd, global_use_lvmetad_CFG, NULL));
|
||||
log_warn("WARNING: Not using lvmetad because locking_type is 3 (clustered).");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!find_config_tree_bool(cmd, global_use_lvmetad_CFG, NULL)) {
|
||||
if (lvmetad_pidfile_present()) {
|
||||
log_warn("WARNING: Not using lvmetad because config setting use_lvmetad=0.");
|
||||
log_warn("WARNING: To avoid corruption, rescan devices to make changes visible (pvscan --cache).");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!lvmetad_connect(cmd)) {
|
||||
log_warn("WARNING: Failed to connect to lvmetad. Falling back to device scanning.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!lvmetad_used()) {
|
||||
/* This should never happen. */
|
||||
log_error(INTERNAL_ERROR "lvmetad setup incorrect");
|
||||
return 0;
|
||||
}
|
||||
|
||||
lvmetad_init(cmd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1735,6 +1783,49 @@ 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);
|
||||
|
||||
if (!_init_lvm_conf(cmd))
|
||||
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,
|
||||
@@ -1893,6 +1984,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;
|
||||
|
||||
@@ -1904,7 +1997,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;
|
||||
@@ -2161,6 +2254,9 @@ void destroy_toolcontext(struct cmd_context *cmd)
|
||||
if (cmd->cft_def_hash)
|
||||
dm_hash_destroy(cmd->cft_def_hash);
|
||||
|
||||
if (cmd->log_rh)
|
||||
dm_report_free(cmd->log_rh);
|
||||
|
||||
if (cmd->libmem)
|
||||
dm_pool_destroy(cmd->libmem);
|
||||
|
||||
|
||||
@@ -113,6 +113,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;
|
||||
@@ -128,6 +129,8 @@ struct cmd_context {
|
||||
unsigned threaded:1; /* set if running within a thread e.g. clvmd */
|
||||
unsigned independent_metadata_areas:1; /* active formats have MDAs outside PVs */
|
||||
unsigned unknown_system_id:1;
|
||||
unsigned include_historical_lvs:1; /* also process/report/display historical LVs */
|
||||
unsigned record_historical_lvs:1; /* record historical LVs */
|
||||
unsigned include_foreign_vgs:1; /* report/display cmds can reveal foreign VGs */
|
||||
unsigned include_shared_vgs:1; /* report/display cmds can reveal lockd VGs */
|
||||
unsigned include_active_foreign_vgs:1; /* cmd should process foreign VGs with active LVs */
|
||||
@@ -139,6 +142,9 @@ struct cmd_context {
|
||||
unsigned lockd_vg_rescan:1;
|
||||
unsigned lockd_vg_default_sh:1;
|
||||
unsigned lockd_vg_enforce_sh:1;
|
||||
unsigned vg_notify:1;
|
||||
unsigned lv_notify:1;
|
||||
unsigned pv_notify:1;
|
||||
|
||||
/*
|
||||
* Filtering.
|
||||
@@ -179,6 +185,11 @@ struct cmd_context {
|
||||
char dev_dir[PATH_MAX];
|
||||
char proc_dir[PATH_MAX];
|
||||
|
||||
/*
|
||||
* Command log reporting.
|
||||
*/
|
||||
struct dm_report *log_rh; /* keep log report of last cmd for further queries if cmd line is interactive (e.g. lvm shell) */
|
||||
|
||||
/*
|
||||
* Buffers.
|
||||
*/
|
||||
@@ -192,6 +203,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 */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -213,6 +225,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);
|
||||
|
||||
@@ -124,7 +124,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,
|
||||
@@ -395,6 +395,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"
|
||||
@@ -420,7 +432,7 @@ cfg(allocation_maximise_cling_CFG, "maximise_cling", allocation_CFG_SECTION, 0,
|
||||
"the same disks. This setting can be used to disable the changes\n"
|
||||
"and revert to the previous algorithm.\n")
|
||||
|
||||
cfg(allocation_use_blkid_wiping_CFG, "use_blkid_wiping", allocation_CFG_SECTION, 0, CFG_TYPE_BOOL, 1, vsn(2, 2, 105), "@DEFAULT_USE_BLKID_WIPING@", 0, NULL,
|
||||
cfg(allocation_use_blkid_wiping_CFG, "use_blkid_wiping", allocation_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_USE_BLKID_WIPING, vsn(2, 2, 105), "@DEFAULT_USE_BLKID_WIPING@", 0, NULL,
|
||||
"Use blkid to detect existing signatures on new PVs and LVs.\n"
|
||||
"The blkid library can detect more signatures than the native LVM\n"
|
||||
"detection code, but may take longer. LVM needs to be compiled with\n"
|
||||
@@ -535,7 +547,48 @@ 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_verbose_CFG, "verbose", log_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_VERBOSE, vsn(1, 0, 0), NULL, 0, NULL,
|
||||
cfg(log_report_command_log_CFG, "report_command_log", log_CFG_SECTION, CFG_PROFILABLE | CFG_DEFAULT_COMMENTED, 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_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_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_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")
|
||||
|
||||
cfg(log_silent_CFG, "silent", log_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_SILENT, vsn(2, 2, 98), NULL, 0, NULL,
|
||||
@@ -581,7 +634,7 @@ cfg(log_activation_CFG, "activation", log_CFG_SECTION, 0, CFG_TYPE_BOOL, 0, vsn(
|
||||
|
||||
cfg(log_activate_file_CFG, "activate_file", log_CFG_SECTION, CFG_DEFAULT_UNDEFINED | CFG_UNSUPPORTED, CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL, 0, NULL, NULL)
|
||||
|
||||
cfg_array(log_debug_classes_CFG, "debug_classes", log_CFG_SECTION, CFG_ALLOW_EMPTY, CFG_TYPE_STRING, "#Smemory#Sdevices#Sactivation#Sallocation#Slvmetad#Smetadata#Scache#Slocking#Slvmpolld", vsn(2, 2, 99), NULL, 0, NULL,
|
||||
cfg_array(log_debug_classes_CFG, "debug_classes", log_CFG_SECTION, CFG_ALLOW_EMPTY, CFG_TYPE_STRING, "#Smemory#Sdevices#Sactivation#Sallocation#Slvmetad#Smetadata#Scache#Slocking#Slvmpolld#Sdbus", vsn(2, 2, 99), NULL, 0, NULL,
|
||||
"Select log messages by class.\n"
|
||||
"Some debugging messages are assigned to a class and only appear in\n"
|
||||
"debug output if the class is listed here. Classes currently\n"
|
||||
@@ -854,12 +907,20 @@ cfg(global_use_lvmetad_CFG, "use_lvmetad", global_CFG_SECTION, 0, CFG_TYPE_BOOL,
|
||||
"scanning from the LVM system entirely, including lvmetad, use\n"
|
||||
"devices/global_filter.\n")
|
||||
|
||||
cfg(global_lvmetad_update_wait_time_CFG, "lvmetad_update_wait_time", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_LVMETAD_UPDATE_WAIT_TIME, vsn(2, 2, 151), NULL, 0, NULL,
|
||||
"The number of seconds a command will wait for lvmetad update to finish.\n"
|
||||
"After waiting for this period, a command will not use lvmetad, and\n"
|
||||
"will revert to disk scanning.\n")
|
||||
|
||||
cfg(global_use_lvmlockd_CFG, "use_lvmlockd", global_CFG_SECTION, 0, CFG_TYPE_BOOL, 0, vsn(2, 2, 124), NULL, 0, NULL,
|
||||
"Use lvmlockd for locking among hosts using LVM on shared storage.\n"
|
||||
"See lvmlockd(8) for more information.\n")
|
||||
"Applicable only if LVM is compiled with lockd support in which\n"
|
||||
"case there is also lvmlockd(8) man page available for more\n"
|
||||
"information.\n")
|
||||
|
||||
cfg(global_lvmlockd_lock_retries_CFG, "lvmlockd_lock_retries", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_LVMLOCKD_LOCK_RETRIES, vsn(2, 2, 125), NULL, 0, NULL,
|
||||
"Retry lvmlockd lock requests this many times.\n")
|
||||
"Retry lvmlockd lock requests this many times.\n"
|
||||
"Applicable only if LVM is compiled with lockd support\n")
|
||||
|
||||
cfg(global_sanlock_lv_extend_CFG, "sanlock_lv_extend", global_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_SANLOCK_LV_EXTEND_MB, vsn(2, 2, 124), NULL, 0, NULL,
|
||||
"Size in MiB to extend the internal LV holding sanlock locks.\n"
|
||||
@@ -867,7 +928,8 @@ cfg(global_sanlock_lv_extend_CFG, "sanlock_lv_extend", global_CFG_SECTION, CFG_D
|
||||
"LVs have been created, the internal LV needs to be extended. lvcreate\n"
|
||||
"will automatically extend the internal LV when needed by the amount\n"
|
||||
"specified here. Setting this to 0 disables the automatic extension\n"
|
||||
"and can cause lvcreate to fail.\n")
|
||||
"and can cause lvcreate to fail. Applicable only if LVM is compiled\n"
|
||||
"with lockd support\n")
|
||||
|
||||
cfg(global_thin_check_executable_CFG, "thin_check_executable", global_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, THIN_CHECK_CMD, vsn(2, 2, 94), "@THIN_CHECK_CMD@", 0, NULL,
|
||||
"The full path to the thin_check command.\n"
|
||||
@@ -993,7 +1055,13 @@ cfg(global_use_lvmpolld_CFG, "use_lvmpolld", global_CFG_SECTION, 0, CFG_TYPE_BOO
|
||||
"manage the progress of ongoing operations. lvmpolld can be used as\n"
|
||||
"a native systemd service, which allows it to be started on demand,\n"
|
||||
"and to use its own control group. When this option is disabled, LVM\n"
|
||||
"commands will supervise long running operations by forking themselves.\n")
|
||||
"commands will supervise long running operations by forking themselves.\n"
|
||||
"Applicable only if LVM is compiled with lvmpolld support.\n")
|
||||
|
||||
cfg(global_notify_dbus_CFG, "notify_dbus", global_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_NOTIFY_DBUS, vsn(2, 2, 145), NULL, 0, NULL,
|
||||
"Enable D-Bus notification from LVM commands.\n"
|
||||
"When enabled, an LVM command that changes PVs, changes VG metadata,\n"
|
||||
"or changes the activation state of an LV will send a notification.\n")
|
||||
|
||||
cfg(activation_udev_sync_CFG, "udev_sync", activation_CFG_SECTION, 0, CFG_TYPE_BOOL, DEFAULT_UDEV_SYNC, vsn(2, 2, 51), NULL, 0, NULL,
|
||||
"Use udev notifications to synchronize udev and LVM.\n"
|
||||
@@ -1360,6 +1428,18 @@ cfg(metadata_check_pv_device_sizes_CFG, "check_pv_device_sizes", metadata_CFG_SE
|
||||
"less than corresponding PV size. You should not disable this unless\n"
|
||||
"you are absolutely sure about what you are doing!\n")
|
||||
|
||||
cfg(metadata_record_lvs_history_CFG, "record_lvs_history", metadata_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_RECORD_LVS_HISTORY, vsn(2, 2, 145), NULL, 0, NULL,
|
||||
"When enabled, LVM keeps history records about removed LVs in\n"
|
||||
"metadata. The information that is recorded in metadata for\n"
|
||||
"historical LVs is reduced when compared to original\n"
|
||||
"information kept in metadata for live LVs. Currently, this\n"
|
||||
"feature is supported for thin and thin snapshot LVs only.\n")
|
||||
|
||||
cfg(metadata_lvs_history_retention_time_CFG, "lvs_history_retention_time", metadata_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_LVS_HISTORY_RETENTION_TIME, vsn(2, 2, 145), NULL, 0, NULL,
|
||||
"Retention time in seconds after which a record about individual\n"
|
||||
"historical logical volume is automatically destroyed.\n"
|
||||
"A value of 0 disables this feature.\n")
|
||||
|
||||
cfg(metadata_pvmetadatacopies_CFG, "pvmetadatacopies", metadata_CFG_SECTION, CFG_ADVANCED | CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_PVMETADATACOPIES, vsn(1, 0, 0), NULL, 0, NULL,
|
||||
"Number of copies of metadata to store on each PV.\n"
|
||||
"The --pvmetadatacopies option overrides this setting.\n"
|
||||
@@ -1423,6 +1503,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_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"
|
||||
@@ -1679,9 +1773,53 @@ 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")
|
||||
|
||||
cfg(report_two_word_unknown_device_CFG, "two_word_unknown_device", report_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, 0, vsn(2, 2, 146), NULL, 0, NULL,
|
||||
"Use the two words 'unknown device' in place of '[unknown]'.\n"
|
||||
"This is displayed when the device for a PV is not known.\n")
|
||||
|
||||
cfg(dmeventd_mirror_library_CFG, "mirror_library", dmeventd_CFG_SECTION, 0, CFG_TYPE_STRING, DEFAULT_DMEVENTD_MIRROR_LIB, vsn(1, 2, 3), NULL, 0, NULL,
|
||||
"The library dmeventd uses when monitoring a mirror device.\n"
|
||||
"libdevmapper-event-lvm2mirror.so attempts to recover from\n"
|
||||
@@ -1758,6 +1896,7 @@ cfg_array(local_extra_system_ids_CFG, "extra_system_ids", local_CFG_SECTION, CFG
|
||||
|
||||
cfg(local_host_id_CFG, "host_id", local_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, 0, vsn(2, 2, 124), NULL, 0, NULL,
|
||||
"The lvmlockd sanlock host_id.\n"
|
||||
"This must be unique among all hosts, and must be between 1 and 2000.\n")
|
||||
"This must be unique among all hosts, and must be between 1 and 2000.\n"
|
||||
"Applicable only if LVM is compiled with lockd support\n")
|
||||
|
||||
cfg(CFG_COUNT, NULL, root_CFG_SECTION, 0, CFG_TYPE_INT, 0, vsn(0, 0, 0), NULL, 0, NULL, NULL)
|
||||
|
||||
@@ -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
|
||||
@@ -52,10 +53,12 @@
|
||||
#define DEFAULT_FALLBACK_TO_CLUSTERED_LOCKING 1
|
||||
#define DEFAULT_WAIT_FOR_LOCKS 1
|
||||
#define DEFAULT_LVMLOCKD_LOCK_RETRIES 3
|
||||
#define DEFAULT_LVMETAD_UPDATE_WAIT_TIME 10
|
||||
#define DEFAULT_PRIORITISE_WRITE_LOCKS 1
|
||||
#define DEFAULT_USE_MLOCKALL 0
|
||||
#define DEFAULT_METADATA_READ_ONLY 0
|
||||
#define DEFAULT_LVDISPLAY_SHOWS_FULL_DEVICE_PATH 0
|
||||
#define DEFAULT_UNKNOWN_DEVICE_NAME "[unknown]"
|
||||
|
||||
#define DEFAULT_SANLOCK_LV_EXTEND_MB 256
|
||||
|
||||
@@ -63,7 +66,8 @@
|
||||
#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
|
||||
// FIXME Increase this to 64
|
||||
#define DEFAULT_RAID_MAX_IMAGES 8 /* limited by kernel failed devices bitfield in superblock (raid4/5/6 max 253) */
|
||||
|
||||
#define DEFAULT_RAID_FAULT_POLICY "warn"
|
||||
|
||||
@@ -127,6 +131,8 @@
|
||||
#define DEFAULT_FORMAT "lvm2"
|
||||
|
||||
#define DEFAULT_STRIPESIZE 64 /* KB */
|
||||
#define DEFAULT_RECORD_LVS_HISTORY 0
|
||||
#define DEFAULT_LVS_HISTORY_RETENTION_TIME 0
|
||||
#define DEFAULT_PVMETADATAIGNORE 0
|
||||
#define DEFAULT_PVMETADATASIZE 255
|
||||
#define DEFAULT_PVMETADATACOPIES 1
|
||||
@@ -135,6 +141,7 @@
|
||||
#define DEFAULT_READ_AHEAD "auto"
|
||||
#define DEFAULT_UDEV_RULES 1
|
||||
#define DEFAULT_UDEV_SYNC 1
|
||||
#define DEFAULT_NOTIFY_DBUS 1
|
||||
#define DEFAULT_VERIFY_UDEV_OPERATIONS 0
|
||||
#define DEFAULT_RETRY_DEACTIVATION 1
|
||||
#define DEFAULT_ACTIVATION_CHECKS 0
|
||||
@@ -154,6 +161,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
|
||||
@@ -201,14 +209,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"
|
||||
@@ -217,12 +229,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"
|
||||
@@ -231,6 +256,4 @@
|
||||
#define DEFAULT_THIN_POOL_AUTOEXTEND_THRESHOLD 100
|
||||
#define DEFAULT_THIN_POOL_AUTOEXTEND_PERCENT 20
|
||||
|
||||
#define DEFAULT_CY_LOCK_TYPE "sanlock"
|
||||
|
||||
#endif /* _LVM_DEFAULTS_H */
|
||||
|
||||
@@ -92,7 +92,15 @@ void str_list_del(struct dm_list *sll, const char *str)
|
||||
|
||||
dm_list_iterate_safe(slh, slht, sll)
|
||||
if (!strcmp(str, dm_list_item(slh, struct dm_str_list)->str))
|
||||
dm_list_del(slh);
|
||||
dm_list_del(slh);
|
||||
}
|
||||
|
||||
void str_list_wipe(struct dm_list *sll)
|
||||
{
|
||||
struct dm_list *slh, *slht;
|
||||
|
||||
dm_list_iterate_safe(slh, slht, sll)
|
||||
dm_list_del(slh);
|
||||
}
|
||||
|
||||
int str_list_dup(struct dm_pool *mem, struct dm_list *sllnew,
|
||||
@@ -118,8 +126,8 @@ int str_list_match_item(const struct dm_list *sll, const char *str)
|
||||
struct dm_str_list *sl;
|
||||
|
||||
dm_list_iterate_items(sl, sll)
|
||||
if (!strcmp(str, sl->str))
|
||||
return 1;
|
||||
if (!strcmp(str, sl->str))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -153,8 +161,8 @@ int str_list_lists_equal(const struct dm_list *sll, const struct dm_list *sll2)
|
||||
return 0;
|
||||
|
||||
dm_list_iterate_items(sl, sll)
|
||||
if (!str_list_match_item(sll2, sl->str))
|
||||
return 0;
|
||||
if (!str_list_match_item(sll2, sl->str))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ int str_list_add_list(struct dm_pool *mem, struct dm_list *sll, struct dm_list *
|
||||
int str_list_add_no_dup_check(struct dm_pool *mem, struct dm_list *sll, const char *str);
|
||||
int str_list_add_h_no_dup_check(struct dm_pool *mem, struct dm_list *sll, const char *str);
|
||||
void str_list_del(struct dm_list *sll, const char *str);
|
||||
void str_list_wipe(struct dm_list *sll);
|
||||
int str_list_match_item(const struct dm_list *sll, const char *str);
|
||||
int str_list_match_list(const struct dm_list *sll, const struct dm_list *sll2, const char **tag_matched);
|
||||
int str_list_lists_equal(const struct dm_list *sll, const struct dm_list *sll2);
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include "btree.h"
|
||||
#include "config.h"
|
||||
#include "toolcontext.h"
|
||||
#include "dm-ioctl.h" /* for DM_UUID_LEN */
|
||||
#include "lvm-string.h" /* for LVM's UUID_PREFIX */
|
||||
|
||||
#ifdef UDEV_SYNC_SUPPORT
|
||||
#include <libudev.h>
|
||||
@@ -38,6 +40,9 @@ struct dir_list {
|
||||
static struct {
|
||||
struct dm_pool *mem;
|
||||
struct dm_hash_table *names;
|
||||
struct dm_hash_table *vgid_index;
|
||||
struct dm_hash_table *lvid_index;
|
||||
struct btree *sysfs_only_devices; /* see comments in _get_device_for_sysfs_dev_name_using_devno */
|
||||
struct btree *devices;
|
||||
struct dm_regex *preferred_names_matcher;
|
||||
const char *dev_dir;
|
||||
@@ -358,6 +363,312 @@ static int _add_alias(struct device *dev, const char *path)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _get_sysfs_value(const char *path, char *buf, size_t buf_size, int error_if_no_value)
|
||||
{
|
||||
FILE *fp;
|
||||
size_t len;
|
||||
int r = 0;
|
||||
|
||||
if (!(fp = fopen(path, "r"))) {
|
||||
log_sys_error("fopen", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!fgets(buf, buf_size, fp)) {
|
||||
log_sys_error("fgets", path);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((len = strlen(buf)) && buf[len - 1] == '\n')
|
||||
buf[--len] = '\0';
|
||||
|
||||
if (!len && error_if_no_value)
|
||||
log_error("_get_sysfs_value: %s: no value", path);
|
||||
else
|
||||
r = 1;
|
||||
out:
|
||||
if (fclose(fp))
|
||||
log_sys_error("fclose", path);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _get_dm_uuid_from_sysfs(char *buf, size_t buf_size, int major, int minor)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%sdev/block/%d:%d/dm/uuid", dm_sysfs_dir(), major, minor) < 0) {
|
||||
log_error("%d:%d: dm_snprintf failed for path to sysfs dm directory.", major, minor);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _get_sysfs_value(path, buf, buf_size, 0);
|
||||
}
|
||||
|
||||
static struct dm_list *_get_or_add_list_by_index_key(struct dm_hash_table *idx, const char *key)
|
||||
{
|
||||
struct dm_list *list;
|
||||
|
||||
if ((list = dm_hash_lookup(idx, key)))
|
||||
return list;
|
||||
|
||||
if (!(list = _zalloc(sizeof(*list)))) {
|
||||
log_error("%s: failed to allocate device list for device cache index.", key);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dm_list_init(list);
|
||||
|
||||
if (!dm_hash_insert(idx, key, list)) {
|
||||
log_error("%s: failed to insert device list to device cache index.", key);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static struct device *_insert_sysfs_dev(dev_t devno, const char *devname)
|
||||
{
|
||||
static struct device _fake_dev = { .flags = DEV_USED_FOR_LV };
|
||||
struct stat stat0;
|
||||
char path[PATH_MAX];
|
||||
char *path_copy;
|
||||
struct device *dev;
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%s%s", _cache.dev_dir, devname) < 0) {
|
||||
log_error("_insert_sysfs_dev: %s: dm_snprintf failed", devname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (lstat(path, &stat0) < 0) {
|
||||
/* When device node does not exist return fake entry.
|
||||
* This may happen when i.e. lvm2 device dir != /dev */
|
||||
log_debug("%s: Not available device node", path);
|
||||
return &_fake_dev;
|
||||
}
|
||||
|
||||
if (!(dev = _dev_create(devno)))
|
||||
return_NULL;
|
||||
|
||||
if (!(path_copy = dm_pool_strdup(_cache.mem, path))) {
|
||||
log_error("_insert_sysfs_dev: %s: dm_pool_strdup failed", devname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!_add_alias(dev, path_copy)) {
|
||||
log_error("Couldn't add alias to dev cache.");
|
||||
_free(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!btree_insert(_cache.sysfs_only_devices, (uint32_t) devno, dev)) {
|
||||
log_error("Couldn't add device to binary tree of sysfs-only devices in dev cache.");
|
||||
_free(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static struct device *_get_device_for_sysfs_dev_name_using_devno(const char *devname)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char buf[PATH_MAX];
|
||||
int major, minor;
|
||||
dev_t devno;
|
||||
struct device *dev;
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%sblock/%s/dev", dm_sysfs_dir(), devname) < 0) {
|
||||
log_error("_get_device_for_sysfs_dev_name_using_devno: %s: dm_snprintf failed", devname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!_get_sysfs_value(path, buf, sizeof(buf), 1))
|
||||
return_NULL;
|
||||
|
||||
if (sscanf(buf, "%d:%d", &major, &minor) != 2) {
|
||||
log_error("_get_device_for_sysfs_dev_name_using_devno: %s: failed to get major and minor number", devname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
devno = MKDEV((dev_t)major, (dev_t)minor);
|
||||
if (!(dev = (struct device *) btree_lookup(_cache.devices, (uint32_t) devno))) {
|
||||
/*
|
||||
* If we get here, it means the device is referenced in sysfs, but it's not yet in /dev.
|
||||
* This may happen in some rare cases right after LVs get created - we sync with udev
|
||||
* (or alternatively we create /dev content ourselves) while VG lock is held. However,
|
||||
* dev scan is done without VG lock so devices may already be in sysfs, but /dev may
|
||||
* not be updated yet if we call LVM command right after LV creation. This is not a
|
||||
* problem with devtmpfs as there's at least kernel name for device in /dev as soon
|
||||
* as the sysfs item exists, but we still support environments without devtmpfs or
|
||||
* where different directory for dev nodes is used (e.g. our test suite). So track
|
||||
* such devices in _cache.sysfs_only_devices hash for the vgid/lvid check to work still.
|
||||
*/
|
||||
if (!(dev = (struct device *) btree_lookup(_cache.sysfs_only_devices, (uint32_t) devno)) &&
|
||||
!(dev = _insert_sysfs_dev(devno, devname)))
|
||||
return_NULL;
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
#define NOT_LVM_UUID "-"
|
||||
|
||||
static int _get_vgid_and_lvid_for_dev(struct device *dev)
|
||||
{
|
||||
static size_t lvm_prefix_len = sizeof(UUID_PREFIX) - 1;
|
||||
static size_t lvm_uuid_len = sizeof(UUID_PREFIX) - 1 + 2 * ID_LEN;
|
||||
char uuid[DM_UUID_LEN];
|
||||
size_t uuid_len;
|
||||
|
||||
if (!_get_dm_uuid_from_sysfs(uuid, sizeof(uuid), (int) MAJOR(dev->dev), (int) MINOR(dev->dev)))
|
||||
return_0;
|
||||
|
||||
uuid_len = strlen(uuid);
|
||||
|
||||
/*
|
||||
* UUID for LV is either "LVM-<vg_uuid><lv_uuid>" or "LVM-<vg_uuid><lv_uuid>-<suffix>",
|
||||
* where vg_uuid and lv_uuid has length of ID_LEN and suffix len is not restricted
|
||||
* (only restricted by whole DM UUID max len).
|
||||
*/
|
||||
if (((uuid_len == lvm_uuid_len) ||
|
||||
((uuid_len > lvm_uuid_len) && (uuid[lvm_uuid_len] == '-'))) &&
|
||||
!strncmp(uuid, UUID_PREFIX, lvm_prefix_len)) {
|
||||
/* Separate VGID and LVID part from DM UUID. */
|
||||
if (!(dev->vgid = dm_pool_strndup(_cache.mem, uuid + lvm_prefix_len, ID_LEN)) ||
|
||||
!(dev->lvid = dm_pool_strndup(_cache.mem, uuid + lvm_prefix_len + ID_LEN, ID_LEN)))
|
||||
return_0;
|
||||
} else
|
||||
dev->vgid = dev->lvid = NOT_LVM_UUID;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _index_dev_by_vgid_and_lvid(struct device *dev)
|
||||
{
|
||||
const char *devname = dev_name(dev);
|
||||
char devpath[PATH_MAX];
|
||||
char path[PATH_MAX];
|
||||
DIR *d;
|
||||
struct dirent *dirent;
|
||||
struct device *holder_dev;
|
||||
struct dm_list *vgid_list, *lvid_list;
|
||||
struct device_list *dl_vgid, *dl_lvid;
|
||||
int r = 0;
|
||||
|
||||
if (dev->flags & DEV_USED_FOR_LV)
|
||||
/* already indexed */
|
||||
return 1;
|
||||
|
||||
/* Get holders for device. */
|
||||
if (dm_snprintf(path, sizeof(path), "%sdev/block/%d:%d/holders/", dm_sysfs_dir(), (int) MAJOR(dev->dev), (int) MINOR(dev->dev)) < 0) {
|
||||
log_error("%s: dm_snprintf failed for path to holders directory.", devname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(d = opendir(path))) {
|
||||
if (errno == ENOENT) {
|
||||
log_debug("%s: path does not exist, skipping", path);
|
||||
return 1;
|
||||
}
|
||||
log_sys_error("opendir", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Iterate over device's holders and look for LVs. */
|
||||
while ((dirent = readdir(d))) {
|
||||
if (!strcmp(".", dirent->d_name) ||
|
||||
!strcmp("..", dirent->d_name))
|
||||
continue;
|
||||
|
||||
if (dm_snprintf(devpath, sizeof(devpath), "%s%s", _cache.dev_dir, dirent->d_name) == -1) {
|
||||
log_error("%s: dm_snprintf failed for holder %s device path.", devname, dirent->d_name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(holder_dev = (struct device *) dm_hash_lookup(_cache.names, devpath))) {
|
||||
/*
|
||||
* Cope with situation where canonical /<dev_dir>/<dirent->d_name>
|
||||
* does not exist, but some other node name or symlink exists in
|
||||
* non-standard environments - someone renaming the nodes or using
|
||||
* mknod with different dev names than actual kernel names.
|
||||
* This looks up struct device by major:minor pair which we get
|
||||
* by looking at /sys/block/<dirent->d_name>/dev sysfs attribute.
|
||||
*/
|
||||
if (!(holder_dev = _get_device_for_sysfs_dev_name_using_devno(dirent->d_name))) {
|
||||
log_error("%s: failed to find associated device structure for holder %s.", devname, devpath);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* We're only interested in a holder which is a DM device. */
|
||||
if (!dm_is_dm_major(MAJOR(holder_dev->dev)))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* And if it's a DM device, we're only interested in a holder which is an LVM device.
|
||||
* Get the VG UUID and LV UUID if we don't have that already.
|
||||
*/
|
||||
if (!holder_dev->vgid && !_get_vgid_and_lvid_for_dev(holder_dev))
|
||||
goto_out;
|
||||
|
||||
if (*holder_dev->vgid == *NOT_LVM_UUID)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Do not add internal LV devices to index.
|
||||
* If a device is internal, the holder has the same VG UUID as the device.
|
||||
*/
|
||||
if (dm_is_dm_major(MAJOR(dev->dev))) {
|
||||
if (!dev->vgid && !_get_vgid_and_lvid_for_dev(dev))
|
||||
goto_out;
|
||||
|
||||
if (*dev->vgid != *NOT_LVM_UUID && !strcmp(holder_dev->vgid, dev->vgid))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(vgid_list = _get_or_add_list_by_index_key(_cache.vgid_index, holder_dev->vgid)) ||
|
||||
!(lvid_list = _get_or_add_list_by_index_key(_cache.lvid_index, holder_dev->lvid)))
|
||||
goto_out;
|
||||
|
||||
/* Create dev list items for the holder device. */
|
||||
if (!(dl_vgid = _zalloc(sizeof(*dl_vgid))) ||
|
||||
!(dl_lvid = _zalloc(sizeof(*dl_lvid)))) {
|
||||
log_error("%s: failed to allocate dev list item.", devname);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dl_vgid->dev = dl_lvid->dev = dev;
|
||||
|
||||
/* Add dev list item to VGID device list if it's not there already. */
|
||||
if (!(dev->flags & DEV_USED_FOR_LV))
|
||||
dm_list_add(vgid_list, &dl_vgid->list);
|
||||
|
||||
/* Add dev list item to LVID device list. */
|
||||
dm_list_add(lvid_list, &dl_lvid->list);
|
||||
|
||||
/* Mark device as used == also indexed in dev cache by VGID and LVID. */
|
||||
dev->flags |= DEV_USED_FOR_LV;
|
||||
}
|
||||
|
||||
r = 1;
|
||||
out:
|
||||
if (closedir(d))
|
||||
log_sys_error("closedir", path);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
struct dm_list *dev_cache_get_dev_list_for_vgid(const char *vgid)
|
||||
{
|
||||
return dm_hash_lookup(_cache.vgid_index, vgid);
|
||||
}
|
||||
|
||||
struct dm_list *dev_cache_get_dev_list_for_lvid(const char *lvid)
|
||||
{
|
||||
return dm_hash_lookup(_cache.lvid_index, lvid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Either creates a new dev, or adds an alias to
|
||||
* an existing dev.
|
||||
@@ -378,14 +689,15 @@ static int _insert_dev(const char *path, dev_t d)
|
||||
}
|
||||
|
||||
/* is this device already registered ? */
|
||||
if (!(dev = (struct device *) btree_lookup(_cache.devices,
|
||||
(uint32_t) d))) {
|
||||
/* create new device */
|
||||
if (loopfile) {
|
||||
if (!(dev = dev_create_file(path, NULL, NULL, 0)))
|
||||
if (!(dev = (struct device *) btree_lookup(_cache.devices, (uint32_t) d))) {
|
||||
if (!(dev = (struct device *) btree_lookup(_cache.sysfs_only_devices, (uint32_t) d))) {
|
||||
/* create new device */
|
||||
if (loopfile) {
|
||||
if (!(dev = dev_create_file(path, NULL, NULL, 0)))
|
||||
return_0;
|
||||
} else if (!(dev = _dev_create(d)))
|
||||
return_0;
|
||||
} else if (!(dev = _dev_create(d)))
|
||||
return_0;
|
||||
}
|
||||
|
||||
if (!(btree_insert(_cache.devices, (uint32_t) d, dev))) {
|
||||
log_error("Couldn't insert device into binary tree.");
|
||||
@@ -493,6 +805,108 @@ static int _insert_file(const char *path)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int _dev_cache_iterate_devs_for_index(void)
|
||||
{
|
||||
struct btree_iter *iter = btree_first(_cache.devices);
|
||||
struct device *dev;
|
||||
int r = 1;
|
||||
|
||||
while (iter) {
|
||||
dev = btree_get_data(iter);
|
||||
|
||||
if (!_index_dev_by_vgid_and_lvid(dev))
|
||||
r = 0;
|
||||
|
||||
iter = btree_next(iter);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int _dev_cache_iterate_sysfs_for_index(const char *path)
|
||||
{
|
||||
char devname[PATH_MAX];
|
||||
DIR *d;
|
||||
struct dirent *dirent;
|
||||
int major, minor;
|
||||
dev_t devno;
|
||||
struct device *dev;
|
||||
int partial_failure = 0;
|
||||
int r = 0;
|
||||
|
||||
if (!(d = opendir(path))) {
|
||||
log_sys_error("opendir", path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((dirent = readdir(d))) {
|
||||
if (!strcmp(".", dirent->d_name) ||
|
||||
!strcmp("..", dirent->d_name))
|
||||
continue;
|
||||
|
||||
if (sscanf(dirent->d_name, "%d:%d", &major, &minor) != 2) {
|
||||
log_error("_dev_cache_iterate_sysfs_for_index: %s: failed "
|
||||
"to get major and minor number", dirent->d_name);
|
||||
partial_failure = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
devno = MKDEV((dev_t)major, (dev_t)minor);
|
||||
if (!(dev = (struct device *) btree_lookup(_cache.devices, (uint32_t) devno)) &&
|
||||
!(dev = (struct device *) btree_lookup(_cache.sysfs_only_devices, (uint32_t) devno))) {
|
||||
if (!dm_device_get_name(major, minor, 1, devname, sizeof(devname)) ||
|
||||
!(dev = _insert_sysfs_dev(devno, devname))) {
|
||||
partial_failure = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_index_dev_by_vgid_and_lvid(dev))
|
||||
partial_failure = 1;
|
||||
}
|
||||
|
||||
r = !partial_failure;
|
||||
|
||||
if (closedir(d))
|
||||
log_sys_error("closedir", path);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int dev_cache_index_devs(void)
|
||||
{
|
||||
static int sysfs_has_dev_block = -1;
|
||||
char path[PATH_MAX];
|
||||
|
||||
if (dm_snprintf(path, sizeof(path), "%sdev/block", dm_sysfs_dir()) < 0) {
|
||||
log_error("dev_cache_index_devs: dm_snprintf failed.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Skip indexing if /sys/dev/block is not available.*/
|
||||
if (sysfs_has_dev_block == -1) {
|
||||
struct stat info;
|
||||
if (stat(path, &info) == 0)
|
||||
sysfs_has_dev_block = 1;
|
||||
else {
|
||||
if (errno == ENOENT) {
|
||||
sysfs_has_dev_block = 0;
|
||||
return 1;
|
||||
} else {
|
||||
log_sys_error("stat", path);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} else if (!sysfs_has_dev_block)
|
||||
return 1;
|
||||
|
||||
if (obtain_device_list_from_udev() &&
|
||||
udev_get_library_context())
|
||||
return _dev_cache_iterate_devs_for_index(); /* with udev */
|
||||
|
||||
return _dev_cache_iterate_sysfs_for_index(path);
|
||||
}
|
||||
|
||||
#ifdef UDEV_SYNC_SUPPORT
|
||||
|
||||
static int _device_in_udev_db(const dev_t d)
|
||||
@@ -663,6 +1077,8 @@ static void _full_scan(int dev_scan)
|
||||
|
||||
_insert_dirs(&_cache.dirs);
|
||||
|
||||
(void) dev_cache_index_devs();
|
||||
|
||||
dm_list_iterate_items(dl, &_cache.files)
|
||||
_insert_file(dl->dir);
|
||||
|
||||
@@ -751,7 +1167,9 @@ int dev_cache_init(struct cmd_context *cmd)
|
||||
if (!(_cache.mem = dm_pool_create("dev_cache", 10 * 1024)))
|
||||
return_0;
|
||||
|
||||
if (!(_cache.names = dm_hash_create(128))) {
|
||||
if (!(_cache.names = dm_hash_create(128)) ||
|
||||
!(_cache.vgid_index = dm_hash_create(32)) ||
|
||||
!(_cache.lvid_index = dm_hash_create(32))) {
|
||||
dm_pool_destroy(_cache.mem);
|
||||
_cache.mem = 0;
|
||||
return_0;
|
||||
@@ -762,6 +1180,11 @@ int dev_cache_init(struct cmd_context *cmd)
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(_cache.sysfs_only_devices = btree_create(_cache.mem))) {
|
||||
log_error("Couldn't create binary tree for sysfs-only devices in dev cache.");
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!(_cache.dev_dir = _strdup(cmd->dev_dir))) {
|
||||
log_error("strdup dev_dir failed.");
|
||||
goto bad;
|
||||
@@ -825,6 +1248,12 @@ int dev_cache_exit(void)
|
||||
if (_cache.names)
|
||||
dm_hash_destroy(_cache.names);
|
||||
|
||||
if (_cache.vgid_index)
|
||||
dm_hash_destroy(_cache.vgid_index);
|
||||
|
||||
if (_cache.lvid_index)
|
||||
dm_hash_destroy(_cache.lvid_index);
|
||||
|
||||
memset(&_cache, 0, sizeof(_cache));
|
||||
|
||||
return (!num_open);
|
||||
@@ -1108,5 +1537,5 @@ int dev_fd(struct device *dev)
|
||||
const char *dev_name(const struct device *dev)
|
||||
{
|
||||
return (dev && dev->aliases.n) ? dm_list_item(dev->aliases.n, struct dm_str_list)->str :
|
||||
"unknown device";
|
||||
unknown_device_name();
|
||||
}
|
||||
|
||||
@@ -31,6 +31,10 @@ struct dev_filter {
|
||||
unsigned use_count;
|
||||
};
|
||||
|
||||
int dev_cache_index_devs(void);
|
||||
struct dm_list *dev_cache_get_dev_list_for_vgid(const char *vgid);
|
||||
struct dm_list *dev_cache_get_dev_list_for_lvid(const char *lvid);
|
||||
|
||||
/*
|
||||
* The global device cache.
|
||||
*/
|
||||
|
||||
@@ -32,7 +32,9 @@
|
||||
#define DEV_EXT_UDEV_BLKID_TYPE_RAID_SUFFIX "_raid_member"
|
||||
#define DEV_EXT_UDEV_BLKID_TYPE_SW_RAID "linux_raid_member"
|
||||
#define DEV_EXT_UDEV_BLKID_PART_TABLE_TYPE "ID_PART_TABLE_TYPE"
|
||||
#define DEV_EXT_UDEV_BLKID_PART_ENTRY_DISK "ID_PART_ENTRY_DISK"
|
||||
|
||||
#define DEV_EXT_UDEV_DEVTYPE "DEVTYPE"
|
||||
#define DEV_EXT_UDEV_DEVTYPE_DISK "disk"
|
||||
|
||||
/*
|
||||
* DEV_EXT_UDEV_MPATH_DEVICE_PATH is set by multipath in udev db
|
||||
|
||||
@@ -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;
|
||||
@@ -513,6 +524,8 @@ int dev_open_flags(struct device *dev, int flags, int direct, int quiet)
|
||||
log_sys_debug("open", name);
|
||||
else
|
||||
log_sys_error("open", name);
|
||||
|
||||
dev->flags |= DEV_OPEN_FAILURE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -556,6 +569,7 @@ int dev_open_flags(struct device *dev, int flags, int direct, int quiet)
|
||||
dev->flags & DEV_OPENED_EXCL ? " O_EXCL" : "",
|
||||
dev->flags & DEV_O_DIRECT ? " O_DIRECT" : "");
|
||||
|
||||
dev->flags &= ~DEV_OPEN_FAILURE;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,8 @@ static int _dev_has_md_magic(struct device *dev, uint64_t sb_offset)
|
||||
|
||||
/* Version 1 is little endian; version 0.90.0 is machine endian */
|
||||
if (dev_read(dev, sb_offset, sizeof(uint32_t), &md_magic) &&
|
||||
((md_magic == xlate32(MD_SB_MAGIC)) ||
|
||||
(md_magic == MD_SB_MAGIC)))
|
||||
((md_magic == MD_SB_MAGIC) ||
|
||||
((MD_SB_MAGIC != xlate32(MD_SB_MAGIC)) && (md_magic == xlate32(MD_SB_MAGIC)))))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -333,23 +376,43 @@ static int _has_partition_table(struct device *dev)
|
||||
}
|
||||
|
||||
#ifdef UDEV_SYNC_SUPPORT
|
||||
static int _udev_dev_is_partitioned(struct device *dev)
|
||||
static int _udev_dev_is_partitioned(struct dev_types *dt, struct device *dev)
|
||||
{
|
||||
struct dev_ext *ext;
|
||||
struct udev_device *device;
|
||||
const char *value;
|
||||
|
||||
if (!(ext = dev_ext_get(dev)))
|
||||
return_0;
|
||||
|
||||
if (!udev_device_get_property_value((struct udev_device *)ext->handle, DEV_EXT_UDEV_BLKID_PART_TABLE_TYPE))
|
||||
device = (struct udev_device *) ext->handle;
|
||||
if (!(value = udev_device_get_property_value(device, DEV_EXT_UDEV_BLKID_PART_TABLE_TYPE)))
|
||||
return 0;
|
||||
|
||||
if (udev_device_get_property_value((struct udev_device *)ext->handle, DEV_EXT_UDEV_BLKID_PART_ENTRY_DISK))
|
||||
return 0;
|
||||
/*
|
||||
* Device-mapper devices have DEV_EXT_UDEV_BLKID_PART_TABLE_TYPE
|
||||
* variable set if there's partition table found on whole device.
|
||||
* Partitions do not have this variable set - it's enough to use
|
||||
* only this variable to decide whether this device has partition
|
||||
* table on it.
|
||||
*/
|
||||
if (MAJOR(dev->dev) == dt->device_mapper_major)
|
||||
return 1;
|
||||
|
||||
return 1;
|
||||
/*
|
||||
* Other devices have DEV_EXT_UDEV_BLKID_PART_TABLE_TYPE set for
|
||||
* *both* whole device and partitions. We need to look at the
|
||||
* DEV_EXT_UDEV_DEVTYPE in addition to decide - whole device
|
||||
* with partition table on it has this variable set to
|
||||
* DEV_EXT_UDEV_DEVTYPE_DISK.
|
||||
*/
|
||||
if (!(value = udev_device_get_property_value(device, DEV_EXT_UDEV_DEVTYPE)))
|
||||
return_0;
|
||||
|
||||
return !strcmp(value, DEV_EXT_UDEV_DEVTYPE_DISK);
|
||||
}
|
||||
#else
|
||||
static int _udev_dev_is_partitioned(struct device *dev)
|
||||
static int _udev_dev_is_partitioned(struct dev_types *dt, struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -386,7 +449,7 @@ int dev_is_partitioned(struct dev_types *dt, struct device *dev)
|
||||
return _native_dev_is_partitioned(dt, dev);
|
||||
|
||||
if (dev->ext.src == DEV_EXT_UDEV)
|
||||
return _udev_dev_is_partitioned(dev);
|
||||
return _udev_dev_is_partitioned(dt, dev);
|
||||
|
||||
log_error(INTERNAL_ERROR "Missing hook for partition table recognition "
|
||||
"using external device info source %s", dev_ext_name(dev));
|
||||
@@ -833,6 +896,12 @@ static unsigned long _dev_topology_attribute(struct dev_types *dt,
|
||||
|
||||
result = value >> SECTOR_SHIFT;
|
||||
|
||||
if (!result && value) {
|
||||
log_warn("WARNING: Device %s: %s is %lu and is unexpectedly less than sector.",
|
||||
dev_name(dev), attribute, value);
|
||||
result = 1;
|
||||
}
|
||||
|
||||
out_close:
|
||||
if (fclose(fp))
|
||||
log_sys_debug("fclose", path);
|
||||
|
||||
@@ -21,9 +21,7 @@
|
||||
#define NUMBER_OF_MAJORS 4096
|
||||
|
||||
#ifdef __linux__
|
||||
# define MAJOR(dev) ((dev & 0xfff00) >> 8)
|
||||
# define MINOR(dev) ((dev & 0xff) | ((dev >> 12) & 0xfff00))
|
||||
# define MKDEV(ma,mi) ((mi & 0xff) | (ma << 8) | ((mi & ~0xff) << 12))
|
||||
# include "kdev_t.h"
|
||||
#else
|
||||
# define MAJOR(x) major((x))
|
||||
# define MINOR(x) minor((x))
|
||||
@@ -45,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];
|
||||
};
|
||||
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
#define DEV_OPENED_EXCL 0x00000010 /* Opened EXCL */
|
||||
#define DEV_O_DIRECT 0x00000020 /* Use O_DIRECT */
|
||||
#define DEV_O_DIRECT_TESTED 0x00000040 /* DEV_O_DIRECT is reliable */
|
||||
#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.
|
||||
@@ -68,7 +72,10 @@ struct device {
|
||||
struct dm_list open_list;
|
||||
struct dev_ext ext;
|
||||
|
||||
char pvid[ID_LEN + 1];
|
||||
const char *vgid; /* if device is an LV */
|
||||
const char *lvid; /* if device is an LV */
|
||||
|
||||
char pvid[ID_LEN + 1]; /* if device is a PV */
|
||||
char _padding[7];
|
||||
};
|
||||
|
||||
|
||||
@@ -121,19 +121,29 @@ const char *get_percent_string(percent_type_t def)
|
||||
return _percent_types[def];
|
||||
}
|
||||
|
||||
static const char *_lv_name(const struct logical_volume *lv)
|
||||
{
|
||||
/* Never try to display names of the internal snapshot structures. */
|
||||
if (lv_is_snapshot(lv))
|
||||
return find_cow(lv)->name;
|
||||
|
||||
return lv->name;
|
||||
}
|
||||
|
||||
const char *display_lvname(const struct logical_volume *lv)
|
||||
{
|
||||
char *name;
|
||||
const char *lv_name = _lv_name(lv);
|
||||
int r;
|
||||
|
||||
if ((lv->vg->cmd->display_lvname_idx + NAME_LEN) >= sizeof((lv->vg->cmd->display_buffer)))
|
||||
lv->vg->cmd->display_lvname_idx = 0;
|
||||
|
||||
name = lv->vg->cmd->display_buffer + lv->vg->cmd->display_lvname_idx;
|
||||
r = dm_snprintf(name, NAME_LEN, "%s/%s", lv->vg->name, lv->name);
|
||||
r = dm_snprintf(name, NAME_LEN, "%s/%s", lv->vg->name, lv_name);
|
||||
|
||||
if (r < 0) {
|
||||
log_error("Full LV name \"%s/%s\" is too long.", lv->vg->name, lv->name);
|
||||
log_error("Full LV name \"%s/%s\" is too long.", lv->vg->name, lv_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -332,6 +342,34 @@ void lvdisplay_colons(const struct logical_volume *lv)
|
||||
inkernel ? info.major : -1, inkernel ? info.minor : -1);
|
||||
}
|
||||
|
||||
static int _lvdisplay_historical_full(struct cmd_context *cmd,
|
||||
const struct logical_volume *lv)
|
||||
{
|
||||
char uuid[64] __attribute__((aligned(8)));
|
||||
int lvm1compat = find_config_tree_bool(cmd, global_lvdisplay_shows_full_device_path_CFG, NULL);
|
||||
struct historical_logical_volume *hlv = lv->this_glv->historical;
|
||||
|
||||
if (!id_write_format(&hlv->lvid.id[1], uuid, sizeof(uuid)))
|
||||
return_0;
|
||||
|
||||
log_print("--- Historical Logical volume ---");
|
||||
|
||||
if (lvm1compat)
|
||||
/* /dev/vgname/lvname doen't actually exist for historical devices */
|
||||
log_print("LV Name %s%s/%s",
|
||||
hlv->vg->cmd->dev_dir, hlv->vg->name, hlv->name);
|
||||
else
|
||||
log_print("LV Name %s%s", HISTORICAL_LV_PREFIX, hlv->name);
|
||||
|
||||
log_print("VG Name %s", hlv->vg->name);
|
||||
log_print("LV UUID %s", uuid);
|
||||
log_print("LV Creation time %s", lv_creation_time_dup(cmd->mem, lv, 1));
|
||||
log_print("LV Removal time %s", lv_removal_time_dup(cmd->mem, lv, 1));
|
||||
|
||||
log_print(" ");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int lvdisplay_full(struct cmd_context *cmd,
|
||||
const struct logical_volume *lv,
|
||||
void *handle __attribute__((unused)))
|
||||
@@ -349,6 +387,9 @@ int lvdisplay_full(struct cmd_context *cmd,
|
||||
int thin_active = 0;
|
||||
dm_percent_t thin_percent;
|
||||
|
||||
if (lv_is_historical(lv))
|
||||
return _lvdisplay_historical_full(cmd, lv);
|
||||
|
||||
if (!id_write_format(&lv->lvid.id[1], uuid, sizeof(uuid)))
|
||||
return_0;
|
||||
|
||||
@@ -383,7 +424,7 @@ int lvdisplay_full(struct cmd_context *cmd,
|
||||
log_print("LV UUID %s", uuid);
|
||||
log_print("LV Write Access %s", access_str);
|
||||
log_print("LV Creation host, time %s, %s",
|
||||
lv_host_dup(cmd->mem, lv), lv_time_dup(cmd->mem, lv, 1));
|
||||
lv_host_dup(cmd->mem, lv), lv_creation_time_dup(cmd->mem, lv, 1));
|
||||
|
||||
if (lv_is_origin(lv)) {
|
||||
log_print("LV snapshot status source of");
|
||||
@@ -790,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;
|
||||
}
|
||||
|
||||
@@ -55,8 +55,8 @@ static int _errseg_target_present(struct cmd_context *cmd,
|
||||
/* Reported truncated in older kernels */
|
||||
if (!_errseg_checked) {
|
||||
_errseg_checked = 1;
|
||||
_errseg_present = target_present(cmd, "error", 0) ||
|
||||
target_present(cmd, "erro", 0);
|
||||
_errseg_present = target_present(cmd, TARGET_NAME_ERROR, 0) ||
|
||||
target_present(cmd, TARGET_NAME_ERROR_OLD, 0);
|
||||
}
|
||||
|
||||
return _errseg_present;
|
||||
@@ -66,7 +66,7 @@ static int _errseg_modules_needed(struct dm_pool *mem,
|
||||
const struct lv_segment *seg __attribute__((unused)),
|
||||
struct dm_list *modules)
|
||||
{
|
||||
if (!str_list_add(mem, modules, "error")) {
|
||||
if (!str_list_add(mem, modules, MODULE_NAME_ERROR)) {
|
||||
log_error("error module string list allocation failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -129,6 +129,8 @@ int persistent_filter_load(struct dev_filter *f, struct dm_config_tree **cft_out
|
||||
if (dm_hash_get_num_entries(pf->devices)) {
|
||||
/* We populated dev_cache ourselves */
|
||||
dev_cache_scan(0);
|
||||
if (!dev_cache_index_devs())
|
||||
stack;
|
||||
r = 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -126,15 +126,8 @@ static int _passes_usable_filter(struct dev_filter *f, struct device *dev)
|
||||
break;
|
||||
case FILTER_MODE_PRE_LVMETAD:
|
||||
ucp.check_empty = 1;
|
||||
/*
|
||||
* If we're scanning for lvmetad update,
|
||||
* we don't want to hang on blocked/suspended devices.
|
||||
* When the device is unblocked/resumed, surely,
|
||||
* there's going to be a CHANGE event so the device
|
||||
* gets scanned via udev rule anyway after resume.
|
||||
*/
|
||||
ucp.check_blocked = 1;
|
||||
ucp.check_suspended = 1;
|
||||
ucp.check_suspended = 0;
|
||||
ucp.check_error_target = 1;
|
||||
ucp.check_reserved = 1;
|
||||
break;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -345,10 +345,7 @@ static int _format1_pv_read(const struct format_type *fmt, const char *pv_name,
|
||||
}
|
||||
|
||||
static int _format1_pv_initialise(const struct format_type * fmt,
|
||||
int64_t label_sector __attribute__((unused)),
|
||||
unsigned long data_alignment __attribute__((unused)),
|
||||
unsigned long data_alignment_offset __attribute__((unused)),
|
||||
struct pvcreate_restorable_params *rp,
|
||||
struct pv_create_args *pva,
|
||||
struct physical_volume * pv)
|
||||
{
|
||||
if (pv->size > MAX_PV_SIZE)
|
||||
@@ -360,18 +357,18 @@ static int _format1_pv_initialise(const struct format_type * fmt,
|
||||
}
|
||||
|
||||
/* Nothing more to do if extent size isn't provided */
|
||||
if (!rp->extent_size)
|
||||
if (!pva->extent_size)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* This works out pe_start and pe_count.
|
||||
*/
|
||||
if (!calculate_extent_count(pv, rp->extent_size, rp->extent_count, rp->pe_start))
|
||||
if (!calculate_extent_count(pv, pva->extent_size, pva->extent_count, pva->pe_start))
|
||||
return_0;
|
||||
|
||||
/* Retain existing extent locations exactly */
|
||||
if (((rp->pe_start || rp->extent_count) && (rp->pe_start != pv->pe_start)) ||
|
||||
(rp->extent_count && (rp->extent_count != pv->pe_count))) {
|
||||
if (((pva->pe_start || pva->extent_count) && (pva->pe_start != pv->pe_start)) ||
|
||||
(pva->extent_count && (pva->extent_count != pv->pe_count))) {
|
||||
log_error("Metadata would overwrite physical extents");
|
||||
return 0;
|
||||
}
|
||||
@@ -383,16 +380,15 @@ static int _format1_pv_setup(const struct format_type *fmt,
|
||||
struct physical_volume *pv,
|
||||
struct volume_group *vg)
|
||||
{
|
||||
struct pvcreate_restorable_params rp = {.restorefile = NULL,
|
||||
.id = {{0}},
|
||||
.idp = NULL,
|
||||
.ba_start = 0,
|
||||
.ba_size = 0,
|
||||
.pe_start = 0,
|
||||
.extent_count = 0,
|
||||
.extent_size = vg->extent_size};
|
||||
struct pv_create_args pva = { .id = {{0}},
|
||||
.idp = NULL,
|
||||
.ba_start = 0,
|
||||
.ba_size = 0,
|
||||
.pe_start = 0,
|
||||
.extent_count = 0,
|
||||
.extent_size = vg->extent_size};
|
||||
|
||||
return _format1_pv_initialise(fmt, -1, 0, 0, &rp, pv);
|
||||
return _format1_pv_initialise(fmt, &pva, pv);
|
||||
}
|
||||
|
||||
static int _format1_lv_setup(struct format_instance *fid, struct logical_volume *lv)
|
||||
|
||||
@@ -80,7 +80,10 @@ static int _lvm1_read(struct labeller *l, struct device *dev, void *buf,
|
||||
*label = lvmcache_get_label(info);
|
||||
|
||||
lvmcache_set_device_size(info, ((uint64_t)xlate32(pvd->pv_size)) << SECTOR_SHIFT);
|
||||
lvmcache_set_ext_version(info, 0);
|
||||
lvmcache_set_ext_flags(info, 0);
|
||||
lvmcache_del_mdas(info);
|
||||
lvmcache_del_bas(info);
|
||||
lvmcache_make_valid(info);
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -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,
|
||||
@@ -104,7 +107,10 @@ int read_pool_label(struct pool_list *pl, struct labeller *l,
|
||||
*label = lvmcache_get_label(info);
|
||||
|
||||
lvmcache_set_device_size(info, ((uint64_t)xlate32_be(pd->pl_blocks)) << SECTOR_SHIFT);
|
||||
lvmcache_set_ext_version(info, 0);
|
||||
lvmcache_set_ext_flags(info, 0);
|
||||
lvmcache_del_mdas(info);
|
||||
lvmcache_del_bas(info);
|
||||
lvmcache_make_valid(info);
|
||||
|
||||
pl->dev = dev;
|
||||
|
||||
@@ -166,10 +166,7 @@ bad:
|
||||
}
|
||||
|
||||
static int _pool_pv_initialise(const struct format_type *fmt __attribute__((unused)),
|
||||
int64_t label_sector __attribute__((unused)),
|
||||
unsigned long data_alignment __attribute__((unused)),
|
||||
unsigned long data_alignment_offset __attribute__((unused)),
|
||||
struct pvcreate_restorable_params *rp __attribute__((unused)),
|
||||
struct pv_create_args *pva __attribute__((unused)),
|
||||
struct physical_volume *pv __attribute__((unused)))
|
||||
{
|
||||
return 1;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -329,27 +329,113 @@ struct volume_group *backup_read_vg(struct cmd_context *cmd,
|
||||
return vg;
|
||||
}
|
||||
|
||||
/* ORPHAN and VG locks held before calling this */
|
||||
int backup_restore_vg(struct cmd_context *cmd, struct volume_group *vg, int drop_lvmetad)
|
||||
static int _restore_vg_should_write_pv(struct physical_volume *pv, int do_pvcreate)
|
||||
{
|
||||
struct pv_list *pvl;
|
||||
struct lvmcache_info *info;
|
||||
|
||||
if (do_pvcreate)
|
||||
return 1;
|
||||
|
||||
if (!(pv->fmt->features & FMT_PV_FLAGS))
|
||||
return 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* We're restoring a VG and if the PV_EXT_USED
|
||||
* flag is not set yet in PV, we need to set it now!
|
||||
* This may happen if we have plain PVs without a VG
|
||||
* and we're restoring former VG from backup on top
|
||||
* of these PVs.
|
||||
*/
|
||||
if (!(lvmcache_ext_flags(info) & PV_EXT_USED))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ORPHAN and VG locks held before calling this */
|
||||
int backup_restore_vg(struct cmd_context *cmd, struct volume_group *vg,
|
||||
int do_pvcreate, struct pv_create_args *pva)
|
||||
{
|
||||
struct dm_list new_pvs;
|
||||
struct pv_list *pvl, *new_pvl;
|
||||
struct physical_volume *existing_pv, *pv;
|
||||
struct dm_list *pvs = &vg->pvs;
|
||||
struct format_instance *fid;
|
||||
struct format_instance_ctx fic;
|
||||
uint32_t tmp;
|
||||
int should_write_pv;
|
||||
uint32_t tmp_extent_size;
|
||||
|
||||
/*
|
||||
* FIXME: Check that the PVs referenced in the backup are
|
||||
* not members of other existing VGs.
|
||||
*/
|
||||
|
||||
/* Prepare new PVs if needed. */
|
||||
if (do_pvcreate) {
|
||||
dm_list_init(&new_pvs);
|
||||
|
||||
dm_list_iterate_items(pvl, &vg->pvs) {
|
||||
existing_pv = pvl->pv;
|
||||
|
||||
pva->id = existing_pv->id;
|
||||
pva->idp = &pva->id;
|
||||
pva->pe_start = pv_pe_start(existing_pv);
|
||||
pva->extent_count = pv_pe_count(existing_pv);
|
||||
pva->extent_size = pv_pe_size(existing_pv);
|
||||
/* pe_end = pv_pe_count(existing_pv) * pv_pe_size(existing_pv) + pe_start - 1 */
|
||||
|
||||
if (!(pv = pv_create(cmd, pv_dev(existing_pv), pva))) {
|
||||
log_error("Failed to setup physical volume \"%s\".",
|
||||
pv_dev_name(existing_pv));
|
||||
return 0;
|
||||
}
|
||||
pv->vg_name = vg->name;
|
||||
pv->vgid = vg->id;
|
||||
|
||||
if (!(new_pvl = dm_pool_zalloc(vg->vgmem, sizeof(*new_pvl)))) {
|
||||
log_error("Failed to allocate PV list item for \"%s\".",
|
||||
pv_dev_name(pvl->pv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
new_pvl->pv = pv;
|
||||
dm_list_add(&new_pvs, &new_pvl->list);
|
||||
|
||||
log_verbose("Set up physical volume for \"%s\" with %" PRIu64
|
||||
" available sectors.", pv_dev_name(pv), pv_size(pv));
|
||||
}
|
||||
|
||||
pvs = &new_pvs;
|
||||
}
|
||||
|
||||
/* Attempt to write out using currently active format */
|
||||
fic.type = FMT_INSTANCE_AUX_MDAS;
|
||||
fic.context.vg_ref.vg_name = vg->name;
|
||||
fic.context.vg_ref.vg_id = NULL;
|
||||
if (!(fid = cmd->fmt->ops->create_instance(cmd->fmt, &fic))) {
|
||||
log_error("Failed to allocate format instance");
|
||||
log_error("Failed to allocate format instance.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (do_pvcreate) {
|
||||
log_verbose("Deleting existing metadata for VG %s.", vg->name);
|
||||
if (!vg_remove_mdas(vg)) {
|
||||
cmd->fmt->ops->destroy_instance(fid);
|
||||
log_error("Removal of existing metadata for VG %s failed.", vg->name);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
vg_set_fid(vg, fid);
|
||||
|
||||
/*
|
||||
@@ -362,31 +448,63 @@ int backup_restore_vg(struct cmd_context *cmd, struct volume_group *vg, int drop
|
||||
}
|
||||
|
||||
/* Add any metadata areas on the PVs */
|
||||
dm_list_iterate_items(pvl, &vg->pvs) {
|
||||
tmp = vg->extent_size;
|
||||
dm_list_iterate_items(pvl, pvs) {
|
||||
if ((should_write_pv = _restore_vg_should_write_pv(pvl->pv, do_pvcreate)) < 0)
|
||||
return_0;
|
||||
|
||||
if (should_write_pv) {
|
||||
if (!(new_pvl = dm_pool_zalloc(vg->vgmem, sizeof(*new_pvl)))) {
|
||||
log_error("Failed to allocate structure for scheduled "
|
||||
"writing of PV '%s'.", pv_dev_name(pvl->pv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
new_pvl->pv = pvl->pv;
|
||||
dm_list_add(&vg->pv_write_list, &new_pvl->list);
|
||||
}
|
||||
|
||||
/* Add any metadata areas on the PV. */
|
||||
tmp_extent_size = vg->extent_size;
|
||||
vg->extent_size = 0;
|
||||
if (!vg->fid->fmt->ops->pv_setup(vg->fid->fmt, pvl->pv, vg)) {
|
||||
vg->extent_size = tmp;
|
||||
log_error("Format-specific setup for %s failed",
|
||||
vg->extent_size = tmp_extent_size;
|
||||
log_error("Format-specific setup for %s failed.",
|
||||
pv_dev_name(pvl->pv));
|
||||
return 0;
|
||||
}
|
||||
vg->extent_size = tmp;
|
||||
vg->extent_size = tmp_extent_size;
|
||||
}
|
||||
|
||||
if (do_pvcreate) {
|
||||
dm_list_iterate_items(pvl, &vg->pv_write_list) {
|
||||
struct device *dev = pv_dev(pvl->pv);
|
||||
const char *pv_name = dev_name(dev);
|
||||
|
||||
if (!label_remove(dev)) {
|
||||
log_error("Failed to wipe existing label on %s", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_verbose("Zeroing start of device %s", pv_name);
|
||||
if (!dev_open_quiet(dev)) {
|
||||
log_error("%s not opened: device not zeroed", pv_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!dev_set(dev, UINT64_C(0), (size_t) 2048, 0)) {
|
||||
log_error("%s not wiped: aborting", pv_name);
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
return 0;
|
||||
}
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vg_write(vg))
|
||||
return_0;
|
||||
|
||||
if (drop_lvmetad && lvmetad_active()) {
|
||||
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;
|
||||
|
||||
@@ -425,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);
|
||||
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);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user