Compare commits
967 Commits
Author | SHA1 | Date | |
---|---|---|---|
c4430a937d | |||
237314ad0d | |||
caf76ec592 | |||
0af8c26b74 | |||
825dfe7e0d | |||
30a0809553 | |||
6ee3035523 | |||
b627ebbf40 | |||
ef4bdf6b8b | |||
54722acada | |||
0e2bf3aa1d | |||
365126efa9 | |||
03d4c9217d | |||
8498290848 | |||
654db565cb | |||
51f83548ed | |||
5847a6bdb5 | |||
313e5e2047 | |||
7914e62b10 | |||
84d3284609 | |||
70fab5b46e | |||
e36135031d | |||
5a5ee0326e | |||
776dabfb2e | |||
5c4755ad08 | |||
7c1666289d | |||
cded320e92 | |||
b31cdec225 | |||
591b120d35 | |||
e8913fea12 | |||
355a41a763 | |||
5bd4825432 | |||
8f7e5b028a | |||
2a29d9a1ee | |||
e056966bc7 | |||
ef0ea4ba05 | |||
2892624783 | |||
2c10410b0d | |||
d1d74c4367 | |||
8b7f3b8f1d | |||
3f6c2efb8d | |||
227f36497a | |||
5ef4c7bcd3 | |||
70d00e0149 | |||
dcf155dac9 | |||
3c5b523631 | |||
6396bace3d | |||
713a128adf | |||
affc224aca | |||
6f82d32977 | |||
2a06e08618 | |||
1057b1f5a5 | |||
af76234112 | |||
1825c1a9b7 | |||
9a8bf2cac9 | |||
cc5ef79bec | |||
3725d95c65 | |||
4fb068019e | |||
6446a078a0 | |||
1d7fcbece8 | |||
8703a68a31 | |||
9bcdade85f | |||
b0156179b9 | |||
d0a0bad9d6 | |||
a4003d9078 | |||
3f4a62de2f | |||
bf23f63aa5 | |||
fd641b99c3 | |||
225affc9ca | |||
9ce2481a69 | |||
d95c74c6e7 | |||
218ee3269f | |||
5ca5f8daf3 | |||
98cdee781a | |||
9cf4504909 | |||
5f846a3fc1 | |||
c9793d47f9 | |||
be8adca115 | |||
9152a0077f | |||
0b90c67fb4 | |||
b4975d3102 | |||
ee33795b72 | |||
90e16be3ae | |||
cf90a369e2 | |||
6b303323be | |||
1576c7a0d9 | |||
cd5d6103ea | |||
207f763d1a | |||
1bed3aedc8 | |||
ab77d660cc | |||
b74a1daae9 | |||
bec357e2cb | |||
78593b5b5c | |||
7d6f03a7fe | |||
f46573f8c3 | |||
b83e136fb6 | |||
5c4203b20c | |||
7f9eef1d47 | |||
a8a0132766 | |||
831c43c91b | |||
b452e2df74 | |||
7f37cacfac | |||
3bb7e62e88 | |||
3b060167f6 | |||
8a76e71129 | |||
396fd747a6 | |||
16bd08b297 | |||
ccdf327ac8 | |||
8cd63df0dc | |||
b90cb34fd6 | |||
d6c1e12c06 | |||
d33d1c880b | |||
985e84e369 | |||
cc2c5c7762 | |||
40bf636b47 | |||
347cde827b | |||
ac4a1fb35c | |||
6f3714b9aa | |||
d810014eeb | |||
e0f6892625 | |||
9d5b426a6d | |||
8bf5769382 | |||
2970cd3d6d | |||
d41114c5a8 | |||
6c92449702 | |||
db04d10d14 | |||
5a4233f07b | |||
3c715edd07 | |||
bbe05d7fe9 | |||
2af8b8ef91 | |||
d4bfdfe749 | |||
1d14c31658 | |||
9bd81bb384 | |||
d64226efee | |||
2440eaa2df | |||
e8bf4f31f2 | |||
6682461d88 | |||
41f1132e0e | |||
d938c9337a | |||
9896a75caf | |||
7eefd0c3d7 | |||
2e268e311c | |||
3e182fd828 | |||
7b60850334 | |||
1552d9699c | |||
7507b19cd2 | |||
16f9ea6708 | |||
d984a9acf0 | |||
955f4aefcd | |||
858bbfbbd1 | |||
c1570b373f | |||
d336363771 | |||
e57aa36d3e | |||
b488f850aa | |||
ec07a280ba | |||
5006632550 | |||
7eb9f48485 | |||
31cba7098d | |||
f4571b0b50 | |||
3832911d50 | |||
28c86760da | |||
c4604ca468 | |||
464c409aa3 | |||
08ec39be0c | |||
25350f3370 | |||
0023cfa385 | |||
ed24142767 | |||
917230e4f8 | |||
05228f17f5 | |||
e8653b96be | |||
1cf191c597 | |||
3d3e31b7f8 | |||
8730cfcc3e | |||
5830e5620d | |||
46d53e3e90 | |||
3554fe6480 | |||
0dadf66dc7 | |||
a941bbd0c9 | |||
21e3ed3449 | |||
81678129fb | |||
52d8db7925 | |||
875d375d7a | |||
cba167b874 | |||
e68c0e68bd | |||
ff2bc2d21f | |||
4961404c7c | |||
3fbf2311e4 | |||
41685061f7 | |||
35a7ab5778 | |||
e1beaae4a2 | |||
965bd58693 | |||
00fdaaf12b | |||
60473d234a | |||
4f688e09a4 | |||
24e84128e4 | |||
e63457b6b2 | |||
a83cedc2ac | |||
076afa6197 | |||
423e3cbd18 | |||
0263396187 | |||
043018cfbe | |||
2037d9af03 | |||
7f07991035 | |||
18ce01caff | |||
5bc8e80a99 | |||
6252df4c18 | |||
451856d21d | |||
aa30663ca5 | |||
8616a4afe5 | |||
bc2358319b | |||
0bf4b81370 | |||
c9dd5a2452 | |||
cf95f616c5 | |||
1adbc7c13c | |||
9d28974c27 | |||
3dbc35b5c1 | |||
fee0fe5422 | |||
86d9f4e733 | |||
3f16f1b006 | |||
cbd9899389 | |||
cd44fb8d84 | |||
aca4c2b5a9 | |||
85eedfb78b | |||
f26276bc4e | |||
6d62e69f9a | |||
4188fd59a0 | |||
5b9f575648 | |||
0d890ec414 | |||
926d05ef0b | |||
8be48ddfc7 | |||
41e66bfaf6 | |||
47a7241410 | |||
54c77b3d62 | |||
a1c5575308 | |||
a44c934b5d | |||
546d2653ee | |||
33c06b3388 | |||
1917ea3ce1 | |||
70842b9ef2 | |||
e6122a657e | |||
9e860ac01a | |||
7690a8e7bd | |||
1860208560 | |||
1689296d46 | |||
7aa4851b77 | |||
6ef8e2902f | |||
aa16b7b284 | |||
9bbd83b1f2 | |||
65535670f9 | |||
9d42fe4d3b | |||
918a367258 | |||
970a70b41e | |||
4094fe5a31 | |||
dea8e2cb54 | |||
0514a4308f | |||
d0647e5a02 | |||
bbe06f97be | |||
f1a83e9759 | |||
38a81c6b46 | |||
6afb60abf5 | |||
a42212fc1e | |||
2e21948156 | |||
5279ee745f | |||
227501c063 | |||
89d25b1931 | |||
b57c0dbe30 | |||
8b910bb6bc | |||
dfde34e612 | |||
2530811e22 | |||
85205bc253 | |||
3cdd1a3424 | |||
002865405c | |||
8a73ef897a | |||
be61c56c21 | |||
dbaef7d04d | |||
2048073355 | |||
a585e1f696 | |||
415737b2b8 | |||
54f7007cc5 | |||
b0338178d7 | |||
159100b944 | |||
41a8db3576 | |||
fe291ab794 | |||
adb65b9889 | |||
8513626b9f | |||
7ca0ba4515 | |||
42200c405a | |||
be327dbccd | |||
c724dc3892 | |||
70dc2ff3ab | |||
81f5d03e8d | |||
e50c6b94c1 | |||
28eaff20bd | |||
8d1a1b2976 | |||
92eaec53db | |||
b3c2c57897 | |||
f458e97fda | |||
80bf9ae99b | |||
bebd4a7ca4 | |||
9468e94412 | |||
6b66c8507f | |||
167e5406c3 | |||
c111c9a931 | |||
bb71e3a023 | |||
7b1bf4c098 | |||
32b88d928a | |||
f8e1932337 | |||
7c9fb570cc | |||
56d22c66c0 | |||
85cdc4f371 | |||
96bcfb9b1f | |||
4a874665eb | |||
6f6b69946e | |||
5b7f44555e | |||
2ca396c015 | |||
d8dae16035 | |||
8f02db04f9 | |||
9f35e44681 | |||
6279b8f5a5 | |||
3084232cb5 | |||
67cc79ec52 | |||
b9a09a9501 | |||
4a0d3a3e3f | |||
2322a980d0 | |||
c19f5b85a3 | |||
7f9d8438ab | |||
51c80c5a52 | |||
6477ebcf6f | |||
bc02c2789c | |||
c0b3d09236 | |||
3ddbab6193 | |||
befd95a90a | |||
ab6cd4229b | |||
9213744ecb | |||
41c0333814 | |||
afcf8b3ed6 | |||
69ebbec40b | |||
b22a9c14a4 | |||
54067d8225 | |||
d64c4eeab0 | |||
15d2c7786e | |||
73a1da5ed6 | |||
fbf8779388 | |||
3231c35fb8 | |||
ced7838de4 | |||
2f26b8668a | |||
9432838914 | |||
1a89a7794e | |||
c0a87c12fb | |||
c6a7ea0a2f | |||
5bb057e5a2 | |||
2924b37d6d | |||
42c0f784e2 | |||
05f17d1ec4 | |||
777690a121 | |||
a98e228766 | |||
4c9174ce26 | |||
1d70e3812c | |||
e2225aa882 | |||
99dd709f3e | |||
f197c286d5 | |||
b121711baa | |||
085655b21b | |||
4c209d6b10 | |||
8dc45e291a | |||
ec1ae7e631 | |||
25aa55b5f5 | |||
b5c6088130 | |||
a65eb0ec29 | |||
42eef1451c | |||
11ecf058e4 | |||
5f1f7ef564 | |||
2e4e698633 | |||
02dce8cad0 | |||
8aa4842fa8 | |||
efc09f63cc | |||
3253d8a2e4 | |||
1531185dd0 | |||
baf9c3704e | |||
cdf39e62b3 | |||
b81e37f6ab | |||
ddebbb52fd | |||
983e929e25 | |||
f47e035721 | |||
a80d72f999 | |||
8de9a9917f | |||
fa016c1697 | |||
7d2c156eb1 | |||
04cec92e8d | |||
64394b0de8 | |||
2f617a4548 | |||
2ba64bed18 | |||
cafccb5991 | |||
b22e8c3632 | |||
7929292618 | |||
0d4e4cae7f | |||
f4ba2e3155 | |||
7101ed6e27 | |||
85ac35aa9a | |||
40590561fe | |||
631e550920 | |||
f806c0effa | |||
50a4797fb1 | |||
cc2a0b12f8 | |||
988e8de122 | |||
2f8809c6bc | |||
92b7775fa1 | |||
f4d231e70a | |||
b419050aa7 | |||
8937c65951 | |||
6c6ad82d90 | |||
d0f11b66f7 | |||
f9fcac51a5 | |||
ca953d831f | |||
01c023d50f | |||
c2113a405e | |||
5dae81d199 | |||
bd768c3320 | |||
572fc035a2 | |||
99b2f045af | |||
6248e51797 | |||
19e4a36c70 | |||
90769e5694 | |||
b8cbe5d65b | |||
35c95ca653 | |||
2dbc1a9a55 | |||
dceecb0bbf | |||
d690d14568 | |||
85ef624440 | |||
e995996290 | |||
8e6ad4301d | |||
86740dfc89 | |||
1399c592d1 | |||
9883b54cba | |||
83b8949a98 | |||
28f60e5291 | |||
1f31d06f48 | |||
2f2e83c890 | |||
b22c618734 | |||
1e041082bb | |||
a57ce270ac | |||
b5b99a52cd | |||
9586ce2f46 | |||
b8d526f18d | |||
d2edc68ead | |||
4d651378e2 | |||
58791864d7 | |||
1a41e9af4f | |||
c297835b01 | |||
e68269fcaf | |||
5243df4712 | |||
4470eba551 | |||
1f2c4713ef | |||
a6c16894ff | |||
271764deb9 | |||
52f7a73009 | |||
bdb6e6b83f | |||
41dacd5d3d | |||
eb1dfb02b5 | |||
1a0eb86344 | |||
bdb62b20a3 | |||
f2ca03d7d0 | |||
00ac86c31b | |||
627d000098 | |||
4be4736603 | |||
2da7aca8e8 | |||
8306b8b1a5 | |||
605cfd4ab1 | |||
dec3147501 | |||
c642aec128 | |||
fd9aa8dfa2 | |||
07d6c0967d | |||
80a3749088 | |||
c72fdb53ae | |||
b03ec281bf | |||
cef4654ff4 | |||
f45dceeb73 | |||
18262a88c9 | |||
87f4be7998 | |||
d737adc6be | |||
5fdaecf6f4 | |||
d8792b88ef | |||
8b1174f50a | |||
8c8f7b5a09 | |||
44915932d5 | |||
e90fdf5bed | |||
a11c8ab485 | |||
74a50158ca | |||
6ee85d57be | |||
b2fc6f9228 | |||
f91481eded | |||
651a61f559 | |||
b06edeca02 | |||
89ccb125d1 | |||
c972704477 | |||
887f1cb90c | |||
16b4d78400 | |||
ec8d9c6b80 | |||
49c2d1dcad | |||
d0f51651f9 | |||
481ccf16a5 | |||
a223458753 | |||
e1740f3f01 | |||
740dc9d1d4 | |||
bbf01b644c | |||
66d066964c | |||
c81c46c336 | |||
c3747b93c8 | |||
d43265b7f1 | |||
6864fd0149 | |||
340c0bf9e3 | |||
4d104cd4d8 | |||
367c0ff7c6 | |||
9c26a3d61a | |||
93e3581ce7 | |||
f4e52bb27d | |||
72064fd0df | |||
77486a608e | |||
e97025ab02 | |||
e43b9175c0 | |||
9cc1415ef5 | |||
bd215dc0e4 | |||
12e874cef0 | |||
6d233161b0 | |||
905a570489 | |||
432fe44187 | |||
51b938496d | |||
b7f9b25e4d | |||
fe61280b6b | |||
68c087d578 | |||
d6bf87cab7 | |||
2b96a43879 | |||
697c41c584 | |||
a2379996e6 | |||
29077d95db | |||
dbd00a57b0 | |||
d08cff51a4 | |||
3e461dec1c | |||
4d08e25913 | |||
43313c2ee7 | |||
81b2a87232 | |||
3d8cd0ced7 | |||
7c78d54231 | |||
f9d71e8b17 | |||
0107fd323c | |||
8ba47929a0 | |||
794b0fe9ce | |||
979dccc7ec | |||
44a5f38bc4 | |||
bf78f70885 | |||
545706cbee | |||
0d916ac531 | |||
d4ab407045 | |||
45212a8c78 | |||
64b83c3d70 | |||
639a6782bd | |||
5f34d69bcc | |||
337ff5a3cc | |||
8e6459a818 | |||
aff3e16194 | |||
9372c0787d | |||
83fb2da53e | |||
645a044bf6 | |||
37796ff73f | |||
e1fdcb1678 | |||
aab9a26409 | |||
958055a789 | |||
edda5039d4 | |||
1c86893d95 | |||
d543587d34 | |||
780bc4cad2 | |||
18bd6ba13d | |||
4dafc513cc | |||
7acd5c5659 | |||
8428063d9e | |||
f490dda05a | |||
2b191385ea | |||
bc228e5eaf | |||
8be65e34de | |||
d967d8f1a7 | |||
50deb0d3f8 | |||
1d928b25fe | |||
f2f81791d1 | |||
382f10a0cc | |||
0d2133db98 | |||
09faa9ee95 | |||
ccec086e25 | |||
05725ac9a4 | |||
96b7483138 | |||
81281d04a4 | |||
e062ebbc29 | |||
b92cad0938 | |||
ea368a06cd | |||
3f48cdb380 | |||
17c7b46a69 | |||
a375df6f4c | |||
a3775bb4e8 | |||
1e0c6194b5 | |||
a6bd669854 | |||
6334bdc1c5 | |||
3b82f3eea5 | |||
38556bf60d | |||
d8d8af9826 | |||
3984a5fd77 | |||
397356096a | |||
365915da9a | |||
87152fbac6 | |||
22a9189ee0 | |||
4428818412 | |||
47ea98e0e3 | |||
6dd0513546 | |||
8abe51b71d | |||
69b8bc3bfa | |||
301b8aa0a5 | |||
e5b6c93323 | |||
9a045790ed | |||
82a103c8f9 | |||
0123039271 | |||
9a0e115a37 | |||
867bfc4378 | |||
feb1645f37 | |||
8ca37d6a65 | |||
ac163a7c18 | |||
9b6bddb24c | |||
f57ae48286 | |||
4cbd7eb7f9 | |||
310686726a | |||
ad5cee1d22 | |||
bad6e32075 | |||
8ae6d28cd4 | |||
ca1060862e | |||
8a0046f519 | |||
84cbdb35c4 | |||
1e93fbb5c1 | |||
619554af2b | |||
d5a48b5ce4 | |||
4e9cc3e97c | |||
492bc2ba63 | |||
995492100a | |||
854319d88c | |||
3189d05134 | |||
b2a43b987c | |||
6676409f7f | |||
44de5bcc00 | |||
e2956c605d | |||
b22b6c2299 | |||
90950c9c20 | |||
0c5b9e7820 | |||
a9ffa010c8 | |||
a6a903293b | |||
3fffcb5d77 | |||
a670b99db1 | |||
aefd74197a | |||
9ff747ef50 | |||
a08a198577 | |||
4cfb123448 | |||
198ebc6c86 | |||
a8abcd9b30 | |||
b7469f5a9a | |||
6bbe49aa14 | |||
5aa1019010 | |||
29a59b380c | |||
0bfcea6a11 | |||
19f5aa252f | |||
89e9134a3f | |||
b5a202acb6 | |||
0f860f712f | |||
7c66701366 | |||
585e90c0de | |||
5c852d5b82 | |||
484172b5f8 | |||
d148958b67 | |||
0a8d773ad0 | |||
427d90e6c1 | |||
9b2e4079d0 | |||
1a0b410554 | |||
2d50a6192f | |||
781da7f6f0 | |||
646221cc29 | |||
b168a27f73 | |||
a442bd9792 | |||
884fec7735 | |||
1cb89f302f | |||
da36bbe756 | |||
25e464c5ce | |||
8446fbca85 | |||
9738dd545f | |||
0bce2118e7 | |||
6543214dde | |||
d91c6fd4e1 | |||
711d1f6fc3 | |||
e422beec74 | |||
a484c9cf96 | |||
5654d8ceba | |||
31cf625af5 | |||
93be18ffd2 | |||
e96464c795 | |||
ad0ed40a59 | |||
63fd8e58b2 | |||
758a827c2d | |||
7ad33e8052 | |||
abfe0c0e70 | |||
f22dfb5ece | |||
4bda51688b | |||
eab25e2f33 | |||
94bd11bae2 | |||
759af9f00c | |||
f58e5132aa | |||
d831846706 | |||
1fc9ac0433 | |||
5c48d0af1f | |||
30fb19be35 | |||
fbeac4ea28 | |||
7f066a9b21 | |||
c5a767cd1d | |||
027ef213aa | |||
dc1fdd6267 | |||
96918252e5 | |||
014dc5f9d7 | |||
59e94227af | |||
e84b801c2e | |||
6638c034d2 | |||
04df41cec1 | |||
483da89d03 | |||
c92e3832bf | |||
edb90f6afa | |||
0057f0e580 | |||
e6217b8b36 | |||
6fe16039b9 | |||
42967bf185 | |||
5843268c47 | |||
7273ba3de2 | |||
0bf1c314da | |||
c7926d8e8c | |||
44ce25e7ac | |||
3a2cc5c66e | |||
3838ce3330 | |||
59217472aa | |||
df69a4fc59 | |||
25d3965769 | |||
08d8b2a4fd | |||
879569d73f | |||
b63f833d36 | |||
482c6e33dd | |||
46a1863f88 | |||
632756b6fb | |||
04eba29c55 | |||
0912878ecf | |||
d5035c5600 | |||
38ae42b11a | |||
a174854a0d | |||
c4b2b9ab41 | |||
ef942e04c2 | |||
f54cd66924 | |||
b40ab10d38 | |||
f8ccbfdedd | |||
470f1c798a | |||
5c012b392a | |||
165b641c1d | |||
66e42bec05 | |||
c503ea7045 | |||
745ec187ce | |||
f046313c0e | |||
74595b8821 | |||
c9fdd142a4 | |||
abaa6d0ac9 | |||
cfae8f0656 | |||
54f4ecd46a | |||
1835d86e9d | |||
b9b4b31284 | |||
b4772d1c43 | |||
9933dc3133 | |||
08ac90f920 | |||
13f5863561 | |||
81764111fe | |||
cb022525ff | |||
75656a78c6 | |||
284eb5daff | |||
ff58c51919 | |||
2fb1bdda20 | |||
12299b333b | |||
b017bbc441 | |||
9e8c0d2e33 | |||
250c29edd2 | |||
c431659d05 | |||
a33389c391 | |||
3460565414 | |||
26b62138ee | |||
afb0220642 | |||
0993923ed5 | |||
e0362b0d0f | |||
df3a74d7e0 | |||
d5d457e667 | |||
b27c32821c | |||
76b15a035f | |||
eb8feb1281 | |||
fc6ce9835b | |||
8ae9f4efc2 | |||
c9d13b0fc4 | |||
bfacc1d8c3 | |||
02d484370f | |||
5ae86dfaa1 | |||
dbe7e556b0 | |||
4799280ccd | |||
cb4865466e | |||
cb80d900b3 | |||
ee01737e87 | |||
2012825913 | |||
eb5e3420ae | |||
b2362a1207 | |||
54d968664a | |||
1e20f819d5 | |||
8001c82e81 | |||
baefbc444e | |||
4a227b54bf | |||
8a192bedde | |||
d5efa18ae4 | |||
5f79dc2805 | |||
9aa58f0143 | |||
8835664653 | |||
d37da6b7fc | |||
b9ee86efe1 | |||
d108b610fd | |||
0ec79339f7 | |||
2afdc7f27d | |||
26aa9aca40 | |||
3e2984bcb9 | |||
a7a5406c32 | |||
4f727a783e | |||
23dc68fdea | |||
b532dd00c4 | |||
c01742855a | |||
9c953dd260 | |||
3fbf2d2fcd | |||
e0af222ec3 | |||
73b5011786 | |||
2ea5abcd65 | |||
7137630d43 | |||
8acfd15d6e | |||
48fbbfeb7e | |||
9990af3042 | |||
fe6c19383b | |||
42150d263b | |||
9839d3f778 | |||
dd59e3c2a1 | |||
0b7432ae09 | |||
c1c2c8f635 | |||
7680525eec | |||
42298d5896 | |||
39478aa52c | |||
6a99b930c4 | |||
f6ce45b373 | |||
205e187613 | |||
a78348acbb | |||
410611b4f2 | |||
af07ec8f29 | |||
3f803af00b | |||
ac461bd651 | |||
ce955e1635 | |||
e20d008c6a | |||
fb657d8ee5 | |||
fba0b77469 | |||
b5c1296eaa | |||
065df12872 | |||
7e1d4712b8 | |||
49c965a497 | |||
6fe9aedd0b | |||
42cb9bd6a5 | |||
66dbe5639e | |||
2d87f2fb73 | |||
4c81273274 | |||
73b8f6793e | |||
663ef85992 | |||
e92c75815b | |||
6dbad5b4b5 | |||
bff7e3f3e4 | |||
83abc7497d | |||
8bc5eebeb8 | |||
1433b96ba0 | |||
be1a8c94ae | |||
4606f34353 | |||
7bb720cb4d | |||
c4d8542ec1 | |||
9700d5374a | |||
05e90d6463 | |||
55118ca18e | |||
f70d8091d3 | |||
a3c709ef21 | |||
4917f1e2d4 | |||
93829fc680 | |||
5605ca5619 | |||
e49f0c03d9 | |||
0098b712a5 | |||
5fb694e8c0 | |||
583a68a446 | |||
e6604cf391 | |||
43cfb3c35a | |||
8a16c571d2 | |||
314652a499 | |||
6b68e5d597 | |||
cafd51bf42 | |||
eaff09f483 | |||
9b93c62044 | |||
5d90860688 | |||
5ba83ed099 | |||
50bf10ad56 | |||
16d444c979 | |||
fa9c9be737 | |||
2e7014e31d | |||
a84050c1f0 | |||
7c9835465e | |||
ec00200411 | |||
956e5fec1f | |||
b107fdb99a | |||
7320e9ff4b | |||
c4d2d54a6d | |||
1142350e8d | |||
d735b31345 | |||
e211fee562 | |||
8c15560b68 | |||
327e93711f | |||
a076571470 | |||
ff50c07ebf | |||
179145dc24 | |||
6bd0a00c46 | |||
f6e28f4e62 | |||
37f1b7dd8d | |||
60e6ee46de | |||
2260f065d4 | |||
6eff8dec4f | |||
7e25b9aaaa | |||
f867ef9c4a | |||
fc8920e35d | |||
7f3b0f67e7 | |||
844660036b | |||
efcac39d34 | |||
cb4b721cb0 | |||
7956877f14 | |||
2241c6795f | |||
43e60ceb41 | |||
b760d8a23f | |||
2c1592263d | |||
616533823c | |||
913dddea85 | |||
3530430365 | |||
a4ba60be8f | |||
99e98f605c | |||
935ee97b17 | |||
6b9bfd7fe9 | |||
dd519bbad1 | |||
35fe981c7d | |||
b6570abe79 | |||
54813c650e | |||
781106f8c5 | |||
96f35520a0 | |||
490560e0c6 | |||
52f53d8280 | |||
27b8a3f671 | |||
abf9b6da42 | |||
0c9209b04c | |||
edebd52374 | |||
61205f00fb | |||
a303e00289 | |||
af9f72e9d8 | |||
5176346b30 | |||
731eeef25b | |||
a65e3e4bc0 |
10
.gitignore
vendored
10
.gitignore
vendored
@ -1,6 +1,16 @@
|
|||||||
local.mak
|
local.mak
|
||||||
/target
|
/target
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
*~
|
||||||
|
*.backup
|
||||||
|
*.backup[0-9]
|
||||||
|
*.backup[0-9][0-9]
|
||||||
|
*.old
|
||||||
|
*.old[0-9]
|
||||||
|
*.old[0-9][0-9]
|
||||||
|
*.5
|
||||||
|
*.7
|
||||||
|
__pycache__/
|
||||||
/etc/proxmox-backup.service
|
/etc/proxmox-backup.service
|
||||||
/etc/proxmox-backup-proxy.service
|
/etc/proxmox-backup-proxy.service
|
||||||
build/
|
build/
|
||||||
|
34
Cargo.toml
34
Cargo.toml
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "proxmox-backup"
|
name = "proxmox-backup"
|
||||||
version = "1.0.3"
|
version = "1.0.11"
|
||||||
authors = [
|
authors = [
|
||||||
"Dietmar Maurer <dietmar@proxmox.com>",
|
"Dietmar Maurer <dietmar@proxmox.com>",
|
||||||
"Dominik Csapak <d.csapak@proxmox.com>",
|
"Dominik Csapak <d.csapak@proxmox.com>",
|
||||||
@ -23,22 +23,22 @@ name = "proxmox_backup"
|
|||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
apt-pkg-native = "0.3.1" # custom patched version
|
apt-pkg-native = "0.3.2"
|
||||||
base64 = "0.12"
|
base64 = "0.12"
|
||||||
bitflags = "1.2.1"
|
bitflags = "1.2.1"
|
||||||
bytes = "0.5"
|
bytes = "1.0"
|
||||||
crc32fast = "1"
|
crc32fast = "1"
|
||||||
endian_trait = { version = "0.6", features = ["arrays"] }
|
endian_trait = { version = "0.6", features = ["arrays"] }
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
h2 = { version = "0.2", features = ["stream"] }
|
h2 = { version = "0.3", features = [ "stream" ] }
|
||||||
handlebars = "3.0"
|
handlebars = "3.0"
|
||||||
http = "0.2"
|
http = "0.2"
|
||||||
hyper = "0.13.6"
|
hyper = { version = "0.14", features = [ "full" ] }
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
nix = "0.19"
|
nix = "0.19.1"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
once_cell = "1.3.1"
|
once_cell = "1.3.1"
|
||||||
openssl = "0.10"
|
openssl = "0.10"
|
||||||
@ -46,32 +46,34 @@ pam = "0.7"
|
|||||||
pam-sys = "0.5"
|
pam-sys = "0.5"
|
||||||
percent-encoding = "2.1"
|
percent-encoding = "2.1"
|
||||||
pin-utils = "0.1.0"
|
pin-utils = "0.1.0"
|
||||||
pin-project = "0.4"
|
pin-project = "1.0"
|
||||||
pathpatterns = "0.1.2"
|
pathpatterns = "0.1.2"
|
||||||
proxmox = { version = "0.7.1", features = [ "sortable-macro", "api-macro", "websocket" ] }
|
proxmox = { version = "0.11.0", features = [ "sortable-macro", "api-macro", "websocket" ] }
|
||||||
#proxmox = { git = "git://git.proxmox.com/git/proxmox", version = "0.1.2", features = [ "sortable-macro", "api-macro" ] }
|
#proxmox = { git = "git://git.proxmox.com/git/proxmox", version = "0.1.2", features = [ "sortable-macro", "api-macro" ] }
|
||||||
#proxmox = { path = "../proxmox/proxmox", features = [ "sortable-macro", "api-macro", "websocket" ] }
|
#proxmox = { path = "../proxmox/proxmox", features = [ "sortable-macro", "api-macro", "websocket" ] }
|
||||||
proxmox-fuse = "0.1.0"
|
proxmox-fuse = "0.1.1"
|
||||||
pxar = { version = "0.6.1", features = [ "tokio-io", "futures-io" ] }
|
pxar = { version = "0.10.0", features = [ "tokio-io" ] }
|
||||||
#pxar = { path = "../pxar", features = [ "tokio-io", "futures-io" ] }
|
#pxar = { path = "../pxar", features = [ "tokio-io" ] }
|
||||||
regex = "1.2"
|
regex = "1.2"
|
||||||
rustyline = "6"
|
rustyline = "7"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
siphasher = "0.3"
|
siphasher = "0.3"
|
||||||
syslog = "4.0"
|
syslog = "4.0"
|
||||||
tokio = { version = "0.2.9", features = [ "blocking", "fs", "dns", "io-util", "macros", "process", "rt-threaded", "signal", "stream", "tcp", "time", "uds" ] }
|
tokio = { version = "1.0", features = [ "fs", "io-util", "macros", "net", "parking_lot", "process", "rt", "rt-multi-thread", "signal", "time" ] }
|
||||||
tokio-openssl = "0.4.0"
|
tokio-openssl = "0.6.1"
|
||||||
tokio-util = { version = "0.3", features = [ "codec" ] }
|
tokio-stream = "0.1.0"
|
||||||
|
tokio-util = { version = "0.6", features = [ "codec" ] }
|
||||||
tower-service = "0.3.0"
|
tower-service = "0.3.0"
|
||||||
udev = ">= 0.3, <0.5"
|
udev = ">= 0.3, <0.5"
|
||||||
url = "2.1"
|
url = "2.1"
|
||||||
#valgrind_request = { git = "https://github.com/edef1c/libvalgrind_request", version = "1.1.0", optional = true }
|
#valgrind_request = { git = "https://github.com/edef1c/libvalgrind_request", version = "1.1.0", optional = true }
|
||||||
walkdir = "2"
|
walkdir = "2"
|
||||||
|
webauthn-rs = "0.2.5"
|
||||||
xdg = "2.2"
|
xdg = "2.2"
|
||||||
zstd = { version = "0.4", features = [ "bindgen" ] }
|
zstd = { version = "0.4", features = [ "bindgen" ] }
|
||||||
nom = "5.1"
|
nom = "5.1"
|
||||||
crossbeam-channel = "0.4"
|
crossbeam-channel = "0.5"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
9
Makefile
9
Makefile
@ -9,7 +9,10 @@ SUBDIRS := etc www docs
|
|||||||
# Binaries usable by users
|
# Binaries usable by users
|
||||||
USR_BIN := \
|
USR_BIN := \
|
||||||
proxmox-backup-client \
|
proxmox-backup-client \
|
||||||
pxar
|
pxar \
|
||||||
|
proxmox-tape \
|
||||||
|
pmtx \
|
||||||
|
pmt
|
||||||
|
|
||||||
# Binaries usable by admins
|
# Binaries usable by admins
|
||||||
USR_SBIN := \
|
USR_SBIN := \
|
||||||
@ -20,7 +23,7 @@ SERVICE_BIN := \
|
|||||||
proxmox-backup-api \
|
proxmox-backup-api \
|
||||||
proxmox-backup-banner \
|
proxmox-backup-banner \
|
||||||
proxmox-backup-proxy \
|
proxmox-backup-proxy \
|
||||||
proxmox-daily-update \
|
proxmox-daily-update
|
||||||
|
|
||||||
ifeq ($(BUILD_MODE), release)
|
ifeq ($(BUILD_MODE), release)
|
||||||
CARGO_BUILD_ARGS += --release
|
CARGO_BUILD_ARGS += --release
|
||||||
@ -141,6 +144,8 @@ install: $(COMPILED_BINS)
|
|||||||
install -m755 $(COMPILEDIR)/$(i) $(DESTDIR)$(SBINDIR)/ ; \
|
install -m755 $(COMPILEDIR)/$(i) $(DESTDIR)$(SBINDIR)/ ; \
|
||||||
install -m644 zsh-completions/_$(i) $(DESTDIR)$(ZSH_COMPL_DEST)/ ;)
|
install -m644 zsh-completions/_$(i) $(DESTDIR)$(ZSH_COMPL_DEST)/ ;)
|
||||||
install -dm755 $(DESTDIR)$(LIBEXECDIR)/proxmox-backup
|
install -dm755 $(DESTDIR)$(LIBEXECDIR)/proxmox-backup
|
||||||
|
# install sg-tape-cmd as setuid binary
|
||||||
|
install -m4755 -o root -g root $(COMPILEDIR)/sg-tape-cmd $(DESTDIR)$(LIBEXECDIR)/proxmox-backup/sg-tape-cmd
|
||||||
$(foreach i,$(SERVICE_BIN), \
|
$(foreach i,$(SERVICE_BIN), \
|
||||||
install -m755 $(COMPILEDIR)/$(i) $(DESTDIR)$(LIBEXECDIR)/proxmox-backup/ ;)
|
install -m755 $(COMPILEDIR)/$(i) $(DESTDIR)$(LIBEXECDIR)/proxmox-backup/ ;)
|
||||||
$(MAKE) -C www install
|
$(MAKE) -C www install
|
||||||
|
80
README.rst
80
README.rst
@ -53,3 +53,83 @@ Setup:
|
|||||||
Note: 2. may be skipped if you already added the PVE or PBS package repository
|
Note: 2. may be skipped if you already added the PVE or PBS package repository
|
||||||
|
|
||||||
You are now able to build using the Makefile or cargo itself.
|
You are now able to build using the Makefile or cargo itself.
|
||||||
|
|
||||||
|
|
||||||
|
Design Notes
|
||||||
|
============
|
||||||
|
|
||||||
|
Here are some random thought about the software design (unless I find a better place).
|
||||||
|
|
||||||
|
|
||||||
|
Large chunk sizes
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
It is important to notice that large chunk sizes are crucial for
|
||||||
|
performance. We have a multi-user system, where different people can do
|
||||||
|
different operations on a datastore at the same time, and most operation
|
||||||
|
involves reading a series of chunks.
|
||||||
|
|
||||||
|
So what is the maximal theoretical speed we can get when reading a
|
||||||
|
series of chunks? Reading a chunk sequence need the following steps:
|
||||||
|
|
||||||
|
- seek to the first chunk start location
|
||||||
|
- read the chunk data
|
||||||
|
- seek to the first chunk start location
|
||||||
|
- read the chunk data
|
||||||
|
- ...
|
||||||
|
|
||||||
|
Lets use the following disk performance metrics:
|
||||||
|
|
||||||
|
:AST: Average Seek Time (second)
|
||||||
|
:MRS: Maximum sequential Read Speed (bytes/second)
|
||||||
|
:ACS: Average Chunk Size (bytes)
|
||||||
|
|
||||||
|
The maximum performance you can get is::
|
||||||
|
|
||||||
|
MAX(ACS) = ACS /(AST + ACS/MRS)
|
||||||
|
|
||||||
|
Please note that chunk data is likely to be sequential arranged on disk, but
|
||||||
|
this it is sort of a best case assumption.
|
||||||
|
|
||||||
|
For a typical rotational disk, we assume the following values::
|
||||||
|
|
||||||
|
AST: 10ms
|
||||||
|
MRS: 170MB/s
|
||||||
|
|
||||||
|
MAX(4MB) = 115.37 MB/s
|
||||||
|
MAX(1MB) = 61.85 MB/s;
|
||||||
|
MAX(64KB) = 6.02 MB/s;
|
||||||
|
MAX(4KB) = 0.39 MB/s;
|
||||||
|
MAX(1KB) = 0.10 MB/s;
|
||||||
|
|
||||||
|
Modern SSD are much faster, lets assume the following::
|
||||||
|
|
||||||
|
max IOPS: 20000 => AST = 0.00005
|
||||||
|
MRS: 500Mb/s
|
||||||
|
|
||||||
|
MAX(4MB) = 474 MB/s
|
||||||
|
MAX(1MB) = 465 MB/s;
|
||||||
|
MAX(64KB) = 354 MB/s;
|
||||||
|
MAX(4KB) = 67 MB/s;
|
||||||
|
MAX(1KB) = 18 MB/s;
|
||||||
|
|
||||||
|
|
||||||
|
Also, the average chunk directly relates to the number of chunks produced by
|
||||||
|
a backup::
|
||||||
|
|
||||||
|
CHUNK_COUNT = BACKUP_SIZE / ACS
|
||||||
|
|
||||||
|
Here are some staticics from my developer worstation::
|
||||||
|
|
||||||
|
Disk Usage: 65 GB
|
||||||
|
Directories: 58971
|
||||||
|
Files: 726314
|
||||||
|
Files < 64KB: 617541
|
||||||
|
|
||||||
|
As you see, there are really many small files. If we would do file
|
||||||
|
level deduplication, i.e. generate one chunk per file, we end up with
|
||||||
|
more than 700000 chunks.
|
||||||
|
|
||||||
|
Instead, our current algorithm only produce large chunks with an
|
||||||
|
average chunks size of 4MB. With above data, this produce about 15000
|
||||||
|
chunks (factor 50 less chunks).
|
||||||
|
157
debian/changelog
vendored
157
debian/changelog
vendored
@ -1,3 +1,160 @@
|
|||||||
|
rust-proxmox-backup (1.0.11-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* fix feature flag logic in pxar create
|
||||||
|
|
||||||
|
* tools/zip: add missing start_disk field for zip64 extension to improve
|
||||||
|
compatibility with some strict archive tools
|
||||||
|
|
||||||
|
* tape: speedup backup by doing read/write in parallel
|
||||||
|
|
||||||
|
* tape: store datastore name in tape archives and media catalog
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Thu, 18 Mar 2021 12:36:01 +0100
|
||||||
|
|
||||||
|
rust-proxmox-backup (1.0.10-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* tape: improve MediaPool allocation by sorting tapes by creation time and
|
||||||
|
label text
|
||||||
|
|
||||||
|
* api: tape backup: continue on vanishing snapshots, as a prune during long
|
||||||
|
running tape backup jobs is OK
|
||||||
|
|
||||||
|
* tape: fix scsi volume_statistics and cartridge_memory for quantum drives
|
||||||
|
|
||||||
|
* typo fixes all over the place
|
||||||
|
|
||||||
|
* d/postinst: restart, not reload, when updating from a to old version
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Thu, 11 Mar 2021 08:24:31 +0100
|
||||||
|
|
||||||
|
rust-proxmox-backup (1.0.9-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* client: track key source, print when used
|
||||||
|
|
||||||
|
* fix #3026: pxar: metadata: apply flags _after_ updating mtime
|
||||||
|
|
||||||
|
* docs: add acl.cfg, datastore.cfg, remote.cfg, sync.cfg, user.cfg and
|
||||||
|
verification.cfg manual page pages
|
||||||
|
|
||||||
|
* docs: add API viewer
|
||||||
|
|
||||||
|
* proxmox-backup-manger: add verify-job command group with various sub
|
||||||
|
commands
|
||||||
|
|
||||||
|
* add experimental opt-in tape backup support
|
||||||
|
|
||||||
|
* lto-barcode: fix page offset calibration
|
||||||
|
|
||||||
|
* lto-barcode: fix avery 3420 paper format properties
|
||||||
|
|
||||||
|
* asyncify pxar create archive
|
||||||
|
|
||||||
|
* client: raise HTTP_TIMEOUT for simple requests to 120s
|
||||||
|
|
||||||
|
* docs: depend on mathjax library package from debian instead of CDN
|
||||||
|
|
||||||
|
* fix #3321: docs: client: fix interactive restore command explanation
|
||||||
|
|
||||||
|
* ui: use shorter datetime format for encryption key creation time
|
||||||
|
|
||||||
|
* docs: TFA: improve language
|
||||||
|
|
||||||
|
* config/TFA: webauthn: disallow registering the same token more than once,
|
||||||
|
that can lead to buggy behavior in some token/browser combinations.
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Mon, 08 Mar 2021 15:54:47 +0100
|
||||||
|
|
||||||
|
rust-proxmox-backup (1.0.8-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Https Connector: use hostname instead of URL again to avoid certificate
|
||||||
|
verification issues.
|
||||||
|
|
||||||
|
* ui: task summary: add verification jobs to count
|
||||||
|
|
||||||
|
* docs: explain some technical details about datastores/chunks
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Thu, 04 Feb 2021 12:39:49 +0100
|
||||||
|
|
||||||
|
rust-proxmox-backup (1.0.7-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* fix #3197: skip fingerprint check when restoring key
|
||||||
|
|
||||||
|
* client: add 'import-with-master-key' command
|
||||||
|
|
||||||
|
* fix #3192: correct sort in prune sim
|
||||||
|
|
||||||
|
* tools/daemon: improve reload behaviour
|
||||||
|
|
||||||
|
* http client: add timeouts for critical connects
|
||||||
|
|
||||||
|
* api: improve error messages for restricted endpoints
|
||||||
|
|
||||||
|
* api: allow tokens to list users
|
||||||
|
|
||||||
|
* ui: running tasks: Use gettext for column labels
|
||||||
|
|
||||||
|
* login: add two-factor authenication (TFA) and integrate in web-interface
|
||||||
|
|
||||||
|
* login: support webAuthn, recovery keys and TOTP as TFA methods
|
||||||
|
|
||||||
|
* make it possible to abort tasks with CTRL-C
|
||||||
|
|
||||||
|
* fix #3245: only use default schedule for new jobs
|
||||||
|
|
||||||
|
* manager CLI: user/token list: fix rendering 0 (never) expire date
|
||||||
|
|
||||||
|
* update the event-driven, non-blocking I/O tokio platform to 1.0
|
||||||
|
|
||||||
|
* access: limit editing all pam credentials to superuser
|
||||||
|
|
||||||
|
* access: restrict password changes on @pam realm to superuser
|
||||||
|
|
||||||
|
* patch out wrongly linked libraries from ELFs to avoid extra, bogus
|
||||||
|
dependencies in resulting package
|
||||||
|
|
||||||
|
* add "password hint" to encryption key config
|
||||||
|
|
||||||
|
* improve GC error handling
|
||||||
|
|
||||||
|
* cli: make it possible to abort tasks with CTRL-C
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Wed, 03 Feb 2021 10:34:23 +0100
|
||||||
|
|
||||||
|
rust-proxmox-backup (1.0.6-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* stricter handling of file-descriptors, fixes some cases where some could
|
||||||
|
leak
|
||||||
|
|
||||||
|
* ui: fix various usages of the findRecord emthod, ensuring it matches exact
|
||||||
|
|
||||||
|
* garbage collection: improve task log format
|
||||||
|
|
||||||
|
* verification: improve progress log, make it similar to what's logged on
|
||||||
|
pull (sync)
|
||||||
|
|
||||||
|
* datastore: move manifest locking to /run. This avoids issues with
|
||||||
|
filesystems which cannot natively handle removing in-use files ("delete on
|
||||||
|
last close"), and create a virtual, internal, replacement file to work
|
||||||
|
around that. This is done, for example, by NFS or CIFS (samba).
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Fri, 11 Dec 2020 12:51:33 +0100
|
||||||
|
|
||||||
|
rust-proxmox-backup (1.0.5-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* client: restore: print meta information exclusively to standard error
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Wed, 25 Nov 2020 15:29:58 +0100
|
||||||
|
|
||||||
|
rust-proxmox-backup (1.0.4-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* fingerprint: add bytes() accessor
|
||||||
|
|
||||||
|
* ui: fix broken gettext use
|
||||||
|
|
||||||
|
* cli: move more commands into "snapshot" sub-command
|
||||||
|
|
||||||
|
-- Proxmox Support Team <support@proxmox.com> Wed, 25 Nov 2020 06:37:41 +0100
|
||||||
|
|
||||||
rust-proxmox-backup (1.0.3-1) unstable; urgency=medium
|
rust-proxmox-backup (1.0.3-1) unstable; urgency=medium
|
||||||
|
|
||||||
* client: inform user when automatically using the default encryption key
|
* client: inform user when automatically using the default encryption key
|
||||||
|
88
debian/control
vendored
88
debian/control
vendored
@ -7,24 +7,25 @@ Build-Depends: debhelper (>= 11),
|
|||||||
rustc:native,
|
rustc:native,
|
||||||
libstd-rust-dev,
|
libstd-rust-dev,
|
||||||
librust-anyhow-1+default-dev,
|
librust-anyhow-1+default-dev,
|
||||||
librust-apt-pkg-native-0.3+default-dev (>= 0.3.1-~~),
|
librust-apt-pkg-native-0.3+default-dev (>= 0.3.2-~~),
|
||||||
librust-base64-0.12+default-dev,
|
librust-base64-0.12+default-dev,
|
||||||
librust-bitflags-1+default-dev (>= 1.2.1-~~),
|
librust-bitflags-1+default-dev (>= 1.2.1-~~),
|
||||||
librust-bytes-0.5+default-dev,
|
librust-bytes-1+default-dev,
|
||||||
librust-crc32fast-1+default-dev,
|
librust-crc32fast-1+default-dev,
|
||||||
librust-crossbeam-channel-0.4+default-dev,
|
librust-crossbeam-channel-0.5+default-dev,
|
||||||
librust-endian-trait-0.6+arrays-dev,
|
librust-endian-trait-0.6+arrays-dev,
|
||||||
librust-endian-trait-0.6+default-dev,
|
librust-endian-trait-0.6+default-dev,
|
||||||
librust-futures-0.3+default-dev,
|
librust-futures-0.3+default-dev,
|
||||||
librust-h2-0.2+default-dev,
|
librust-h2-0.3+default-dev,
|
||||||
librust-h2-0.2+stream-dev,
|
librust-h2-0.3+stream-dev,
|
||||||
librust-handlebars-3+default-dev,
|
librust-handlebars-3+default-dev,
|
||||||
librust-http-0.2+default-dev,
|
librust-http-0.2+default-dev,
|
||||||
librust-hyper-0.13+default-dev (>= 0.13.6-~~),
|
librust-hyper-0.14+default-dev,
|
||||||
|
librust-hyper-0.14+full-dev,
|
||||||
librust-lazy-static-1+default-dev (>= 1.4-~~),
|
librust-lazy-static-1+default-dev (>= 1.4-~~),
|
||||||
librust-libc-0.2+default-dev,
|
librust-libc-0.2+default-dev,
|
||||||
librust-log-0.4+default-dev,
|
librust-log-0.4+default-dev,
|
||||||
librust-nix-0.19+default-dev,
|
librust-nix-0.19+default-dev (>= 0.19.1-~~),
|
||||||
librust-nom-5+default-dev (>= 5.1-~~),
|
librust-nom-5+default-dev (>= 5.1-~~),
|
||||||
librust-num-traits-0.2+default-dev,
|
librust-num-traits-0.2+default-dev,
|
||||||
librust-once-cell-1+default-dev (>= 1.3.1-~~),
|
librust-once-cell-1+default-dev (>= 1.3.1-~~),
|
||||||
@ -33,43 +34,42 @@ Build-Depends: debhelper (>= 11),
|
|||||||
librust-pam-sys-0.5+default-dev,
|
librust-pam-sys-0.5+default-dev,
|
||||||
librust-pathpatterns-0.1+default-dev (>= 0.1.2-~~),
|
librust-pathpatterns-0.1+default-dev (>= 0.1.2-~~),
|
||||||
librust-percent-encoding-2+default-dev (>= 2.1-~~),
|
librust-percent-encoding-2+default-dev (>= 2.1-~~),
|
||||||
librust-pin-project-0.4+default-dev,
|
librust-pin-project-1+default-dev,
|
||||||
librust-pin-utils-0.1+default-dev,
|
librust-pin-utils-0.1+default-dev,
|
||||||
librust-proxmox-0.7+api-macro-dev (>= 0.7.1-~~),
|
librust-proxmox-0.11+api-macro-dev,
|
||||||
librust-proxmox-0.7+default-dev (>= 0.7.1-~~),
|
librust-proxmox-0.11+default-dev,
|
||||||
librust-proxmox-0.7+sortable-macro-dev (>= 0.7.1-~~),
|
librust-proxmox-0.11+sortable-macro-dev,
|
||||||
librust-proxmox-0.7+websocket-dev (>= 0.7.1-~~),
|
librust-proxmox-0.11+websocket-dev,
|
||||||
librust-proxmox-fuse-0.1+default-dev,
|
librust-proxmox-fuse-0.1+default-dev (>= 0.1.1-~~),
|
||||||
librust-pxar-0.6+default-dev (>= 0.6.1-~~),
|
librust-pxar-0.10+default-dev,
|
||||||
librust-pxar-0.6+futures-io-dev (>= 0.6.1-~~),
|
librust-pxar-0.10+tokio-io-dev,
|
||||||
librust-pxar-0.6+tokio-io-dev (>= 0.6.1-~~),
|
|
||||||
librust-regex-1+default-dev (>= 1.2-~~),
|
librust-regex-1+default-dev (>= 1.2-~~),
|
||||||
librust-rustyline-6+default-dev,
|
librust-rustyline-7+default-dev,
|
||||||
librust-serde-1+default-dev,
|
librust-serde-1+default-dev,
|
||||||
librust-serde-1+derive-dev,
|
librust-serde-1+derive-dev,
|
||||||
librust-serde-json-1+default-dev,
|
librust-serde-json-1+default-dev,
|
||||||
librust-siphasher-0.3+default-dev,
|
librust-siphasher-0.3+default-dev,
|
||||||
librust-syslog-4+default-dev,
|
librust-syslog-4+default-dev,
|
||||||
librust-tokio-0.2+blocking-dev (>= 0.2.9-~~),
|
librust-tokio-1+default-dev,
|
||||||
librust-tokio-0.2+default-dev (>= 0.2.9-~~),
|
librust-tokio-1+fs-dev,
|
||||||
librust-tokio-0.2+dns-dev (>= 0.2.9-~~),
|
librust-tokio-1+io-util-dev,
|
||||||
librust-tokio-0.2+fs-dev (>= 0.2.9-~~),
|
librust-tokio-1+macros-dev,
|
||||||
librust-tokio-0.2+io-util-dev (>= 0.2.9-~~),
|
librust-tokio-1+net-dev,
|
||||||
librust-tokio-0.2+macros-dev (>= 0.2.9-~~),
|
librust-tokio-1+parking-lot-dev,
|
||||||
librust-tokio-0.2+process-dev (>= 0.2.9-~~),
|
librust-tokio-1+process-dev,
|
||||||
librust-tokio-0.2+rt-threaded-dev (>= 0.2.9-~~),
|
librust-tokio-1+rt-dev,
|
||||||
librust-tokio-0.2+signal-dev (>= 0.2.9-~~),
|
librust-tokio-1+rt-multi-thread-dev,
|
||||||
librust-tokio-0.2+stream-dev (>= 0.2.9-~~),
|
librust-tokio-1+signal-dev,
|
||||||
librust-tokio-0.2+tcp-dev (>= 0.2.9-~~),
|
librust-tokio-1+time-dev,
|
||||||
librust-tokio-0.2+time-dev (>= 0.2.9-~~),
|
librust-tokio-openssl-0.6+default-dev (>= 0.6.1-~~),
|
||||||
librust-tokio-0.2+uds-dev (>= 0.2.9-~~),
|
librust-tokio-stream-0.1+default-dev,
|
||||||
librust-tokio-openssl-0.4+default-dev,
|
librust-tokio-util-0.6+codec-dev,
|
||||||
librust-tokio-util-0.3+codec-dev,
|
librust-tokio-util-0.6+default-dev,
|
||||||
librust-tokio-util-0.3+default-dev,
|
|
||||||
librust-tower-service-0.3+default-dev,
|
librust-tower-service-0.3+default-dev,
|
||||||
librust-udev-0.4+default-dev | librust-udev-0.3+default-dev,
|
librust-udev-0.4+default-dev | librust-udev-0.3+default-dev,
|
||||||
librust-url-2+default-dev (>= 2.1-~~),
|
librust-url-2+default-dev (>= 2.1-~~),
|
||||||
librust-walkdir-2+default-dev,
|
librust-walkdir-2+default-dev,
|
||||||
|
librust-webauthn-rs-0.2+default-dev (>= 0.2.5-~~),
|
||||||
librust-xdg-2+default-dev (>= 2.2-~~),
|
librust-xdg-2+default-dev (>= 2.2-~~),
|
||||||
librust-zstd-0.4+bindgen-dev,
|
librust-zstd-0.4+bindgen-dev,
|
||||||
librust-zstd-0.4+default-dev,
|
librust-zstd-0.4+default-dev,
|
||||||
@ -77,34 +77,40 @@ Build-Depends: debhelper (>= 11),
|
|||||||
libfuse3-dev,
|
libfuse3-dev,
|
||||||
libsystemd-dev,
|
libsystemd-dev,
|
||||||
uuid-dev,
|
uuid-dev,
|
||||||
debhelper (>= 12~),
|
libsgutils2-dev,
|
||||||
bash-completion,
|
bash-completion,
|
||||||
pve-eslint (>= 7.12.1-1),
|
debhelper (>= 12~),
|
||||||
python3-docutils,
|
|
||||||
python3-pygments,
|
|
||||||
rsync,
|
|
||||||
fonts-dejavu-core <!nodoc>,
|
fonts-dejavu-core <!nodoc>,
|
||||||
fonts-lato <!nodoc>,
|
fonts-lato <!nodoc>,
|
||||||
fonts-open-sans <!nodoc>,
|
fonts-open-sans <!nodoc>,
|
||||||
graphviz <!nodoc>,
|
graphviz <!nodoc>,
|
||||||
latexmk <!nodoc>,
|
latexmk <!nodoc>,
|
||||||
|
patchelf,
|
||||||
|
pve-eslint (>= 7.18.0-1),
|
||||||
|
python3-docutils,
|
||||||
|
python3-pygments,
|
||||||
python3-sphinx <!nodoc>,
|
python3-sphinx <!nodoc>,
|
||||||
|
rsync,
|
||||||
texlive-fonts-extra <!nodoc>,
|
texlive-fonts-extra <!nodoc>,
|
||||||
texlive-fonts-recommended <!nodoc>,
|
texlive-fonts-recommended <!nodoc>,
|
||||||
texlive-xetex <!nodoc>,
|
texlive-xetex <!nodoc>,
|
||||||
xindy <!nodoc>
|
xindy <!nodoc>
|
||||||
Maintainer: Proxmox Support Team <support@proxmox.com>
|
Maintainer: Proxmox Support Team <support@proxmox.com>
|
||||||
Standards-Version: 4.4.1
|
Standards-Version: 4.4.1
|
||||||
Vcs-Git:
|
Vcs-Git: git://git.proxmox.com/git/proxmox-backup.git
|
||||||
Vcs-Browser:
|
Vcs-Browser: https://git.proxmox.com/?p=proxmox-backup.git;a=summary
|
||||||
Homepage: https://www.proxmox.com
|
Homepage: https://www.proxmox.com
|
||||||
|
|
||||||
Package: proxmox-backup-server
|
Package: proxmox-backup-server
|
||||||
Architecture: any
|
Architecture: any
|
||||||
Depends: fonts-font-awesome,
|
Depends: fonts-font-awesome,
|
||||||
libjs-extjs (>= 6.0.1),
|
libjs-extjs (>= 6.0.1),
|
||||||
|
libjs-qrcodejs (>= 1.20201119),
|
||||||
|
libsgutils2-2,
|
||||||
libzstd1 (>= 1.3.8),
|
libzstd1 (>= 1.3.8),
|
||||||
lvm2,
|
lvm2,
|
||||||
|
mt-st,
|
||||||
|
mtx,
|
||||||
openssh-server,
|
openssh-server,
|
||||||
pbs-i18n,
|
pbs-i18n,
|
||||||
postfix | mail-transport-agent,
|
postfix | mail-transport-agent,
|
||||||
@ -112,6 +118,7 @@ Depends: fonts-font-awesome,
|
|||||||
proxmox-mini-journalreader,
|
proxmox-mini-journalreader,
|
||||||
proxmox-widget-toolkit (>= 2.3-6),
|
proxmox-widget-toolkit (>= 2.3-6),
|
||||||
pve-xtermjs (>= 4.7.0-1),
|
pve-xtermjs (>= 4.7.0-1),
|
||||||
|
sg3-utils,
|
||||||
smartmontools,
|
smartmontools,
|
||||||
${misc:Depends},
|
${misc:Depends},
|
||||||
${shlibs:Depends},
|
${shlibs:Depends},
|
||||||
@ -134,6 +141,7 @@ Package: proxmox-backup-docs
|
|||||||
Build-Profiles: <!nodoc>
|
Build-Profiles: <!nodoc>
|
||||||
Section: doc
|
Section: doc
|
||||||
Depends: libjs-extjs,
|
Depends: libjs-extjs,
|
||||||
|
libjs-mathjax,
|
||||||
${misc:Depends},
|
${misc:Depends},
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Description: Proxmox Backup Documentation
|
Description: Proxmox Backup Documentation
|
||||||
|
6
debian/control.in
vendored
6
debian/control.in
vendored
@ -2,8 +2,12 @@ Package: proxmox-backup-server
|
|||||||
Architecture: any
|
Architecture: any
|
||||||
Depends: fonts-font-awesome,
|
Depends: fonts-font-awesome,
|
||||||
libjs-extjs (>= 6.0.1),
|
libjs-extjs (>= 6.0.1),
|
||||||
|
libjs-qrcodejs (>= 1.20201119),
|
||||||
|
libsgutils2-2,
|
||||||
libzstd1 (>= 1.3.8),
|
libzstd1 (>= 1.3.8),
|
||||||
lvm2,
|
lvm2,
|
||||||
|
mt-st,
|
||||||
|
mtx,
|
||||||
openssh-server,
|
openssh-server,
|
||||||
pbs-i18n,
|
pbs-i18n,
|
||||||
postfix | mail-transport-agent,
|
postfix | mail-transport-agent,
|
||||||
@ -11,6 +15,7 @@ Depends: fonts-font-awesome,
|
|||||||
proxmox-mini-journalreader,
|
proxmox-mini-journalreader,
|
||||||
proxmox-widget-toolkit (>= 2.3-6),
|
proxmox-widget-toolkit (>= 2.3-6),
|
||||||
pve-xtermjs (>= 4.7.0-1),
|
pve-xtermjs (>= 4.7.0-1),
|
||||||
|
sg3-utils,
|
||||||
smartmontools,
|
smartmontools,
|
||||||
${misc:Depends},
|
${misc:Depends},
|
||||||
${shlibs:Depends},
|
${shlibs:Depends},
|
||||||
@ -33,6 +38,7 @@ Package: proxmox-backup-docs
|
|||||||
Build-Profiles: <!nodoc>
|
Build-Profiles: <!nodoc>
|
||||||
Section: doc
|
Section: doc
|
||||||
Depends: libjs-extjs,
|
Depends: libjs-extjs,
|
||||||
|
libjs-mathjax,
|
||||||
${misc:Depends},
|
${misc:Depends},
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Description: Proxmox Backup Documentation
|
Description: Proxmox Backup Documentation
|
||||||
|
2
debian/copyright
vendored
2
debian/copyright
vendored
@ -1,4 +1,4 @@
|
|||||||
Copyright (C) 2019 Proxmox Server Solutions GmbH
|
Copyright (C) 2019 - 2021 Proxmox Server Solutions GmbH
|
||||||
|
|
||||||
This software is written by Proxmox Server Solutions GmbH <support@proxmox.com>
|
This software is written by Proxmox Server Solutions GmbH <support@proxmox.com>
|
||||||
|
|
||||||
|
22
debian/debcargo.toml
vendored
22
debian/debcargo.toml
vendored
@ -2,33 +2,32 @@ overlay = "."
|
|||||||
crate_src_path = ".."
|
crate_src_path = ".."
|
||||||
whitelist = ["tests/*.c"]
|
whitelist = ["tests/*.c"]
|
||||||
|
|
||||||
# needed for pinutils alpha
|
maintainer = "Proxmox Support Team <support@proxmox.com>"
|
||||||
allow_prerelease_deps = true
|
|
||||||
|
|
||||||
[source]
|
[source]
|
||||||
# TODO: update once public
|
vcs_git = "git://git.proxmox.com/git/proxmox-backup.git"
|
||||||
vcs_git = ""
|
vcs_browser = "https://git.proxmox.com/?p=proxmox-backup.git;a=summary"
|
||||||
vcs_browser = ""
|
|
||||||
maintainer = "Proxmox Support Team <support@proxmox.com>"
|
|
||||||
section = "admin"
|
section = "admin"
|
||||||
build_depends = [
|
build_depends = [
|
||||||
"debhelper (>= 12~)",
|
|
||||||
"bash-completion",
|
"bash-completion",
|
||||||
"pve-eslint (>= 7.12.1-1)",
|
"debhelper (>= 12~)",
|
||||||
"python3-docutils",
|
|
||||||
"python3-pygments",
|
|
||||||
"rsync",
|
|
||||||
"fonts-dejavu-core <!nodoc>",
|
"fonts-dejavu-core <!nodoc>",
|
||||||
"fonts-lato <!nodoc>",
|
"fonts-lato <!nodoc>",
|
||||||
"fonts-open-sans <!nodoc>",
|
"fonts-open-sans <!nodoc>",
|
||||||
"graphviz <!nodoc>",
|
"graphviz <!nodoc>",
|
||||||
"latexmk <!nodoc>",
|
"latexmk <!nodoc>",
|
||||||
|
"patchelf",
|
||||||
|
"pve-eslint (>= 7.18.0-1)",
|
||||||
|
"python3-docutils",
|
||||||
|
"python3-pygments",
|
||||||
"python3-sphinx <!nodoc>",
|
"python3-sphinx <!nodoc>",
|
||||||
|
"rsync",
|
||||||
"texlive-fonts-extra <!nodoc>",
|
"texlive-fonts-extra <!nodoc>",
|
||||||
"texlive-fonts-recommended <!nodoc>",
|
"texlive-fonts-recommended <!nodoc>",
|
||||||
"texlive-xetex <!nodoc>",
|
"texlive-xetex <!nodoc>",
|
||||||
"xindy <!nodoc>",
|
"xindy <!nodoc>",
|
||||||
]
|
]
|
||||||
|
|
||||||
build_depends_excludes = [
|
build_depends_excludes = [
|
||||||
"debhelper (>=11)",
|
"debhelper (>=11)",
|
||||||
]
|
]
|
||||||
@ -39,4 +38,5 @@ depends = [
|
|||||||
"libfuse3-dev",
|
"libfuse3-dev",
|
||||||
"libsystemd-dev",
|
"libsystemd-dev",
|
||||||
"uuid-dev",
|
"uuid-dev",
|
||||||
|
"libsgutils2-dev",
|
||||||
]
|
]
|
||||||
|
3
debian/pmt.bc
vendored
Normal file
3
debian/pmt.bc
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# pmt bash completion
|
||||||
|
|
||||||
|
complete -C 'pmt bashcomplete' pmt
|
3
debian/pmtx.bc
vendored
Normal file
3
debian/pmtx.bc
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# pmtx bash completion
|
||||||
|
|
||||||
|
complete -C 'pmtx bashcomplete' pmtx
|
16
debian/postinst
vendored
16
debian/postinst
vendored
@ -6,10 +6,21 @@ set -e
|
|||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
configure)
|
configure)
|
||||||
|
# need to have user backup in the tape group
|
||||||
|
usermod -a -G tape backup
|
||||||
|
|
||||||
# modeled after dh_systemd_start output
|
# modeled after dh_systemd_start output
|
||||||
systemctl --system daemon-reload >/dev/null || true
|
systemctl --system daemon-reload >/dev/null || true
|
||||||
if [ -n "$2" ]; then
|
if [ -n "$2" ]; then
|
||||||
|
if dpkg --compare-versions "$2" 'lt' '1.0.7-1'; then
|
||||||
|
# there was an issue with reloading and systemd being confused in older daemon versions
|
||||||
|
# so restart instead of reload if upgrading from there, see commit 0ec79339f7aebf9
|
||||||
|
# FIXME: remove with PBS 2.1
|
||||||
|
echo "Upgrading from older proxmox-backup-server: restart (not reload) daemons"
|
||||||
|
_dh_action=try-restart
|
||||||
|
else
|
||||||
_dh_action=try-reload-or-restart
|
_dh_action=try-reload-or-restart
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
_dh_action=start
|
_dh_action=start
|
||||||
fi
|
fi
|
||||||
@ -37,12 +48,17 @@ case "$1" in
|
|||||||
/etc/proxmox-backup/remote.cfg || true
|
/etc/proxmox-backup/remote.cfg || true
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
# FIXME: remove with 2.0
|
||||||
|
if [ -d "/var/lib/proxmox-backup/tape" ] &&
|
||||||
|
[ "$(stat --printf '%a' '/var/lib/proxmox-backup/tape')" != "750" ]; then
|
||||||
|
chmod 0750 /var/lib/proxmox-backup/tape || true
|
||||||
fi
|
fi
|
||||||
# FIXME: Remove in future version once we're sure no broken entries remain in anyone's files
|
# FIXME: Remove in future version once we're sure no broken entries remain in anyone's files
|
||||||
if grep -q -e ':termproxy::[^@]\+: ' /var/log/proxmox-backup/tasks/active; then
|
if grep -q -e ':termproxy::[^@]\+: ' /var/log/proxmox-backup/tasks/active; then
|
||||||
echo "Fixing up termproxy user id in task log..."
|
echo "Fixing up termproxy user id in task log..."
|
||||||
flock -w 30 /var/log/proxmox-backup/tasks/active.lock sed -i 's/:termproxy::\([^@]\+\): /:termproxy::\1@pam: /' /var/log/proxmox-backup/tasks/active || true
|
flock -w 30 /var/log/proxmox-backup/tasks/active.lock sed -i 's/:termproxy::\([^@]\+\): /:termproxy::\1@pam: /' /var/log/proxmox-backup/tasks/active || true
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
|
|
||||||
abort-upgrade|abort-remove|abort-deconfigure)
|
abort-upgrade|abort-remove|abort-deconfigure)
|
||||||
|
3
debian/proxmox-backup-docs.links
vendored
3
debian/proxmox-backup-docs.links
vendored
@ -1,2 +1,5 @@
|
|||||||
/usr/share/doc/proxmox-backup/proxmox-backup.pdf /usr/share/doc/proxmox-backup/html/proxmox-backup.pdf
|
/usr/share/doc/proxmox-backup/proxmox-backup.pdf /usr/share/doc/proxmox-backup/html/proxmox-backup.pdf
|
||||||
/usr/share/javascript/extjs /usr/share/doc/proxmox-backup/html/prune-simulator/extjs
|
/usr/share/javascript/extjs /usr/share/doc/proxmox-backup/html/prune-simulator/extjs
|
||||||
|
/usr/share/javascript/extjs /usr/share/doc/proxmox-backup/html/lto-barcode/extjs
|
||||||
|
/usr/share/javascript/extjs /usr/share/doc/proxmox-backup/html/api-viewer/extjs
|
||||||
|
/usr/share/javascript/mathjax /usr/share/doc/proxmox-backup/html/_static/mathjax
|
||||||
|
3
debian/proxmox-backup-server.bash-completion
vendored
3
debian/proxmox-backup-server.bash-completion
vendored
@ -1 +1,4 @@
|
|||||||
debian/proxmox-backup-manager.bc proxmox-backup-manager
|
debian/proxmox-backup-manager.bc proxmox-backup-manager
|
||||||
|
debian/proxmox-tape.bc proxmox-tape
|
||||||
|
debian/pmtx.bc pmtx
|
||||||
|
debian/pmt.bc pmt
|
||||||
|
22
debian/proxmox-backup-server.install
vendored
22
debian/proxmox-backup-server.install
vendored
@ -8,12 +8,30 @@ usr/lib/x86_64-linux-gnu/proxmox-backup/proxmox-backup-api
|
|||||||
usr/lib/x86_64-linux-gnu/proxmox-backup/proxmox-backup-proxy
|
usr/lib/x86_64-linux-gnu/proxmox-backup/proxmox-backup-proxy
|
||||||
usr/lib/x86_64-linux-gnu/proxmox-backup/proxmox-backup-banner
|
usr/lib/x86_64-linux-gnu/proxmox-backup/proxmox-backup-banner
|
||||||
usr/lib/x86_64-linux-gnu/proxmox-backup/proxmox-daily-update
|
usr/lib/x86_64-linux-gnu/proxmox-backup/proxmox-daily-update
|
||||||
|
usr/lib/x86_64-linux-gnu/proxmox-backup/sg-tape-cmd
|
||||||
usr/sbin/proxmox-backup-manager
|
usr/sbin/proxmox-backup-manager
|
||||||
|
usr/bin/pmtx
|
||||||
|
usr/bin/pmt
|
||||||
|
usr/bin/proxmox-tape
|
||||||
usr/share/javascript/proxmox-backup/index.hbs
|
usr/share/javascript/proxmox-backup/index.hbs
|
||||||
usr/share/javascript/proxmox-backup/css/ext6-pbs.css
|
usr/share/javascript/proxmox-backup/css/ext6-pbs.css
|
||||||
usr/share/javascript/proxmox-backup/images/logo-128.png
|
usr/share/javascript/proxmox-backup/images
|
||||||
usr/share/javascript/proxmox-backup/images/proxmox_logo.png
|
|
||||||
usr/share/javascript/proxmox-backup/js/proxmox-backup-gui.js
|
usr/share/javascript/proxmox-backup/js/proxmox-backup-gui.js
|
||||||
usr/share/man/man1/proxmox-backup-manager.1
|
usr/share/man/man1/proxmox-backup-manager.1
|
||||||
usr/share/man/man1/proxmox-backup-proxy.1
|
usr/share/man/man1/proxmox-backup-proxy.1
|
||||||
|
usr/share/man/man1/proxmox-tape.1
|
||||||
|
usr/share/man/man1/pmtx.1
|
||||||
|
usr/share/man/man1/pmt.1
|
||||||
|
usr/share/man/man5/acl.cfg.5
|
||||||
|
usr/share/man/man5/datastore.cfg.5
|
||||||
|
usr/share/man/man5/user.cfg.5
|
||||||
|
usr/share/man/man5/remote.cfg.5
|
||||||
|
usr/share/man/man5/sync.cfg.5
|
||||||
|
usr/share/man/man5/verification.cfg.5
|
||||||
|
usr/share/man/man5/media-pool.cfg.5
|
||||||
|
usr/share/man/man5/tape.cfg.5
|
||||||
|
usr/share/man/man5/tape-job.cfg.5
|
||||||
usr/share/zsh/vendor-completions/_proxmox-backup-manager
|
usr/share/zsh/vendor-completions/_proxmox-backup-manager
|
||||||
|
usr/share/zsh/vendor-completions/_proxmox-tape
|
||||||
|
usr/share/zsh/vendor-completions/_pmtx
|
||||||
|
usr/share/zsh/vendor-completions/_pmt
|
||||||
|
3
debian/proxmox-tape.bc
vendored
Normal file
3
debian/proxmox-tape.bc
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# proxmox-tape bash completion
|
||||||
|
|
||||||
|
complete -C 'proxmox-tape bashcomplete' proxmox-tape
|
10
debian/rules
vendored
10
debian/rules
vendored
@ -42,10 +42,20 @@ override_dh_installsystemd:
|
|||||||
# note: we start/try-reload-restart services manually in postinst
|
# note: we start/try-reload-restart services manually in postinst
|
||||||
dh_installsystemd --no-start --no-restart-after-upgrade
|
dh_installsystemd --no-start --no-restart-after-upgrade
|
||||||
|
|
||||||
|
override_dh_fixperms:
|
||||||
|
dh_fixperms --exclude sg-tape-cmd
|
||||||
|
|
||||||
# workaround https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=933541
|
# workaround https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=933541
|
||||||
# TODO: remove once available (Debian 11 ?)
|
# TODO: remove once available (Debian 11 ?)
|
||||||
override_dh_dwz:
|
override_dh_dwz:
|
||||||
dh_dwz --no-dwz-multifile
|
dh_dwz --no-dwz-multifile
|
||||||
|
|
||||||
|
override_dh_strip:
|
||||||
|
dh_strip
|
||||||
|
for exe in $$(find debian/proxmox-backup-client/usr \
|
||||||
|
debian/proxmox-backup-server/usr -executable -type f); do \
|
||||||
|
debian/scripts/elf-strip-unused-dependencies.sh "$$exe" || true; \
|
||||||
|
done
|
||||||
|
|
||||||
override_dh_compress:
|
override_dh_compress:
|
||||||
dh_compress -X.pdf
|
dh_compress -X.pdf
|
||||||
|
20
debian/scripts/elf-strip-unused-dependencies.sh
vendored
Executable file
20
debian/scripts/elf-strip-unused-dependencies.sh
vendored
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
binary=$1
|
||||||
|
|
||||||
|
exec 3< <(ldd -u "$binary" | grep -oP '[^/:]+$')
|
||||||
|
|
||||||
|
patchargs=""
|
||||||
|
dropped=""
|
||||||
|
while read -r dep; do
|
||||||
|
dropped="$dep $dropped"
|
||||||
|
patchargs="--remove-needed $dep $patchargs"
|
||||||
|
done <&3
|
||||||
|
exec 3<&-
|
||||||
|
|
||||||
|
if [[ $dropped == "" ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "patchelf '$binary' - removing unused dependencies:\n $dropped"
|
||||||
|
patchelf $patchargs $binary
|
@ -5,6 +5,7 @@ LIBDIR = $(PREFIX)/lib
|
|||||||
LIBEXECDIR = $(LIBDIR)
|
LIBEXECDIR = $(LIBDIR)
|
||||||
DATAROOTDIR = $(PREFIX)/share
|
DATAROOTDIR = $(PREFIX)/share
|
||||||
MAN1DIR = $(PREFIX)/share/man/man1
|
MAN1DIR = $(PREFIX)/share/man/man1
|
||||||
|
MAN5DIR = $(PREFIX)/share/man/man5
|
||||||
DOCDIR = $(PREFIX)/share/doc/proxmox-backup
|
DOCDIR = $(PREFIX)/share/doc/proxmox-backup
|
||||||
JSDIR = $(DATAROOTDIR)/javascript/proxmox-backup
|
JSDIR = $(DATAROOTDIR)/javascript/proxmox-backup
|
||||||
SYSCONFDIR = /etc
|
SYSCONFDIR = /etc
|
||||||
|
151
docs/Makefile
151
docs/Makefile
@ -1,25 +1,66 @@
|
|||||||
include ../defines.mk
|
include ../defines.mk
|
||||||
|
|
||||||
GENERATED_SYNOPSIS := \
|
GENERATED_SYNOPSIS := \
|
||||||
|
proxmox-tape/synopsis.rst \
|
||||||
proxmox-backup-client/synopsis.rst \
|
proxmox-backup-client/synopsis.rst \
|
||||||
proxmox-backup-client/catalog-shell-synopsis.rst \
|
proxmox-backup-client/catalog-shell-synopsis.rst \
|
||||||
proxmox-backup-manager/synopsis.rst \
|
proxmox-backup-manager/synopsis.rst \
|
||||||
pxar/synopsis.rst \
|
pxar/synopsis.rst \
|
||||||
backup-protocol-api.rst \
|
pmtx/synopsis.rst \
|
||||||
reader-protocol-api.rst
|
pmt/synopsis.rst \
|
||||||
|
config/media-pool/config.rst \
|
||||||
|
config/tape/config.rst \
|
||||||
|
config/tape-job/config.rst \
|
||||||
|
config/user/config.rst \
|
||||||
|
config/remote/config.rst \
|
||||||
|
config/sync/config.rst \
|
||||||
|
config/verification/config.rst \
|
||||||
|
config/acl/roles.rst \
|
||||||
|
config/datastore/config.rst
|
||||||
|
|
||||||
MANUAL_PAGES := \
|
MAN1_PAGES := \
|
||||||
pxar.1 \
|
pxar.1 \
|
||||||
|
pmtx.1 \
|
||||||
|
pmt.1 \
|
||||||
|
proxmox-tape.1 \
|
||||||
proxmox-backup-proxy.1 \
|
proxmox-backup-proxy.1 \
|
||||||
proxmox-backup-client.1 \
|
proxmox-backup-client.1 \
|
||||||
proxmox-backup-manager.1
|
proxmox-backup-manager.1
|
||||||
|
|
||||||
|
MAN5_PAGES := \
|
||||||
|
media-pool.cfg.5 \
|
||||||
|
tape.cfg.5 \
|
||||||
|
tape-job.cfg.5 \
|
||||||
|
acl.cfg.5 \
|
||||||
|
user.cfg.5 \
|
||||||
|
remote.cfg.5 \
|
||||||
|
sync.cfg.5 \
|
||||||
|
verification.cfg.5 \
|
||||||
|
datastore.cfg.5
|
||||||
|
|
||||||
PRUNE_SIMULATOR_FILES := \
|
PRUNE_SIMULATOR_FILES := \
|
||||||
prune-simulator/index.html \
|
prune-simulator/index.html \
|
||||||
prune-simulator/documentation.html \
|
prune-simulator/documentation.html \
|
||||||
prune-simulator/clear-trigger.png \
|
prune-simulator/clear-trigger.png \
|
||||||
prune-simulator/prune-simulator.js
|
prune-simulator/prune-simulator.js
|
||||||
|
|
||||||
|
LTO_BARCODE_FILES := \
|
||||||
|
lto-barcode/index.html \
|
||||||
|
lto-barcode/code39.js \
|
||||||
|
lto-barcode/prefix-field.js \
|
||||||
|
lto-barcode/label-style.js \
|
||||||
|
lto-barcode/tape-type.js \
|
||||||
|
lto-barcode/paper-size.js \
|
||||||
|
lto-barcode/page-layout.js \
|
||||||
|
lto-barcode/page-calibration.js \
|
||||||
|
lto-barcode/label-list.js \
|
||||||
|
lto-barcode/label-setup.js \
|
||||||
|
lto-barcode/lto-barcode.js
|
||||||
|
|
||||||
|
API_VIEWER_SOURCES= \
|
||||||
|
api-viewer/index.html \
|
||||||
|
api-viewer/apidoc.js
|
||||||
|
|
||||||
# Sphinx documentation setup
|
# Sphinx documentation setup
|
||||||
SPHINXOPTS =
|
SPHINXOPTS =
|
||||||
SPHINXBUILD = sphinx-build
|
SPHINXBUILD = sphinx-build
|
||||||
@ -36,15 +77,7 @@ endif
|
|||||||
# Sphinx internal variables.
|
# Sphinx internal variables.
|
||||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(SPHINXOPTS) .
|
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(SPHINXOPTS) .
|
||||||
|
|
||||||
all: ${MANUAL_PAGES}
|
all: ${MAN1_PAGES} ${MAN5_PAGES}
|
||||||
|
|
||||||
# Extract backup protocol docs
|
|
||||||
backup-protocol-api.rst: ${COMPILEDIR}/dump-backup-api
|
|
||||||
${COMPILEDIR}/dump-backup-api >$@
|
|
||||||
|
|
||||||
# Extract reader protocol docs
|
|
||||||
reader-protocol-api.rst: ${COMPILEDIR}/dump-reader-api
|
|
||||||
${COMPILEDIR}/dump-backup-api >$@
|
|
||||||
|
|
||||||
# Build manual pages using rst2man
|
# Build manual pages using rst2man
|
||||||
|
|
||||||
@ -54,6 +87,80 @@ pxar/synopsis.rst: ${COMPILEDIR}/pxar
|
|||||||
pxar.1: pxar/man1.rst pxar/description.rst pxar/synopsis.rst
|
pxar.1: pxar/man1.rst pxar/description.rst pxar/synopsis.rst
|
||||||
rst2man $< >$@
|
rst2man $< >$@
|
||||||
|
|
||||||
|
|
||||||
|
pmtx/synopsis.rst: ${COMPILEDIR}/pmtx
|
||||||
|
${COMPILEDIR}/pmtx printdoc > pmtx/synopsis.rst
|
||||||
|
|
||||||
|
pmtx.1: pmtx/man1.rst pmtx/description.rst pmtx/synopsis.rst
|
||||||
|
rst2man $< >$@
|
||||||
|
|
||||||
|
|
||||||
|
pmt/synopsis.rst: ${COMPILEDIR}/pmt
|
||||||
|
${COMPILEDIR}/pmt printdoc > pmt/synopsis.rst
|
||||||
|
|
||||||
|
pmt.1: pmt/man1.rst pmt/description.rst pmt/options.rst pmt/synopsis.rst
|
||||||
|
rst2man $< >$@
|
||||||
|
|
||||||
|
config/datastore/config.rst: ${COMPILEDIR}/docgen
|
||||||
|
${COMPILEDIR}/docgen datastore.cfg >$@
|
||||||
|
|
||||||
|
datastore.cfg.5: config/datastore/man5.rst config/datastore/config.rst config/datastore/format.rst
|
||||||
|
rst2man $< >$@
|
||||||
|
|
||||||
|
config/user/config.rst: ${COMPILEDIR}/docgen
|
||||||
|
${COMPILEDIR}/docgen user.cfg >$@
|
||||||
|
|
||||||
|
user.cfg.5: config/user/man5.rst config/user/config.rst config/user/format.rst
|
||||||
|
rst2man $< >$@
|
||||||
|
|
||||||
|
config/remote/config.rst: ${COMPILEDIR}/docgen
|
||||||
|
${COMPILEDIR}/docgen remote.cfg >$@
|
||||||
|
|
||||||
|
remote.cfg.5: config/remote/man5.rst config/remote/config.rst config/remote/format.rst
|
||||||
|
rst2man $< >$@
|
||||||
|
|
||||||
|
config/sync/config.rst: ${COMPILEDIR}/docgen
|
||||||
|
${COMPILEDIR}/docgen sync.cfg >$@
|
||||||
|
|
||||||
|
sync.cfg.5: config/sync/man5.rst config/sync/config.rst config/sync/format.rst
|
||||||
|
rst2man $< >$@
|
||||||
|
|
||||||
|
config/verification/config.rst: ${COMPILEDIR}/docgen
|
||||||
|
${COMPILEDIR}/docgen verification.cfg >$@
|
||||||
|
|
||||||
|
verification.cfg.5: config/verification/man5.rst config/verification/config.rst config/verification/format.rst
|
||||||
|
rst2man $< >$@
|
||||||
|
|
||||||
|
config/acl/roles.rst: ${COMPILEDIR}/docgen
|
||||||
|
${COMPILEDIR}/docgen "config::acl::Role" >$@
|
||||||
|
|
||||||
|
acl.cfg.5: config/acl/man5.rst config/acl/roles.rst config/acl/format.rst
|
||||||
|
rst2man $< >$@
|
||||||
|
|
||||||
|
config/media-pool/config.rst: ${COMPILEDIR}/docgen
|
||||||
|
${COMPILEDIR}/docgen media-pool.cfg >$@
|
||||||
|
|
||||||
|
media-pool.cfg.5: config/media-pool/man5.rst config/media-pool/config.rst config/media-pool/format.rst
|
||||||
|
rst2man $< >$@
|
||||||
|
|
||||||
|
config/tape/config.rst: ${COMPILEDIR}/docgen
|
||||||
|
${COMPILEDIR}/docgen tape.cfg >$@
|
||||||
|
|
||||||
|
tape.cfg.5: config/tape/man5.rst config/tape/config.rst config/tape/format.rst
|
||||||
|
rst2man $< >$@
|
||||||
|
|
||||||
|
config/tape-job/config.rst: ${COMPILEDIR}/docgen
|
||||||
|
${COMPILEDIR}/docgen tape-job.cfg >$@
|
||||||
|
|
||||||
|
tape-job.cfg.5: config/tape-job/man5.rst config/tape-job/config.rst config/tape-job/format.rst
|
||||||
|
rst2man $< >$@
|
||||||
|
|
||||||
|
proxmox-tape/synopsis.rst: ${COMPILEDIR}/proxmox-tape
|
||||||
|
${COMPILEDIR}/proxmox-tape printdoc > proxmox-tape/synopsis.rst
|
||||||
|
|
||||||
|
proxmox-tape.1: proxmox-tape/man1.rst proxmox-tape/description.rst proxmox-tape/synopsis.rst
|
||||||
|
rst2man $< >$@
|
||||||
|
|
||||||
proxmox-backup-client/synopsis.rst: ${COMPILEDIR}/proxmox-backup-client
|
proxmox-backup-client/synopsis.rst: ${COMPILEDIR}/proxmox-backup-client
|
||||||
${COMPILEDIR}/proxmox-backup-client printdoc > proxmox-backup-client/synopsis.rst
|
${COMPILEDIR}/proxmox-backup-client printdoc > proxmox-backup-client/synopsis.rst
|
||||||
|
|
||||||
@ -78,12 +185,22 @@ onlinehelpinfo:
|
|||||||
$(SPHINXBUILD) -b proxmox-scanrefs $(ALLSPHINXOPTS) $(BUILDDIR)/scanrefs
|
$(SPHINXBUILD) -b proxmox-scanrefs $(ALLSPHINXOPTS) $(BUILDDIR)/scanrefs
|
||||||
@echo "Build finished. OnlineHelpInfo.js is in $(BUILDDIR)/scanrefs."
|
@echo "Build finished. OnlineHelpInfo.js is in $(BUILDDIR)/scanrefs."
|
||||||
|
|
||||||
|
api-viewer/apidata.js: ${COMPILEDIR}/docgen
|
||||||
|
${COMPILEDIR}/docgen apidata.js >$@
|
||||||
|
|
||||||
|
api-viewer/apidoc.js: api-viewer/apidata.js api-viewer/PBSAPI.js
|
||||||
|
cat api-viewer/apidata.js api-viewer/PBSAPI.js >$@
|
||||||
|
|
||||||
.PHONY: html
|
.PHONY: html
|
||||||
html: ${GENERATED_SYNOPSIS} images/proxmox-logo.svg custom.css conf.py ${PRUNE_SIMULATOR_FILES}
|
html: ${GENERATED_SYNOPSIS} images/proxmox-logo.svg custom.css conf.py ${PRUNE_SIMULATOR_FILES} ${LTO_BARCODE_FILES} ${API_VIEWER_SOURCES}
|
||||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||||
install -m 0644 custom.js custom.css images/proxmox-logo.svg $(BUILDDIR)/html/_static/
|
install -m 0644 custom.js custom.css images/proxmox-logo.svg $(BUILDDIR)/html/_static/
|
||||||
install -dm 0755 $(BUILDDIR)/html/prune-simulator
|
install -dm 0755 $(BUILDDIR)/html/prune-simulator
|
||||||
install -m 0644 ${PRUNE_SIMULATOR_FILES} $(BUILDDIR)/html/prune-simulator
|
install -m 0644 ${PRUNE_SIMULATOR_FILES} $(BUILDDIR)/html/prune-simulator
|
||||||
|
install -dm 0755 $(BUILDDIR)/html/lto-barcode
|
||||||
|
install -m 0644 ${LTO_BARCODE_FILES} $(BUILDDIR)/html/lto-barcode
|
||||||
|
install -dm 0755 $(BUILDDIR)/html/api-viewer
|
||||||
|
install -m 0644 ${API_VIEWER_SOURCES} $(BUILDDIR)/html/api-viewer
|
||||||
@echo
|
@echo
|
||||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||||
|
|
||||||
@ -102,12 +219,14 @@ epub3: ${GENERATED_SYNOPSIS}
|
|||||||
@echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
|
@echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3."
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -r -f *~ *.1 ${BUILDDIR} ${GENERATED_SYNOPSIS}
|
rm -r -f *~ *.1 ${BUILDDIR} ${GENERATED_SYNOPSIS} api-viewer/apidata.js
|
||||||
|
|
||||||
|
|
||||||
install_manual_pages: ${MANUAL_PAGES}
|
install_manual_pages: ${MAN1_PAGES} ${MAN5_PAGES}
|
||||||
install -dm755 $(DESTDIR)$(MAN1DIR)
|
install -dm755 $(DESTDIR)$(MAN1DIR)
|
||||||
for i in ${MANUAL_PAGES}; do install -m755 $$i $(DESTDIR)$(MAN1DIR)/ ; done
|
for i in ${MAN1_PAGES}; do install -m755 $$i $(DESTDIR)$(MAN1DIR)/ ; done
|
||||||
|
install -dm755 $(DESTDIR)$(MAN5DIR)
|
||||||
|
for i in ${MAN5_PAGES}; do install -m755 $$i $(DESTDIR)$(MAN5DIR)/ ; done
|
||||||
|
|
||||||
install_html: html
|
install_html: html
|
||||||
install -dm755 $(DESTDIR)$(DOCDIR)
|
install -dm755 $(DESTDIR)$(DOCDIR)
|
||||||
|
@ -90,7 +90,18 @@ class ReflabelMapper(Builder):
|
|||||||
if hasattr(node, 'expect_referenced_by_id') and len(node['ids']) > 1: # explicit labels
|
if hasattr(node, 'expect_referenced_by_id') and len(node['ids']) > 1: # explicit labels
|
||||||
filename = self.env.doc2path(docname)
|
filename = self.env.doc2path(docname)
|
||||||
filename_html = re.sub('.rst', '.html', filename)
|
filename_html = re.sub('.rst', '.html', filename)
|
||||||
labelid = node['ids'][1] # [0] is predefined by sphinx, we need [1] for explicit ones
|
|
||||||
|
# node['ids'][0] contains a normalized version of the
|
||||||
|
# headline. If the ref and headline are the same
|
||||||
|
# (normalized) sphinx will set the node['ids'][1] to a
|
||||||
|
# generic id in the format `idX` where X is numeric. If the
|
||||||
|
# ref and headline are not the same, the ref name will be
|
||||||
|
# stored in node['ids'][1]
|
||||||
|
if re.match('^id[0-9]*$', node['ids'][1]):
|
||||||
|
labelid = node['ids'][0]
|
||||||
|
else:
|
||||||
|
labelid = node['ids'][1]
|
||||||
|
|
||||||
title = cast(nodes.title, node[0])
|
title = cast(nodes.title, node[0])
|
||||||
logger.info('traversing section {}'.format(title.astext()))
|
logger.info('traversing section {}'.format(title.astext()))
|
||||||
ref_name = getattr(title, 'rawsource', title.astext())
|
ref_name = getattr(title, 'rawsource', title.astext())
|
||||||
|
511
docs/api-viewer/PBSAPI.js
Normal file
511
docs/api-viewer/PBSAPI.js
Normal file
@ -0,0 +1,511 @@
|
|||||||
|
// avoid errors when running without development tools
|
||||||
|
if (!Ext.isDefined(Ext.global.console)) {
|
||||||
|
var console = {
|
||||||
|
dir: function() {},
|
||||||
|
log: function() {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ext.onReady(function() {
|
||||||
|
|
||||||
|
Ext.define('pve-param-schema', {
|
||||||
|
extend: 'Ext.data.Model',
|
||||||
|
fields: [
|
||||||
|
'name', 'type', 'typetext', 'description', 'verbose_description',
|
||||||
|
'enum', 'minimum', 'maximum', 'minLength', 'maxLength',
|
||||||
|
'pattern', 'title', 'requires', 'format', 'default',
|
||||||
|
'disallow', 'extends', 'links',
|
||||||
|
{
|
||||||
|
name: 'optional',
|
||||||
|
type: 'boolean'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
var store = Ext.define('pve-updated-treestore', {
|
||||||
|
extend: 'Ext.data.TreeStore',
|
||||||
|
model: Ext.define('pve-api-doc', {
|
||||||
|
extend: 'Ext.data.Model',
|
||||||
|
fields: [
|
||||||
|
'path', 'info', 'text',
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
proxy: {
|
||||||
|
type: 'memory',
|
||||||
|
data: pbsapi
|
||||||
|
},
|
||||||
|
sorters: [{
|
||||||
|
property: 'leaf',
|
||||||
|
direction: 'ASC'
|
||||||
|
}, {
|
||||||
|
property: 'text',
|
||||||
|
direction: 'ASC'
|
||||||
|
}],
|
||||||
|
filterer: 'bottomup',
|
||||||
|
doFilter: function(node) {
|
||||||
|
this.filterNodes(node, this.getFilters().getFilterFn(), true);
|
||||||
|
},
|
||||||
|
|
||||||
|
filterNodes: function(node, filterFn, parentVisible) {
|
||||||
|
var me = this,
|
||||||
|
bottomUpFiltering = me.filterer === 'bottomup',
|
||||||
|
match = filterFn(node) && parentVisible || (node.isRoot() && !me.getRootVisible()),
|
||||||
|
childNodes = node.childNodes,
|
||||||
|
len = childNodes && childNodes.length, i, matchingChildren;
|
||||||
|
|
||||||
|
if (len) {
|
||||||
|
for (i = 0; i < len; ++i) {
|
||||||
|
matchingChildren = me.filterNodes(childNodes[i], filterFn, match || bottomUpFiltering) || matchingChildren;
|
||||||
|
}
|
||||||
|
if (bottomUpFiltering) {
|
||||||
|
match = matchingChildren || match;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node.set("visible", match, me._silentOptions);
|
||||||
|
return match;
|
||||||
|
},
|
||||||
|
|
||||||
|
}).create();
|
||||||
|
|
||||||
|
var render_description = function(value, metaData, record) {
|
||||||
|
var pdef = record.data;
|
||||||
|
|
||||||
|
value = pdef.verbose_description || value;
|
||||||
|
|
||||||
|
// TODO: try to render asciidoc correctly
|
||||||
|
|
||||||
|
metaData.style = 'white-space:pre-wrap;'
|
||||||
|
|
||||||
|
return Ext.htmlEncode(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
var render_type = function(value, metaData, record) {
|
||||||
|
var pdef = record.data;
|
||||||
|
|
||||||
|
return pdef['enum'] ? 'enum' : (pdef.type || 'string');
|
||||||
|
};
|
||||||
|
|
||||||
|
var render_format = function(value, metaData, record) {
|
||||||
|
var pdef = record.data;
|
||||||
|
|
||||||
|
metaData.style = 'white-space:normal;'
|
||||||
|
|
||||||
|
if (pdef.typetext)
|
||||||
|
return Ext.htmlEncode(pdef.typetext);
|
||||||
|
|
||||||
|
if (pdef['enum'])
|
||||||
|
return pdef['enum'].join(' | ');
|
||||||
|
|
||||||
|
if (pdef.format)
|
||||||
|
return pdef.format;
|
||||||
|
|
||||||
|
if (pdef.pattern)
|
||||||
|
return Ext.htmlEncode(pdef.pattern);
|
||||||
|
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
var real_path = function(path) {
|
||||||
|
return path.replace(/^.*\/_upgrade_(\/)?/, "/");
|
||||||
|
};
|
||||||
|
|
||||||
|
var permission_text = function(permission) {
|
||||||
|
let permhtml = "";
|
||||||
|
|
||||||
|
if (permission.user) {
|
||||||
|
if (!permission.description) {
|
||||||
|
if (permission.user === 'world') {
|
||||||
|
permhtml += "Accessible without any authentication.";
|
||||||
|
} else if (permission.user === 'all') {
|
||||||
|
permhtml += "Accessible by all authenticated users.";
|
||||||
|
} else {
|
||||||
|
permhtml += 'Onyl accessible by user "' +
|
||||||
|
permission.user + '"';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (permission.check) {
|
||||||
|
permhtml += "<pre>Check: " +
|
||||||
|
Ext.htmlEncode(Ext.JSON.encode(permission.check)) + "</pre>";
|
||||||
|
} else if (permission.userParam) {
|
||||||
|
permhtml += `<div>Check if user matches parameter '${permission.userParam}'`;
|
||||||
|
} else if (permission.or) {
|
||||||
|
permhtml += "<div>Or<div style='padding-left: 10px;'>";
|
||||||
|
Ext.Array.each(permission.or, function(sub_permission) {
|
||||||
|
permhtml += permission_text(sub_permission);
|
||||||
|
})
|
||||||
|
permhtml += "</div></div>";
|
||||||
|
} else if (permission.and) {
|
||||||
|
permhtml += "<div>And<div style='padding-left: 10px;'>";
|
||||||
|
Ext.Array.each(permission.and, function(sub_permission) {
|
||||||
|
permhtml += permission_text(sub_permission);
|
||||||
|
})
|
||||||
|
permhtml += "</div></div>";
|
||||||
|
} else {
|
||||||
|
//console.log(permission);
|
||||||
|
permhtml += "Unknown systax!";
|
||||||
|
}
|
||||||
|
|
||||||
|
return permhtml;
|
||||||
|
};
|
||||||
|
|
||||||
|
var render_docu = function(data) {
|
||||||
|
var md = data.info;
|
||||||
|
|
||||||
|
// console.dir(data);
|
||||||
|
|
||||||
|
var items = [];
|
||||||
|
|
||||||
|
var clicmdhash = {
|
||||||
|
GET: 'get',
|
||||||
|
POST: 'create',
|
||||||
|
PUT: 'set',
|
||||||
|
DELETE: 'delete'
|
||||||
|
};
|
||||||
|
|
||||||
|
Ext.Array.each(['GET', 'POST', 'PUT', 'DELETE'], function(method) {
|
||||||
|
var info = md[method];
|
||||||
|
if (info) {
|
||||||
|
|
||||||
|
var usage = "";
|
||||||
|
|
||||||
|
usage += "<table><tr><td>HTTP: </td><td>"
|
||||||
|
+ method + " " + real_path("/api2/json" + data.path) + "</td></tr>";
|
||||||
|
|
||||||
|
var sections = [
|
||||||
|
{
|
||||||
|
title: 'Description',
|
||||||
|
html: Ext.htmlEncode(info.description),
|
||||||
|
bodyPadding: 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Usage',
|
||||||
|
html: usage,
|
||||||
|
bodyPadding: 10
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
if (info.parameters && info.parameters.properties) {
|
||||||
|
|
||||||
|
var pstore = Ext.create('Ext.data.Store', {
|
||||||
|
model: 'pve-param-schema',
|
||||||
|
proxy: {
|
||||||
|
type: 'memory'
|
||||||
|
},
|
||||||
|
groupField: 'optional',
|
||||||
|
sorters: [
|
||||||
|
{
|
||||||
|
property: 'name',
|
||||||
|
direction: 'ASC'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
Ext.Object.each(info.parameters.properties, function(name, pdef) {
|
||||||
|
pdef.name = name;
|
||||||
|
pstore.add(pdef);
|
||||||
|
});
|
||||||
|
|
||||||
|
pstore.sort();
|
||||||
|
|
||||||
|
var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{
|
||||||
|
enableGroupingMenu: false,
|
||||||
|
groupHeaderTpl: '<tpl if="groupValue">Optional</tpl><tpl if="!groupValue">Required</tpl>'
|
||||||
|
});
|
||||||
|
|
||||||
|
sections.push({
|
||||||
|
xtype: 'gridpanel',
|
||||||
|
title: 'Parameters',
|
||||||
|
features: [groupingFeature],
|
||||||
|
store: pstore,
|
||||||
|
viewConfig: {
|
||||||
|
trackOver: false,
|
||||||
|
stripeRows: true
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
header: 'Name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Type',
|
||||||
|
dataIndex: 'type',
|
||||||
|
renderer: render_type,
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Default',
|
||||||
|
dataIndex: 'default',
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Format',
|
||||||
|
dataIndex: 'type',
|
||||||
|
renderer: render_format,
|
||||||
|
flex: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Description',
|
||||||
|
dataIndex: 'description',
|
||||||
|
renderer: render_description,
|
||||||
|
flex: 6
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info.returns) {
|
||||||
|
|
||||||
|
var retinf = info.returns;
|
||||||
|
var rtype = retinf.type;
|
||||||
|
if (!rtype && retinf.items)
|
||||||
|
rtype = 'array';
|
||||||
|
if (!rtype)
|
||||||
|
rtype = 'object';
|
||||||
|
|
||||||
|
var rpstore = Ext.create('Ext.data.Store', {
|
||||||
|
model: 'pve-param-schema',
|
||||||
|
proxy: {
|
||||||
|
type: 'memory'
|
||||||
|
},
|
||||||
|
groupField: 'optional',
|
||||||
|
sorters: [
|
||||||
|
{
|
||||||
|
property: 'name',
|
||||||
|
direction: 'ASC'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
var properties;
|
||||||
|
if (rtype === 'array' && retinf.items.properties) {
|
||||||
|
properties = retinf.items.properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rtype === 'object' && retinf.properties) {
|
||||||
|
properties = retinf.properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ext.Object.each(properties, function(name, pdef) {
|
||||||
|
pdef.name = name;
|
||||||
|
rpstore.add(pdef);
|
||||||
|
});
|
||||||
|
|
||||||
|
rpstore.sort();
|
||||||
|
|
||||||
|
var groupingFeature = Ext.create('Ext.grid.feature.Grouping',{
|
||||||
|
enableGroupingMenu: false,
|
||||||
|
groupHeaderTpl: '<tpl if="groupValue">Optional</tpl><tpl if="!groupValue">Obligatory</tpl>'
|
||||||
|
});
|
||||||
|
var returnhtml;
|
||||||
|
if (retinf.items) {
|
||||||
|
returnhtml = '<pre>items: ' + Ext.htmlEncode(JSON.stringify(retinf.items, null, 4)) + '</pre>';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retinf.properties) {
|
||||||
|
returnhtml = returnhtml || '';
|
||||||
|
returnhtml += '<pre>properties:' + Ext.htmlEncode(JSON.stringify(retinf.properties, null, 4)) + '</pre>';
|
||||||
|
}
|
||||||
|
|
||||||
|
var rawSection = Ext.create('Ext.panel.Panel', {
|
||||||
|
bodyPadding: '0px 10px 10px 10px',
|
||||||
|
html: returnhtml,
|
||||||
|
hidden: true
|
||||||
|
});
|
||||||
|
|
||||||
|
sections.push({
|
||||||
|
xtype: 'gridpanel',
|
||||||
|
title: 'Returns: ' + rtype,
|
||||||
|
features: [groupingFeature],
|
||||||
|
store: rpstore,
|
||||||
|
viewConfig: {
|
||||||
|
trackOver: false,
|
||||||
|
stripeRows: true
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
header: 'Name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Type',
|
||||||
|
dataIndex: 'type',
|
||||||
|
renderer: render_type,
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Default',
|
||||||
|
dataIndex: 'default',
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Format',
|
||||||
|
dataIndex: 'type',
|
||||||
|
renderer: render_format,
|
||||||
|
flex: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: 'Description',
|
||||||
|
dataIndex: 'description',
|
||||||
|
renderer: render_description,
|
||||||
|
flex: 6
|
||||||
|
}
|
||||||
|
],
|
||||||
|
bbar: [
|
||||||
|
{
|
||||||
|
xtype: 'button',
|
||||||
|
text: 'Show RAW',
|
||||||
|
handler: function(btn) {
|
||||||
|
rawSection.setVisible(!rawSection.isVisible());
|
||||||
|
btn.setText(rawSection.isVisible() ? 'Hide RAW' : 'Show RAW');
|
||||||
|
}}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
sections.push(rawSection);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.path.match(/\/_upgrade_/)) {
|
||||||
|
var permhtml = '';
|
||||||
|
|
||||||
|
if (!info.permissions) {
|
||||||
|
permhtml = "Root only.";
|
||||||
|
} else {
|
||||||
|
if (info.permissions.description) {
|
||||||
|
permhtml += "<div style='white-space:pre-wrap;padding-bottom:10px;'>" +
|
||||||
|
Ext.htmlEncode(info.permissions.description) + "</div>";
|
||||||
|
}
|
||||||
|
permhtml += permission_text(info.permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we do not have this information for PBS api
|
||||||
|
//if (!info.allowtoken) {
|
||||||
|
// permhtml += "<br />This API endpoint is not available for API tokens."
|
||||||
|
//}
|
||||||
|
|
||||||
|
sections.push({
|
||||||
|
title: 'Required permissions',
|
||||||
|
bodyPadding: 10,
|
||||||
|
html: permhtml
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
title: method,
|
||||||
|
autoScroll: true,
|
||||||
|
defaults: {
|
||||||
|
border: false
|
||||||
|
},
|
||||||
|
items: sections
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var ct = Ext.getCmp('docview');
|
||||||
|
ct.setTitle("Path: " + real_path(data.path));
|
||||||
|
ct.removeAll(true);
|
||||||
|
ct.add(items);
|
||||||
|
ct.setActiveTab(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
Ext.define('Ext.form.SearchField', {
|
||||||
|
extend: 'Ext.form.field.Text',
|
||||||
|
alias: 'widget.searchfield',
|
||||||
|
|
||||||
|
emptyText: 'Search...',
|
||||||
|
|
||||||
|
flex: 1,
|
||||||
|
|
||||||
|
inputType: 'search',
|
||||||
|
listeners: {
|
||||||
|
'change': function(){
|
||||||
|
|
||||||
|
var value = this.getValue();
|
||||||
|
if (!Ext.isEmpty(value)) {
|
||||||
|
store.filter({
|
||||||
|
property: 'path',
|
||||||
|
value: value,
|
||||||
|
anyMatch: true
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
store.clearFilter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var tree = Ext.create('Ext.tree.Panel', {
|
||||||
|
title: 'Resource Tree',
|
||||||
|
tbar: [
|
||||||
|
{
|
||||||
|
xtype: 'searchfield',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
tools: [
|
||||||
|
{
|
||||||
|
type: 'expand',
|
||||||
|
tooltip: 'Expand all',
|
||||||
|
tooltipType: 'title',
|
||||||
|
callback: (tree) => tree.expandAll(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'collapse',
|
||||||
|
tooltip: 'Collapse all',
|
||||||
|
tooltipType: 'title',
|
||||||
|
callback: (tree) => tree.collapseAll(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
store: store,
|
||||||
|
width: 200,
|
||||||
|
region: 'west',
|
||||||
|
split: true,
|
||||||
|
margins: '5 0 5 5',
|
||||||
|
rootVisible: false,
|
||||||
|
listeners: {
|
||||||
|
selectionchange: function(v, selections) {
|
||||||
|
if (!selections[0])
|
||||||
|
return;
|
||||||
|
var rec = selections[0];
|
||||||
|
render_docu(rec.data);
|
||||||
|
location.hash = '#' + rec.data.path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ext.create('Ext.container.Viewport', {
|
||||||
|
layout: 'border',
|
||||||
|
renderTo: Ext.getBody(),
|
||||||
|
items: [
|
||||||
|
tree,
|
||||||
|
{
|
||||||
|
xtype: 'tabpanel',
|
||||||
|
title: 'Documentation',
|
||||||
|
id: 'docview',
|
||||||
|
region: 'center',
|
||||||
|
margins: '5 5 5 0',
|
||||||
|
layout: 'fit',
|
||||||
|
items: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
var deepLink = function() {
|
||||||
|
var path = window.location.hash.substring(1).replace(/\/\s*$/, '')
|
||||||
|
var endpoint = store.findNode('path', path);
|
||||||
|
|
||||||
|
if (endpoint) {
|
||||||
|
tree.getSelectionModel().select(endpoint);
|
||||||
|
tree.expandPath(endpoint.getPath());
|
||||||
|
render_docu(endpoint.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.onhashchange = deepLink;
|
||||||
|
|
||||||
|
deepLink();
|
||||||
|
|
||||||
|
});
|
13
docs/api-viewer/index.html
Normal file
13
docs/api-viewer/index.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||||
|
<title>Proxmox Backup Server API Documentation</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" type="text/css" href="extjs/theme-crisp/resources/theme-crisp-all.css">
|
||||||
|
<script type="text/javascript" src="extjs/ext-all.js"></script>
|
||||||
|
<script type="text/javascript" src="apidoc.js"></script>
|
||||||
|
</head>
|
||||||
|
<body></body>
|
||||||
|
</html>
|
@ -60,33 +60,10 @@ Environment Variables
|
|||||||
Output Format
|
Output Format
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
Most commands support the ``--output-format`` parameter. It accepts
|
.. include:: output-format.rst
|
||||||
the following values:
|
|
||||||
|
|
||||||
:``text``: Text format (default). Structured data is rendered as a table.
|
|
||||||
|
|
||||||
:``json``: JSON (single line).
|
|
||||||
|
|
||||||
:``json-pretty``: JSON (multiple lines, nicely formatted).
|
|
||||||
|
|
||||||
|
|
||||||
Please use the following environment variables to modify output behavior:
|
.. _client_creating_backups:
|
||||||
|
|
||||||
``PROXMOX_OUTPUT_FORMAT``
|
|
||||||
Defines the default output format.
|
|
||||||
|
|
||||||
``PROXMOX_OUTPUT_NO_BORDER``
|
|
||||||
If set (to any value), do not render table borders.
|
|
||||||
|
|
||||||
``PROXMOX_OUTPUT_NO_HEADER``
|
|
||||||
If set (to any value), do not render table headers.
|
|
||||||
|
|
||||||
.. note:: The ``text`` format is designed to be human readable, and
|
|
||||||
not meant to be parsed by automation tools. Please use the ``json``
|
|
||||||
format if you need to process the output.
|
|
||||||
|
|
||||||
|
|
||||||
.. _creating-backups:
|
|
||||||
|
|
||||||
Creating Backups
|
Creating Backups
|
||||||
----------------
|
----------------
|
||||||
@ -246,7 +223,7 @@ Restoring this backup will result in:
|
|||||||
. .. file2
|
. .. file2
|
||||||
|
|
||||||
|
|
||||||
.. _encryption:
|
.. _client_encryption:
|
||||||
|
|
||||||
Encryption
|
Encryption
|
||||||
----------
|
----------
|
||||||
@ -353,8 +330,10 @@ To set up a master key:
|
|||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
# openssl rsautl -decrypt -inkey master-private.pem -in rsa-encrypted.key -out /path/to/target
|
# proxmox-backup-client key import-with-master-key /path/to/target --master-keyfile /path/to/master-private.pem --encrypted-keyfile /path/to/rsa-encrypted.key
|
||||||
Enter pass phrase for ./master-private.pem: *********
|
Master Key Password: ******
|
||||||
|
New Password: ******
|
||||||
|
Verify Password: ******
|
||||||
|
|
||||||
7. The target file will now contain the encryption key information in plain
|
7. The target file will now contain the encryption key information in plain
|
||||||
text. The success of this can be confirmed by passing the resulting ``json``
|
text. The success of this can be confirmed by passing the resulting ``json``
|
||||||
@ -392,11 +371,11 @@ periodic recovery tests to ensure that you can access the data in
|
|||||||
case of problems.
|
case of problems.
|
||||||
|
|
||||||
First, you need to find the snapshot which you want to restore. The snapshot
|
First, you need to find the snapshot which you want to restore. The snapshot
|
||||||
command provides a list of all the snapshots on the server:
|
list command provides a list of all the snapshots on the server:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
# proxmox-backup-client snapshots
|
# proxmox-backup-client snapshot list
|
||||||
┌────────────────────────────────┬─────────────┬────────────────────────────────────┐
|
┌────────────────────────────────┬─────────────┬────────────────────────────────────┐
|
||||||
│ snapshot │ size │ files │
|
│ snapshot │ size │ files │
|
||||||
╞════════════════════════════════╪═════════════╪════════════════════════════════════╡
|
╞════════════════════════════════╪═════════════╪════════════════════════════════════╡
|
||||||
@ -481,16 +460,15 @@ subdirectory and add the corresponding pattern to the list for subsequent restor
|
|||||||
all files in the archive matching the patterns to ``/target/path`` on the local
|
all files in the archive matching the patterns to ``/target/path`` on the local
|
||||||
host. This will scan the whole archive.
|
host. This will scan the whole archive.
|
||||||
|
|
||||||
With ``restore /target/path`` you can restore the sub-archive given by the current
|
The ``restore`` command can be used to restore all the files contained within
|
||||||
working directory to the local target path ``/target/path`` on your host.
|
the backup archive. This is most helpful when paired with the ``--pattern
|
||||||
By additionally passing a glob pattern with ``--pattern <glob>``, the restore is
|
<glob>`` option, as it allows you to restore all files matching a specific
|
||||||
further limited to files matching the pattern.
|
pattern. For example, if you wanted to restore configuration files
|
||||||
For example:
|
located in ``/etc``, you could do the following:
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
pxar:/ > cd /etc/
|
pxar:/ > restore target/ --pattern etc/**/*.conf
|
||||||
pxar:/etc/ > restore /target/ --pattern **/*.conf
|
|
||||||
...
|
...
|
||||||
|
|
||||||
The above will scan trough all the directories below ``/etc`` and restore all
|
The above will scan trough all the directories below ``/etc`` and restore all
|
||||||
@ -581,7 +559,7 @@ command:
|
|||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
# proxmox-backup-client forget <snapshot>
|
# proxmox-backup-client snapshot forget <snapshot>
|
||||||
|
|
||||||
|
|
||||||
.. caution:: This command removes all archives in this backup
|
.. caution:: This command removes all archives in this backup
|
||||||
@ -655,10 +633,10 @@ shows the list of existing snapshots and what actions prune would take.
|
|||||||
|
|
||||||
.. note:: Neither the ``prune`` command nor the ``forget`` command free space
|
.. note:: Neither the ``prune`` command nor the ``forget`` command free space
|
||||||
in the chunk-store. The chunk-store still contains the data blocks. To free
|
in the chunk-store. The chunk-store still contains the data blocks. To free
|
||||||
space you need to perform :ref:`garbage-collection`.
|
space you need to perform :ref:`client_garbage-collection`.
|
||||||
|
|
||||||
|
|
||||||
.. _garbage-collection:
|
.. _client_garbage-collection:
|
||||||
|
|
||||||
Garbage Collection
|
Garbage Collection
|
||||||
------------------
|
------------------
|
||||||
@ -719,32 +697,34 @@ benchmark using the ``benchmark`` subcommand of ``proxmox-backup-client``:
|
|||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
# proxmox-backup-client benchmark
|
# proxmox-backup-client benchmark
|
||||||
Uploaded 656 chunks in 5 seconds.
|
Uploaded 1517 chunks in 5 seconds.
|
||||||
Time per request: 7659 microseconds.
|
Time per request: 3309 microseconds.
|
||||||
TLS speed: 547.60 MB/s
|
TLS speed: 1267.41 MB/s
|
||||||
SHA256 speed: 585.76 MB/s
|
SHA256 speed: 2066.73 MB/s
|
||||||
Compression speed: 1923.96 MB/s
|
Compression speed: 775.11 MB/s
|
||||||
Decompress speed: 7885.24 MB/s
|
Decompress speed: 1233.35 MB/s
|
||||||
AES256/GCM speed: 3974.03 MB/s
|
AES256/GCM speed: 3688.27 MB/s
|
||||||
|
Verify speed: 783.43 MB/s
|
||||||
┌───────────────────────────────────┬─────────────────────┐
|
┌───────────────────────────────────┬─────────────────────┐
|
||||||
│ Name │ Value │
|
│ Name │ Value │
|
||||||
╞═══════════════════════════════════╪═════════════════════╡
|
╞═══════════════════════════════════╪═════════════════════╡
|
||||||
│ TLS (maximal backup upload speed) │ 547.60 MB/s (93%) │
|
│ TLS (maximal backup upload speed) │ 1267.41 MB/s (103%) │
|
||||||
├───────────────────────────────────┼─────────────────────┤
|
├───────────────────────────────────┼─────────────────────┤
|
||||||
│ SHA256 checksum computation speed │ 585.76 MB/s (28%) │
|
│ SHA256 checksum computation speed │ 2066.73 MB/s (102%) │
|
||||||
├───────────────────────────────────┼─────────────────────┤
|
├───────────────────────────────────┼─────────────────────┤
|
||||||
│ ZStd level 1 compression speed │ 1923.96 MB/s (89%) │
|
│ ZStd level 1 compression speed │ 775.11 MB/s (103%) │
|
||||||
├───────────────────────────────────┼─────────────────────┤
|
├───────────────────────────────────┼─────────────────────┤
|
||||||
│ ZStd level 1 decompression speed │ 7885.24 MB/s (98%) │
|
│ ZStd level 1 decompression speed │ 1233.35 MB/s (103%) │
|
||||||
├───────────────────────────────────┼─────────────────────┤
|
├───────────────────────────────────┼─────────────────────┤
|
||||||
│ AES256 GCM encryption speed │ 3974.03 MB/s (104%) │
|
│ Chunk verification speed │ 783.43 MB/s (103%) │
|
||||||
|
├───────────────────────────────────┼─────────────────────┤
|
||||||
|
│ AES256 GCM encryption speed │ 3688.27 MB/s (101%) │
|
||||||
└───────────────────────────────────┴─────────────────────┘
|
└───────────────────────────────────┴─────────────────────┘
|
||||||
|
|
||||||
|
|
||||||
.. note:: The percentages given in the output table correspond to a
|
.. note:: The percentages given in the output table correspond to a
|
||||||
comparison against a Ryzen 7 2700X. The TLS test connects to the
|
comparison against a Ryzen 7 2700X. The TLS test connects to the
|
||||||
local host, so there is no network involved.
|
local host, so there is no network involved.
|
||||||
|
|
||||||
You can also pass the ``--output-format`` parameter to output stats in ``json``,
|
You can also pass the ``--output-format`` parameter to output stats in ``json``,
|
||||||
rather than the default table format.
|
rather than the default table format.
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,19 +1,140 @@
|
|||||||
Backup Protocol
|
Backup Protocol
|
||||||
===============
|
===============
|
||||||
|
|
||||||
.. todo:: add introduction to HTTP2 based backup protocols
|
Proxmox Backup Server uses a REST based API. While the management
|
||||||
|
interface use normal HTTP, the actual backup and restore interface use
|
||||||
|
HTTP/2 for improved performance. Both HTTP and HTTP/2 are well known
|
||||||
|
standards, so the following section assumes that you are familiar on
|
||||||
|
how to use them.
|
||||||
|
|
||||||
|
|
||||||
Backup Protocol API
|
Backup Protocol API
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
.. todo:: describe backup writer protocol
|
To start a new backup, the API call ``GET /api2/json/backup`` needs to
|
||||||
|
be upgraded to a HTTP/2 connection using
|
||||||
|
``proxmox-backup-protocol-v1`` as protocol name::
|
||||||
|
|
||||||
.. include:: backup-protocol-api.rst
|
GET /api2/json/backup HTTP/1.1
|
||||||
|
UPGRADE: proxmox-backup-protocol-v1
|
||||||
|
|
||||||
|
The server replies with HTTP 101 Switching Protocol status code,
|
||||||
|
and you can then issue REST commands on that updated HTTP/2 connection.
|
||||||
|
|
||||||
|
The backup protocol allows you to upload three different kind of files:
|
||||||
|
|
||||||
|
- Chunks and blobs (binary data)
|
||||||
|
|
||||||
|
- Fixed Indexes (List of chunks with fixed size)
|
||||||
|
|
||||||
|
- Dynamic Indexes (List of chunk with variable size)
|
||||||
|
|
||||||
|
The following section gives a short introduction how to upload such
|
||||||
|
files. Please use the `API Viewer <api-viewer/index.html>`_ for
|
||||||
|
details about available REST commands.
|
||||||
|
|
||||||
|
|
||||||
Reader Protocol API
|
Upload Blobs
|
||||||
-------------------
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
.. todo:: describe backup reader protocol
|
Uploading blobs is done using ``POST /blob``. The HTTP body contains the
|
||||||
|
data encoded as :ref:`Data Blob <data-blob-format>`).
|
||||||
|
|
||||||
.. include:: reader-protocol-api.rst
|
The file name needs to end with ``.blob``, and is automatically added
|
||||||
|
to the backup manifest.
|
||||||
|
|
||||||
|
|
||||||
|
Upload Chunks
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Chunks belong to an index, so you first need to open an index (see
|
||||||
|
below). After that, you can upload chunks using ``POST /fixed_chunk``
|
||||||
|
and ``POST /dynamic_chunk``. The HTTP body contains the chunk data
|
||||||
|
encoded as :ref:`Data Blob <data-blob-format>`).
|
||||||
|
|
||||||
|
|
||||||
|
Upload Fixed Indexes
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Fixed indexes are use to store VM image data. The VM image is split
|
||||||
|
into equally sized chunks, which are uploaded individually. The index
|
||||||
|
file simply contains a list to chunk digests.
|
||||||
|
|
||||||
|
You create a fixed index with ``POST /fixed_index``. Then upload
|
||||||
|
chunks with ``POST /fixed_chunk``, and append them to the index with
|
||||||
|
``PUT /fixed_index``. When finished, you need to close the index using
|
||||||
|
``POST /fixed_close``.
|
||||||
|
|
||||||
|
The file name needs to end with ``.fidx``, and is automatically added
|
||||||
|
to the backup manifest.
|
||||||
|
|
||||||
|
|
||||||
|
Upload Dynamic Indexes
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Dynamic indexes are use to store file archive data. The archive data
|
||||||
|
is split into dynamically sized chunks, which are uploaded
|
||||||
|
individually. The index file simply contains a list to chunk digests
|
||||||
|
and offsets.
|
||||||
|
|
||||||
|
You create a dynamic sized index with ``POST /dynamic_index``. Then
|
||||||
|
upload chunks with ``POST /dynamic_chunk``, and append them to the index with
|
||||||
|
``PUT /dynamic_index``. When finished, you need to close the index using
|
||||||
|
``POST /dynamic_close``.
|
||||||
|
|
||||||
|
The file name needs to end with ``.didx``, and is automatically added
|
||||||
|
to the backup manifest.
|
||||||
|
|
||||||
|
Finish Backup
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Once you have uploaded all data, you need to call ``POST
|
||||||
|
/finish``. This commits all data and ends the backup protocol.
|
||||||
|
|
||||||
|
|
||||||
|
Restore/Reader Protocol API
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
To start a new reader, the API call ``GET /api2/json/reader`` needs to
|
||||||
|
be upgraded to a HTTP/2 connection using
|
||||||
|
``proxmox-backup-reader-protocol-v1`` as protocol name::
|
||||||
|
|
||||||
|
GET /api2/json/reader HTTP/1.1
|
||||||
|
UPGRADE: proxmox-backup-reader-protocol-v1
|
||||||
|
|
||||||
|
The server replies with HTTP 101 Switching Protocol status code,
|
||||||
|
and you can then issue REST commands on that updated HTTP/2 connection.
|
||||||
|
|
||||||
|
The reader protocol allows you to download three different kind of files:
|
||||||
|
|
||||||
|
- Chunks and blobs (binary data)
|
||||||
|
|
||||||
|
- Fixed Indexes (List of chunks with fixed size)
|
||||||
|
|
||||||
|
- Dynamic Indexes (List of chunk with variable size)
|
||||||
|
|
||||||
|
The following section gives a short introduction how to download such
|
||||||
|
files. Please use the `API Viewer <api-viewer/index.html>`_ for details about
|
||||||
|
available REST commands.
|
||||||
|
|
||||||
|
|
||||||
|
Download Blobs
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Downloading blobs is done using ``GET /download``. The HTTP body contains the
|
||||||
|
data encoded as :ref:`Data Blob <data-blob-format>`.
|
||||||
|
|
||||||
|
|
||||||
|
Download Chunks
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Downloading chunks is done using ``GET /chunk``. The HTTP body contains the
|
||||||
|
data encoded as :ref:`Data Blob <data-blob-format>`).
|
||||||
|
|
||||||
|
|
||||||
|
Download Index Files
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Downloading index files is done using ``GET /download``. The HTTP body
|
||||||
|
contains the data encoded as :ref:`Fixed Index <fixed-index-format>`
|
||||||
|
or :ref:`Dynamic Index <dynamic-index-format>`.
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
|
.. _calendar-event-scheduling:
|
||||||
.. _calendar-events:
|
|
||||||
|
|
||||||
Calendar Events
|
Calendar Events
|
||||||
===============
|
===============
|
||||||
|
19
docs/conf.py
19
docs/conf.py
@ -74,7 +74,7 @@ rst_epilog = epilog_file.read()
|
|||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = 'Proxmox Backup'
|
project = 'Proxmox Backup'
|
||||||
copyright = '2019-2020, Proxmox Server Solutions GmbH'
|
copyright = '2019-2021, Proxmox Server Solutions GmbH'
|
||||||
author = 'Proxmox Support Team'
|
author = 'Proxmox Support Team'
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
@ -107,10 +107,8 @@ today_fmt = '%A, %d %B %Y'
|
|||||||
# This patterns also effect to html_static_path and html_extra_path
|
# This patterns also effect to html_static_path and html_extra_path
|
||||||
exclude_patterns = [
|
exclude_patterns = [
|
||||||
'_build', 'Thumbs.db', '.DS_Store',
|
'_build', 'Thumbs.db', '.DS_Store',
|
||||||
'proxmox-backup-client/man1.rst',
|
'*/man1.rst',
|
||||||
'proxmox-backup-manager/man1.rst',
|
'config/*/man5.rst',
|
||||||
'proxmox-backup-proxy/man1.rst',
|
|
||||||
'pxar/man1.rst',
|
|
||||||
'epilog.rst',
|
'epilog.rst',
|
||||||
'pbs-copyright.rst',
|
'pbs-copyright.rst',
|
||||||
'local-zfs.rst'
|
'local-zfs.rst'
|
||||||
@ -171,7 +169,9 @@ html_theme_options = {
|
|||||||
'extra_nav_links': {
|
'extra_nav_links': {
|
||||||
'Proxmox Homepage': 'https://proxmox.com',
|
'Proxmox Homepage': 'https://proxmox.com',
|
||||||
'PDF': 'proxmox-backup.pdf',
|
'PDF': 'proxmox-backup.pdf',
|
||||||
|
'API Viewer' : 'api-viewer/index.html',
|
||||||
'Prune Simulator' : 'prune-simulator/index.html',
|
'Prune Simulator' : 'prune-simulator/index.html',
|
||||||
|
'LTO Barcode Generator' : 'lto-barcode/index.html',
|
||||||
},
|
},
|
||||||
|
|
||||||
'sidebar_width': '320px',
|
'sidebar_width': '320px',
|
||||||
@ -245,10 +245,8 @@ html_js_files = [
|
|||||||
#
|
#
|
||||||
# html_last_updated_fmt = None
|
# html_last_updated_fmt = None
|
||||||
|
|
||||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
# We need to disable smatquotes, else Option Lists do not display long options
|
||||||
# typographically correct entities.
|
smartquotes = False
|
||||||
#
|
|
||||||
# html_use_smartypants = True
|
|
||||||
|
|
||||||
# Additional templates that should be rendered to pages, maps page names to
|
# Additional templates that should be rendered to pages, maps page names to
|
||||||
# template names.
|
# template names.
|
||||||
@ -466,3 +464,6 @@ epub_exclude_files = ['search.html']
|
|||||||
# If false, no index is generated.
|
# If false, no index is generated.
|
||||||
#
|
#
|
||||||
# epub_use_index = True
|
# epub_use_index = True
|
||||||
|
|
||||||
|
# use local mathjax package, symlink comes from debian/proxmox-backup-docs.links
|
||||||
|
mathjax_path = "mathjax/MathJax.js?config=TeX-AMS-MML_HTMLorMML"
|
||||||
|
22
docs/config/acl/format.rst
Normal file
22
docs/config/acl/format.rst
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
This file contains the access control list for the Proxmox Backup
|
||||||
|
Server API.
|
||||||
|
|
||||||
|
Each line starts with ``acl:``, followed by 4 additional values
|
||||||
|
separated by collon.
|
||||||
|
|
||||||
|
:propagate: Propagate permissions down the hierachrchy
|
||||||
|
|
||||||
|
:path: The object path
|
||||||
|
|
||||||
|
:User/Token: List of users and token
|
||||||
|
|
||||||
|
:Role: List of assigned roles
|
||||||
|
|
||||||
|
Here is an example list::
|
||||||
|
|
||||||
|
acl:1:/:root@pam!test:Admin
|
||||||
|
acl:1:/datastore/store1:user1@pbs:DatastoreAdmin
|
||||||
|
|
||||||
|
|
||||||
|
You can use the ``proxmox-backup-manager acl`` command to manipulate
|
||||||
|
this file.
|
35
docs/config/acl/man5.rst
Normal file
35
docs/config/acl/man5.rst
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
==========================
|
||||||
|
acl.cfg
|
||||||
|
==========================
|
||||||
|
|
||||||
|
.. include:: ../../epilog.rst
|
||||||
|
|
||||||
|
-------------------------------------------------------------
|
||||||
|
Access Control Configuration
|
||||||
|
-------------------------------------------------------------
|
||||||
|
|
||||||
|
:Author: |AUTHOR|
|
||||||
|
:Version: Version |VERSION|
|
||||||
|
:Manual section: 5
|
||||||
|
|
||||||
|
Description
|
||||||
|
===========
|
||||||
|
|
||||||
|
The file /etc/proxmox-backup/user.cfg is a configuration file for Proxmox
|
||||||
|
Backup Server. It contains the access control configuration for the API.
|
||||||
|
|
||||||
|
File Format
|
||||||
|
===========
|
||||||
|
|
||||||
|
.. include:: format.rst
|
||||||
|
|
||||||
|
|
||||||
|
Roles
|
||||||
|
=====
|
||||||
|
|
||||||
|
The following roles exist:
|
||||||
|
|
||||||
|
.. include:: roles.rst
|
||||||
|
|
||||||
|
|
||||||
|
.. include:: ../../pbs-copyright.rst
|
18
docs/config/datastore/format.rst
Normal file
18
docs/config/datastore/format.rst
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
The file contains a list of datastore configuration sections. Each
|
||||||
|
section starts with a header ``datastore: <name>``, followed by the
|
||||||
|
datastore configuration options.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
datastore: <name1>
|
||||||
|
path <path1>
|
||||||
|
<option1> <value1>
|
||||||
|
...
|
||||||
|
|
||||||
|
datastore: <name2>
|
||||||
|
path <path2>
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
You can use the ``proxmox-backup-manager datastore`` command to manipulate
|
||||||
|
this file.
|
33
docs/config/datastore/man5.rst
Normal file
33
docs/config/datastore/man5.rst
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
==========================
|
||||||
|
datastore.cfg
|
||||||
|
==========================
|
||||||
|
|
||||||
|
.. include:: ../../epilog.rst
|
||||||
|
|
||||||
|
-------------------------------------------------------------
|
||||||
|
Datastore Configuration
|
||||||
|
-------------------------------------------------------------
|
||||||
|
|
||||||
|
:Author: |AUTHOR|
|
||||||
|
:Version: Version |VERSION|
|
||||||
|
:Manual section: 5
|
||||||
|
|
||||||
|
Description
|
||||||
|
===========
|
||||||
|
|
||||||
|
The file /etc/proxmox-backup/datastore.cfg is a configuration file for Proxmox
|
||||||
|
Backup Server. It contains the Datastore configuration.
|
||||||
|
|
||||||
|
File Format
|
||||||
|
===========
|
||||||
|
|
||||||
|
.. include:: format.rst
|
||||||
|
|
||||||
|
|
||||||
|
Options
|
||||||
|
=======
|
||||||
|
|
||||||
|
.. include:: config.rst
|
||||||
|
|
||||||
|
|
||||||
|
.. include:: ../../pbs-copyright.rst
|
13
docs/config/media-pool/format.rst
Normal file
13
docs/config/media-pool/format.rst
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
Each entry starts with a header ``pool: <name>``, followed by the
|
||||||
|
media pool configuration options.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
pool: company1
|
||||||
|
allocation always
|
||||||
|
retention overwrite
|
||||||
|
|
||||||
|
pool: ...
|
||||||
|
|
||||||
|
|
||||||
|
You can use the ``proxmox-tape pool`` command to manipulate this file.
|
35
docs/config/media-pool/man5.rst
Normal file
35
docs/config/media-pool/man5.rst
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
==========================
|
||||||
|
media-pool.cfg
|
||||||
|
==========================
|
||||||
|
|
||||||
|
.. include:: ../../epilog.rst
|
||||||
|
|
||||||
|
-------------------------------------------------------------
|
||||||
|
Media Pool Configuration
|
||||||
|
-------------------------------------------------------------
|
||||||
|
|
||||||
|
:Author: |AUTHOR|
|
||||||
|
:Version: Version |VERSION|
|
||||||
|
:Manual section: 5
|
||||||
|
|
||||||
|
Description
|
||||||
|
===========
|
||||||
|
|
||||||
|
The file /etc/proxmox-backup/media-pool.cfg is a configuration file
|
||||||
|
for Proxmox Backup Server. It contains the medila pool configuration
|
||||||
|
for tape backups.
|
||||||
|
|
||||||
|
|
||||||
|
File Format
|
||||||
|
===========
|
||||||
|
|
||||||
|
.. include:: format.rst
|
||||||
|
|
||||||
|
|
||||||
|
Options
|
||||||
|
=======
|
||||||
|
|
||||||
|
.. include:: config.rst
|
||||||
|
|
||||||
|
|
||||||
|
.. include:: ../../pbs-copyright.rst
|
17
docs/config/remote/format.rst
Normal file
17
docs/config/remote/format.rst
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
This file contains information used to access remote servers.
|
||||||
|
|
||||||
|
Each entry starts with a header ``remote: <name>``, followed by the
|
||||||
|
remote configuration options.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
remote: server1
|
||||||
|
host server1.local
|
||||||
|
auth-id sync@pbs
|
||||||
|
...
|
||||||
|
|
||||||
|
remote: ...
|
||||||
|
|
||||||
|
|
||||||
|
You can use the ``proxmox-backup-manager remote`` command to manipulate
|
||||||
|
this file.
|
35
docs/config/remote/man5.rst
Normal file
35
docs/config/remote/man5.rst
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
==========================
|
||||||
|
remote.cfg
|
||||||
|
==========================
|
||||||
|
|
||||||
|
.. include:: ../../epilog.rst
|
||||||
|
|
||||||
|
-------------------------------------------------------------
|
||||||
|
Remote Server Configuration
|
||||||
|
-------------------------------------------------------------
|
||||||
|
|
||||||
|
:Author: |AUTHOR|
|
||||||
|
:Version: Version |VERSION|
|
||||||
|
:Manual section: 5
|
||||||
|
|
||||||
|
Description
|
||||||
|
===========
|
||||||
|
|
||||||
|
The file /etc/proxmox-backup/remote.cfg is a configuration file for
|
||||||
|
Proxmox Backup Server. It contains information about remote servers,
|
||||||
|
usable for synchronization jobs.
|
||||||
|
|
||||||
|
|
||||||
|
File Format
|
||||||
|
===========
|
||||||
|
|
||||||
|
.. include:: format.rst
|
||||||
|
|
||||||
|
|
||||||
|
Options
|
||||||
|
=======
|
||||||
|
|
||||||
|
.. include:: config.rst
|
||||||
|
|
||||||
|
|
||||||
|
.. include:: ../../pbs-copyright.rst
|
15
docs/config/sync/format.rst
Normal file
15
docs/config/sync/format.rst
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
Each entry starts with a header ``sync: <name>``, followed by the
|
||||||
|
job configuration options.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
sync: job1
|
||||||
|
store store1
|
||||||
|
remote-store store1
|
||||||
|
remote lina
|
||||||
|
|
||||||
|
sync: ...
|
||||||
|
|
||||||
|
|
||||||
|
You can use the ``proxmox-backup-manager sync-job`` command to manipulate
|
||||||
|
this file.
|
35
docs/config/sync/man5.rst
Normal file
35
docs/config/sync/man5.rst
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
==========================
|
||||||
|
sync.cfg
|
||||||
|
==========================
|
||||||
|
|
||||||
|
.. include:: ../../epilog.rst
|
||||||
|
|
||||||
|
-------------------------------------------------------------
|
||||||
|
Synchronization Job Configuration
|
||||||
|
-------------------------------------------------------------
|
||||||
|
|
||||||
|
:Author: |AUTHOR|
|
||||||
|
:Version: Version |VERSION|
|
||||||
|
:Manual section: 5
|
||||||
|
|
||||||
|
Description
|
||||||
|
===========
|
||||||
|
|
||||||
|
The file /etc/proxmox-backup/sync.cfg is a configuration file for
|
||||||
|
Proxmox Backup Server. It contains the synchronization job
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
|
||||||
|
File Format
|
||||||
|
===========
|
||||||
|
|
||||||
|
.. include:: format.rst
|
||||||
|
|
||||||
|
|
||||||
|
Options
|
||||||
|
=======
|
||||||
|
|
||||||
|
.. include:: config.rst
|
||||||
|
|
||||||
|
|
||||||
|
.. include:: ../../pbs-copyright.rst
|
16
docs/config/tape-job/format.rst
Normal file
16
docs/config/tape-job/format.rst
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
Each entry starts with a header ``backup: <name>``, followed by the
|
||||||
|
job configuration options.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
backup: job1
|
||||||
|
drive hh8
|
||||||
|
pool p4
|
||||||
|
store store3
|
||||||
|
schedule daily
|
||||||
|
|
||||||
|
backup: ...
|
||||||
|
|
||||||
|
|
||||||
|
You can use the ``proxmox-tape backup-job`` command to manipulate
|
||||||
|
this file.
|
34
docs/config/tape-job/man5.rst
Normal file
34
docs/config/tape-job/man5.rst
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
==========================
|
||||||
|
tape-job.cfg
|
||||||
|
==========================
|
||||||
|
|
||||||
|
.. include:: ../../epilog.rst
|
||||||
|
|
||||||
|
-------------------------------------------------------------
|
||||||
|
Tape Job Configuration
|
||||||
|
-------------------------------------------------------------
|
||||||
|
|
||||||
|
:Author: |AUTHOR|
|
||||||
|
:Version: Version |VERSION|
|
||||||
|
:Manual section: 5
|
||||||
|
|
||||||
|
Description
|
||||||
|
===========
|
||||||
|
|
||||||
|
The file ``/etc/proxmox-backup/tape-job.cfg`` is a configuration file for
|
||||||
|
Proxmox Backup Server. It contains the tape job configuration.
|
||||||
|
|
||||||
|
|
||||||
|
File Format
|
||||||
|
===========
|
||||||
|
|
||||||
|
.. include:: format.rst
|
||||||
|
|
||||||
|
|
||||||
|
Options
|
||||||
|
=======
|
||||||
|
|
||||||
|
.. include:: config.rst
|
||||||
|
|
||||||
|
|
||||||
|
.. include:: ../../pbs-copyright.rst
|
22
docs/config/tape/format.rst
Normal file
22
docs/config/tape/format.rst
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Each drive configuration section starts with a header ``linux: <name>``,
|
||||||
|
followed by the drive configuration options.
|
||||||
|
|
||||||
|
Tape changer configurations starts with ``changer: <name>``,
|
||||||
|
followed by the changer configuration options.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
linux: hh8
|
||||||
|
changer sl3
|
||||||
|
path /dev/tape/by-id/scsi-10WT065325-nst
|
||||||
|
|
||||||
|
changer: sl3
|
||||||
|
export-slots 14,15,16
|
||||||
|
path /dev/tape/by-id/scsi-CJ0JBE0059
|
||||||
|
|
||||||
|
|
||||||
|
You can use the ``proxmox-tape drive`` and ``proxmox-tape changer``
|
||||||
|
commands to manipulate this file.
|
||||||
|
|
||||||
|
.. NOTE:: The ``virtual:`` drive type is experimental and onyl used
|
||||||
|
for debugging.
|
33
docs/config/tape/man5.rst
Normal file
33
docs/config/tape/man5.rst
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
==========================
|
||||||
|
tape.cfg
|
||||||
|
==========================
|
||||||
|
|
||||||
|
.. include:: ../../epilog.rst
|
||||||
|
|
||||||
|
-------------------------------------------------------------
|
||||||
|
Tape Drive and Changer Configuration
|
||||||
|
-------------------------------------------------------------
|
||||||
|
|
||||||
|
:Author: |AUTHOR|
|
||||||
|
:Version: Version |VERSION|
|
||||||
|
:Manual section: 5
|
||||||
|
|
||||||
|
Description
|
||||||
|
===========
|
||||||
|
|
||||||
|
The file /etc/proxmox-backup/tape.cfg is a configuration file for Proxmox
|
||||||
|
Backup Server. It contains the tape drive and changer configuration.
|
||||||
|
|
||||||
|
File Format
|
||||||
|
===========
|
||||||
|
|
||||||
|
.. include:: format.rst
|
||||||
|
|
||||||
|
|
||||||
|
Options
|
||||||
|
=======
|
||||||
|
|
||||||
|
.. include:: config.rst
|
||||||
|
|
||||||
|
|
||||||
|
.. include:: ../../pbs-copyright.rst
|
28
docs/config/user/format.rst
Normal file
28
docs/config/user/format.rst
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
This file contains the list of API users and API tokens.
|
||||||
|
|
||||||
|
Each user configuration section starts with a header ``user: <name>``,
|
||||||
|
followed by the user configuration options.
|
||||||
|
|
||||||
|
API token configuration starts with a header ``token:
|
||||||
|
<userid!token_name>``, followed by the token configuration. The data
|
||||||
|
used to authenticate tokens is stored in a separate file
|
||||||
|
(``token.shadow``).
|
||||||
|
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
user: root@pam
|
||||||
|
comment Superuser
|
||||||
|
email test@example.local
|
||||||
|
...
|
||||||
|
|
||||||
|
token: root@pam!token1
|
||||||
|
comment API test token
|
||||||
|
enable true
|
||||||
|
expire 0
|
||||||
|
|
||||||
|
user: ...
|
||||||
|
|
||||||
|
|
||||||
|
You can use the ``proxmox-backup-manager user`` command to manipulate
|
||||||
|
this file.
|
33
docs/config/user/man5.rst
Normal file
33
docs/config/user/man5.rst
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
==========================
|
||||||
|
user.cfg
|
||||||
|
==========================
|
||||||
|
|
||||||
|
.. include:: ../../epilog.rst
|
||||||
|
|
||||||
|
-------------------------------------------------------------
|
||||||
|
User Configuration
|
||||||
|
-------------------------------------------------------------
|
||||||
|
|
||||||
|
:Author: |AUTHOR|
|
||||||
|
:Version: Version |VERSION|
|
||||||
|
:Manual section: 5
|
||||||
|
|
||||||
|
Description
|
||||||
|
===========
|
||||||
|
|
||||||
|
The file /etc/proxmox-backup/user.cfg is a configuration file for Proxmox
|
||||||
|
Backup Server. It contains the user configuration.
|
||||||
|
|
||||||
|
File Format
|
||||||
|
===========
|
||||||
|
|
||||||
|
.. include:: format.rst
|
||||||
|
|
||||||
|
|
||||||
|
Options
|
||||||
|
=======
|
||||||
|
|
||||||
|
.. include:: config.rst
|
||||||
|
|
||||||
|
|
||||||
|
.. include:: ../../pbs-copyright.rst
|
16
docs/config/verification/format.rst
Normal file
16
docs/config/verification/format.rst
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
Each entry starts with a header ``verification: <name>``, followed by the
|
||||||
|
job configuration options.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
verification: verify-store2
|
||||||
|
ignore-verified true
|
||||||
|
outdated-after 7
|
||||||
|
schedule daily
|
||||||
|
store store2
|
||||||
|
|
||||||
|
verification: ...
|
||||||
|
|
||||||
|
|
||||||
|
You can use the ``proxmox-backup-manager verify-job`` command to manipulate
|
||||||
|
this file.
|
35
docs/config/verification/man5.rst
Normal file
35
docs/config/verification/man5.rst
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
==========================
|
||||||
|
verification.cfg
|
||||||
|
==========================
|
||||||
|
|
||||||
|
.. include:: ../../epilog.rst
|
||||||
|
|
||||||
|
-------------------------------------------------------------
|
||||||
|
Verification Job Configuration
|
||||||
|
-------------------------------------------------------------
|
||||||
|
|
||||||
|
:Author: |AUTHOR|
|
||||||
|
:Version: Version |VERSION|
|
||||||
|
:Manual section: 5
|
||||||
|
|
||||||
|
Description
|
||||||
|
===========
|
||||||
|
|
||||||
|
The file /etc/proxmox-backup/sync.cfg is a configuration file for
|
||||||
|
Proxmox Backup Server. It contains the verification job
|
||||||
|
configuration.
|
||||||
|
|
||||||
|
|
||||||
|
File Format
|
||||||
|
===========
|
||||||
|
|
||||||
|
.. include:: format.rst
|
||||||
|
|
||||||
|
|
||||||
|
Options
|
||||||
|
=======
|
||||||
|
|
||||||
|
.. include:: config.rst
|
||||||
|
|
||||||
|
|
||||||
|
.. include:: ../../pbs-copyright.rst
|
97
docs/configuration-files.rst
Normal file
97
docs/configuration-files.rst
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
Configuration Files
|
||||||
|
===================
|
||||||
|
|
||||||
|
All Proxmox Backup Server configuration files resides inside directory
|
||||||
|
``/etc/proxmox-backup/``.
|
||||||
|
|
||||||
|
|
||||||
|
``acl.cfg``
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
File Format
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. include:: config/acl/format.rst
|
||||||
|
|
||||||
|
|
||||||
|
Roles
|
||||||
|
^^^^^
|
||||||
|
|
||||||
|
The following roles exist:
|
||||||
|
|
||||||
|
.. include:: config/acl/roles.rst
|
||||||
|
|
||||||
|
|
||||||
|
``datastore.cfg``
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
File Format
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. include:: config/datastore/format.rst
|
||||||
|
|
||||||
|
|
||||||
|
Options
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
.. include:: config/datastore/config.rst
|
||||||
|
|
||||||
|
|
||||||
|
``user.cfg``
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
File Format
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. include:: config/user/format.rst
|
||||||
|
|
||||||
|
|
||||||
|
Options
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
.. include:: config/user/config.rst
|
||||||
|
|
||||||
|
|
||||||
|
``remote.cfg``
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
File Format
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. include:: config/remote/format.rst
|
||||||
|
|
||||||
|
|
||||||
|
Options
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
.. include:: config/remote/config.rst
|
||||||
|
|
||||||
|
|
||||||
|
``sync.cfg``
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
File Format
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. include:: config/sync/format.rst
|
||||||
|
|
||||||
|
|
||||||
|
Options
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
.. include:: config/sync/config.rst
|
||||||
|
|
||||||
|
|
||||||
|
``verification.cfg``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
File Format
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. include:: config/verification/format.rst
|
||||||
|
|
||||||
|
|
||||||
|
Options
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
.. include:: config/verification/config.rst
|
@ -14,6 +14,10 @@ pre {
|
|||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.topic {
|
||||||
|
background-color: #FAFAFA;
|
||||||
|
}
|
||||||
|
|
||||||
li a.current {
|
li a.current {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
border-bottom: 1px solid #000;
|
border-bottom: 1px solid #000;
|
||||||
@ -25,6 +29,23 @@ ul li.toctree-l1 > a {
|
|||||||
color: #000;
|
color: #000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar ul {
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
div.sphinxsidebar ul ul {
|
||||||
|
list-style: circle;
|
||||||
|
}
|
||||||
|
div.sphinxsidebar ul ul ul {
|
||||||
|
list-style: square;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar ul a code {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
div.sphinxsidebar ul ul a {
|
||||||
|
border-bottom: 1px dotted #CCC;
|
||||||
|
}
|
||||||
|
|
||||||
div.sphinxsidebar form.search {
|
div.sphinxsidebar form.search {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
@ -53,9 +53,12 @@ checksums. This manifest file is used to verify the integrity of each backup.
|
|||||||
When backing up to remote servers, do I have to trust the remote server?
|
When backing up to remote servers, do I have to trust the remote server?
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
Proxmox Backup Server supports client-side encryption, meaning your data is
|
Proxmox Backup Server transfers data via `Transport Layer Security (TLS)
|
||||||
encrypted before it reaches the server. Thus, in the event that an attacker
|
<https://en.wikipedia.org/wiki/Transport_Layer_Security>`_ and additionally
|
||||||
gains access to the server, they will not be able to read the data.
|
supports client-side encryption. This means that data is transferred securely
|
||||||
|
and can be encrypted before it reaches the server. Thus, in the event that an
|
||||||
|
attacker gains access to the server or any point of the network, they will not
|
||||||
|
be able to read the data.
|
||||||
|
|
||||||
.. note:: Encryption is not enabled by default. To set up encryption, see the
|
.. note:: Encryption is not enabled by default. To set up encryption, see the
|
||||||
`Encryption
|
`Encryption
|
||||||
|
@ -6,7 +6,113 @@ File Formats
|
|||||||
Proxmox File Archive Format (``.pxar``)
|
Proxmox File Archive Format (``.pxar``)
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
|
|
||||||
.. graphviz:: pxar-format-overview.dot
|
.. graphviz:: pxar-format-overview.dot
|
||||||
|
|
||||||
|
|
||||||
|
.. _data-blob-format:
|
||||||
|
|
||||||
|
Data Blob Format (``.blob``)
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
The data blob format is used to store small binary data. The magic number decides the exact format:
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:widths: auto
|
||||||
|
|
||||||
|
* - ``[66, 171, 56, 7, 190, 131, 112, 161]``
|
||||||
|
- unencrypted
|
||||||
|
- uncompressed
|
||||||
|
* - ``[49, 185, 88, 66, 111, 182, 163, 127]``
|
||||||
|
- unencrypted
|
||||||
|
- compressed
|
||||||
|
* - ``[123, 103, 133, 190, 34, 45, 76, 240]``
|
||||||
|
- encrypted
|
||||||
|
- uncompressed
|
||||||
|
* - ``[230, 89, 27, 191, 11, 191, 216, 11]``
|
||||||
|
- encrypted
|
||||||
|
- compressed
|
||||||
|
|
||||||
|
Compression algorithm is ``zstd``. Encryption cipher is ``AES_256_GCM``.
|
||||||
|
|
||||||
|
Unencrypted blobs use the following format:
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:widths: auto
|
||||||
|
|
||||||
|
* - ``MAGIC: [u8; 8]``
|
||||||
|
* - ``CRC32: [u8; 4]``
|
||||||
|
* - ``Data: (max 16MiB)``
|
||||||
|
|
||||||
|
Encrypted blobs additionally contains a 16 byte IV, followed by a 16
|
||||||
|
byte Authenticated Encyryption (AE) tag, followed by the encrypted
|
||||||
|
data:
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
|
||||||
|
* - ``MAGIC: [u8; 8]``
|
||||||
|
* - ``CRC32: [u8; 4]``
|
||||||
|
* - ``ÌV: [u8; 16]``
|
||||||
|
* - ``TAG: [u8; 16]``
|
||||||
|
* - ``Data: (max 16MiB)``
|
||||||
|
|
||||||
|
|
||||||
|
.. _fixed-index-format:
|
||||||
|
|
||||||
|
Fixed Index Format (``.fidx``)
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
All numbers are stored as little-endian.
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
|
||||||
|
* - ``MAGIC: [u8; 8]``
|
||||||
|
- ``[47, 127, 65, 237, 145, 253, 15, 205]``
|
||||||
|
* - ``uuid: [u8; 16]``,
|
||||||
|
- Unique ID
|
||||||
|
* - ``ctime: i64``,
|
||||||
|
- Creation Time (epoch)
|
||||||
|
* - ``index_csum: [u8; 32]``,
|
||||||
|
- Sha256 over the index (without header) ``SHA256(digest1||digest2||...)``
|
||||||
|
* - ``size: u64``,
|
||||||
|
- Image size
|
||||||
|
* - ``chunk_size: u64``,
|
||||||
|
- Chunk size
|
||||||
|
* - ``reserved: [u8; 4016]``,
|
||||||
|
- overall header size is one page (4096 bytes)
|
||||||
|
* - ``digest1: [u8; 32]``
|
||||||
|
- first chunk digest
|
||||||
|
* - ``digest2: [u8; 32]``
|
||||||
|
- next chunk
|
||||||
|
* - ...
|
||||||
|
- next chunk ...
|
||||||
|
|
||||||
|
|
||||||
|
.. _dynamic-index-format:
|
||||||
|
|
||||||
|
Dynamic Index Format (``.didx``)
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
All numbers are stored as little-endian.
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
|
||||||
|
* - ``MAGIC: [u8; 8]``
|
||||||
|
- ``[28, 145, 78, 165, 25, 186, 179, 205]``
|
||||||
|
* - ``uuid: [u8; 16]``,
|
||||||
|
- Unique ID
|
||||||
|
* - ``ctime: i64``,
|
||||||
|
- Creation Time (epoch)
|
||||||
|
* - ``index_csum: [u8; 32]``,
|
||||||
|
- Sha256 over the index (without header) ``SHA256(offset1||digest1||offset2||digest2||...)``
|
||||||
|
* - ``reserved: [u8; 4032]``,
|
||||||
|
- Overall header size is one page (4096 bytes)
|
||||||
|
* - ``offset1: u64``
|
||||||
|
- End of first chunk
|
||||||
|
* - ``digest1: [u8; 32]``
|
||||||
|
- first chunk digest
|
||||||
|
* - ``offset2: u64``
|
||||||
|
- End of second chunk
|
||||||
|
* - ``digest2: [u8; 32]``
|
||||||
|
- second chunk digest
|
||||||
|
* - ...
|
||||||
|
- next chunk offset/digest
|
||||||
|
@ -129,7 +129,7 @@ top panel to view:
|
|||||||
* **Content**: Information on the datastore's backup groups and their respective
|
* **Content**: Information on the datastore's backup groups and their respective
|
||||||
contents
|
contents
|
||||||
* **Prune & GC**: Schedule :ref:`pruning <backup-pruning>` and :ref:`garbage
|
* **Prune & GC**: Schedule :ref:`pruning <backup-pruning>` and :ref:`garbage
|
||||||
collection <garbage-collection>` operations, and run garbage collection
|
collection <client_garbage-collection>` operations, and run garbage collection
|
||||||
manually
|
manually
|
||||||
* **Sync Jobs**: Create, manage and run :ref:`syncjobs` from remote servers
|
* **Sync Jobs**: Create, manage and run :ref:`syncjobs` from remote servers
|
||||||
* **Verify Jobs**: Create, manage and run :ref:`maintenance_verification` jobs on the
|
* **Verify Jobs**: Create, manage and run :ref:`maintenance_verification` jobs on the
|
||||||
|
BIN
docs/images/screenshots/pbs-gui-tfa-add-recovery-keys.png
Normal file
BIN
docs/images/screenshots/pbs-gui-tfa-add-recovery-keys.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
BIN
docs/images/screenshots/pbs-gui-tfa-add-totp.png
Normal file
BIN
docs/images/screenshots/pbs-gui-tfa-add-totp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 31 KiB |
BIN
docs/images/screenshots/pbs-gui-tfa-login.png
Normal file
BIN
docs/images/screenshots/pbs-gui-tfa-login.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
Welcome to the Proxmox Backup documentation!
|
Welcome to the Proxmox Backup documentation!
|
||||||
============================================
|
============================================
|
||||||
| Copyright (C) 2019-2020 Proxmox Server Solutions GmbH
|
| Copyright (C) 2019-2021 Proxmox Server Solutions GmbH
|
||||||
| Version |version| -- |today|
|
| Version |version| -- |today|
|
||||||
|
|
||||||
Permission is granted to copy, distribute and/or modify this document under the
|
Permission is granted to copy, distribute and/or modify this document under the
|
||||||
@ -33,6 +33,7 @@ in the section entitled "GNU Free Documentation License".
|
|||||||
pve-integration.rst
|
pve-integration.rst
|
||||||
pxar-tool.rst
|
pxar-tool.rst
|
||||||
sysadmin.rst
|
sysadmin.rst
|
||||||
|
technical-overview.rst
|
||||||
faq.rst
|
faq.rst
|
||||||
|
|
||||||
.. raw:: latex
|
.. raw:: latex
|
||||||
@ -44,6 +45,7 @@ in the section entitled "GNU Free Documentation License".
|
|||||||
:caption: Appendix
|
:caption: Appendix
|
||||||
|
|
||||||
command-syntax.rst
|
command-syntax.rst
|
||||||
|
configuration-files.rst
|
||||||
file-formats.rst
|
file-formats.rst
|
||||||
backup-protocol.rst
|
backup-protocol.rst
|
||||||
calendarevents.rst
|
calendarevents.rst
|
||||||
|
@ -14,11 +14,12 @@ It supports deduplication, compression, and authenticated
|
|||||||
encryption (AE_). Using :term:`Rust` as the implementation language guarantees high
|
encryption (AE_). Using :term:`Rust` as the implementation language guarantees high
|
||||||
performance, low resource usage, and a safe, high-quality codebase.
|
performance, low resource usage, and a safe, high-quality codebase.
|
||||||
|
|
||||||
Proxmox Backup uses state of the art cryptography for client communication and
|
Proxmox Backup uses state of the art cryptography for both client-server
|
||||||
backup content :ref:`encryption <encryption>`. Encryption is done on the
|
communication and backup content :ref:`encryption <client_encryption>`. All
|
||||||
client side, making it safer to back up data to targets that are not fully
|
client-server communication uses `TLS
|
||||||
trusted.
|
<https://en.wikipedia.org/wiki/Transport_Layer_Security>`_, and backup data can
|
||||||
|
be encrypted on the client-side before sending, making it safer to back up data
|
||||||
|
to targets that are not fully trusted.
|
||||||
|
|
||||||
Architecture
|
Architecture
|
||||||
------------
|
------------
|
||||||
@ -64,9 +65,10 @@ Main Features
|
|||||||
:Compression: The ultra-fast Zstandard_ compression is able to compress
|
:Compression: The ultra-fast Zstandard_ compression is able to compress
|
||||||
several gigabytes of data per second.
|
several gigabytes of data per second.
|
||||||
|
|
||||||
:Encryption: Backups can be encrypted on the client-side, using AES-256 in
|
:Encryption: Backups can be encrypted on the client-side, using AES-256 GCM_.
|
||||||
Galois/Counter Mode (GCM_) mode. This authenticated encryption (AE_) mode
|
This authenticated encryption (AE_) mode provides very high performance on
|
||||||
provides very high performance on modern hardware.
|
modern hardware. In addition to client-side encryption, all data is
|
||||||
|
transferred via a secure TLS connection.
|
||||||
|
|
||||||
:Web interface: Manage the Proxmox Backup Server with the integrated, web-based
|
:Web interface: Manage the Proxmox Backup Server with the integrated, web-based
|
||||||
user interface.
|
user interface.
|
||||||
@ -74,8 +76,16 @@ Main Features
|
|||||||
:Open Source: No secrets. Proxmox Backup Server is free and open-source
|
:Open Source: No secrets. Proxmox Backup Server is free and open-source
|
||||||
software. The source code is licensed under AGPL, v3.
|
software. The source code is licensed under AGPL, v3.
|
||||||
|
|
||||||
:Support: Enterprise support will be available from `Proxmox`_ once the beta
|
:No Limits: Proxmox Backup Server has no artifical limits for backup storage or
|
||||||
phase is over.
|
backup-clients.
|
||||||
|
|
||||||
|
:Enterprise Support: Proxmox Server Solutions GmbH offers enterprise support in
|
||||||
|
form of `Proxmox Backup Server Subscription Plans
|
||||||
|
<https://www.proxmox.com/en/proxmox-backup-server/pricing>`_. Users at every
|
||||||
|
subscription level get access to the Proxmox Backup :ref:`Enterprise
|
||||||
|
Repository <sysadmin_package_repos_enterprise>`. In addition, with a Basic,
|
||||||
|
Standard or Premium subscription, users have access to the :ref:`Proxmox
|
||||||
|
Customer Portal <get_help_enterprise_support>`.
|
||||||
|
|
||||||
|
|
||||||
Reasons for Data Backup?
|
Reasons for Data Backup?
|
||||||
@ -115,8 +125,8 @@ Proxmox Backup Server consists of multiple components:
|
|||||||
* A client CLI tool (`proxmox-backup-client`) to access the server easily from
|
* A client CLI tool (`proxmox-backup-client`) to access the server easily from
|
||||||
any `Linux amd64` environment
|
any `Linux amd64` environment
|
||||||
|
|
||||||
Aside from the web interface, everything is written in the Rust programming
|
Aside from the web interface, most parts of Proxmox Backup Server are written in
|
||||||
language.
|
the Rust programming language.
|
||||||
|
|
||||||
"The Rust programming language helps you write faster, more reliable software.
|
"The Rust programming language helps you write faster, more reliable software.
|
||||||
High-level ergonomics and low-level control are often at odds in programming
|
High-level ergonomics and low-level control are often at odds in programming
|
||||||
@ -132,6 +142,17 @@ language.
|
|||||||
Getting Help
|
Getting Help
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
.. _get_help_enterprise_support:
|
||||||
|
|
||||||
|
Enterprise Support
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Users with a `Proxmox Backup Server Basic, Standard or Premium Subscription Plan
|
||||||
|
<https://www.proxmox.com/en/proxmox-backup-server/pricing>`_ have access to the
|
||||||
|
Proxmox Customer Portal. The Customer Portal provides support with guaranteed
|
||||||
|
response times from the Proxmox developers.
|
||||||
|
For more information or for volume discounts, please contact office@proxmox.com.
|
||||||
|
|
||||||
Community Support Forum
|
Community Support Forum
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
@ -159,7 +180,7 @@ of the issue and will send a notification once it has been solved.
|
|||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
||||||
Copyright (C) 2019-2020 Proxmox Server Solutions GmbH
|
Copyright (C) 2019-2021 Proxmox Server Solutions GmbH
|
||||||
|
|
||||||
This software is written by Proxmox Server Solutions GmbH <support@proxmox.com>
|
This software is written by Proxmox Server Solutions GmbH <support@proxmox.com>
|
||||||
|
|
||||||
|
344
docs/lto-barcode/code39.js
Normal file
344
docs/lto-barcode/code39.js
Normal file
@ -0,0 +1,344 @@
|
|||||||
|
// Code39 barcode generator
|
||||||
|
// see https://en.wikipedia.org/wiki/Code_39
|
||||||
|
|
||||||
|
// IBM LTO Ultrium Cartridge Label Specification
|
||||||
|
// http://www-01.ibm.com/support/docview.wss?uid=ssg1S7000429
|
||||||
|
|
||||||
|
const code39_codes = {
|
||||||
|
"1": ['B', 's', 'b', 'S', 'b', 's', 'b', 's', 'B'],
|
||||||
|
"A": ['B', 's', 'b', 's', 'b', 'S', 'b', 's', 'B'],
|
||||||
|
"K": ['B', 's', 'b', 's', 'b', 's', 'b', 'S', 'B'],
|
||||||
|
"U": ['B', 'S', 'b', 's', 'b', 's', 'b', 's', 'B'],
|
||||||
|
|
||||||
|
"2": ['b', 's', 'B', 'S', 'b', 's', 'b', 's', 'B'],
|
||||||
|
"B": ['b', 's', 'B', 's', 'b', 'S', 'b', 's', 'B'],
|
||||||
|
"L": ['b', 's', 'B', 's', 'b', 's', 'b', 'S', 'B'],
|
||||||
|
"V": ['b', 'S', 'B', 's', 'b', 's', 'b', 's', 'B'],
|
||||||
|
|
||||||
|
"3": ['B', 's', 'B', 'S', 'b', 's', 'b', 's', 'b'],
|
||||||
|
"C": ['B', 's', 'B', 's', 'b', 'S', 'b', 's', 'b'],
|
||||||
|
"M": ['B', 's', 'B', 's', 'b', 's', 'b', 'S', 'b'],
|
||||||
|
"W": ['B', 'S', 'B', 's', 'b', 's', 'b', 's', 'b'],
|
||||||
|
|
||||||
|
"4": ['b', 's', 'b', 'S', 'B', 's', 'b', 's', 'B'],
|
||||||
|
"D": ['b', 's', 'b', 's', 'B', 'S', 'b', 's', 'B'],
|
||||||
|
"N": ['b', 's', 'b', 's', 'B', 's', 'b', 'S', 'B'],
|
||||||
|
"X": ['b', 'S', 'b', 's', 'B', 's', 'b', 's', 'B'],
|
||||||
|
|
||||||
|
"5": ['B', 's', 'b', 'S', 'B', 's', 'b', 's', 'b'],
|
||||||
|
"E": ['B', 's', 'b', 's', 'B', 'S', 'b', 's', 'b'],
|
||||||
|
"O": ['B', 's', 'b', 's', 'B', 's', 'b', 'S', 'b'],
|
||||||
|
"Y": ['B', 'S', 'b', 's', 'B', 's', 'b', 's', 'b'],
|
||||||
|
|
||||||
|
"6": ['b', 's', 'B', 'S', 'B', 's', 'b', 's', 'b'],
|
||||||
|
"F": ['b', 's', 'B', 's', 'B', 'S', 'b', 's', 'b'],
|
||||||
|
"P": ['b', 's', 'B', 's', 'B', 's', 'b', 'S', 'b'],
|
||||||
|
"Z": ['b', 'S', 'B', 's', 'B', 's', 'b', 's', 'b'],
|
||||||
|
|
||||||
|
"7": ['b', 's', 'b', 'S', 'b', 's', 'B', 's', 'B'],
|
||||||
|
"G": ['b', 's', 'b', 's', 'b', 'S', 'B', 's', 'B'],
|
||||||
|
"Q": ['b', 's', 'b', 's', 'b', 's', 'B', 'S', 'B'],
|
||||||
|
"-": ['b', 'S', 'b', 's', 'b', 's', 'B', 's', 'B'],
|
||||||
|
|
||||||
|
"8": ['B', 's', 'b', 'S', 'b', 's', 'B', 's', 'b'],
|
||||||
|
"H": ['B', 's', 'b', 's', 'b', 'S', 'B', 's', 'b'],
|
||||||
|
"R": ['B', 's', 'b', 's', 'b', 's', 'B', 'S', 'b'],
|
||||||
|
".": ['B', 'S', 'b', 's', 'b', 's', 'B', 's', 'b'],
|
||||||
|
|
||||||
|
"9": ['b', 's', 'B', 'S', 'b', 's', 'B', 's', 'b'],
|
||||||
|
"I": ['b', 's', 'B', 's', 'b', 'S', 'B', 's', 'b'],
|
||||||
|
"S": ['b', 's', 'B', 's', 'b', 's', 'B', 'S', 'b'],
|
||||||
|
" ": ['b', 'S', 'B', 's', 'b', 's', 'B', 's', 'b'],
|
||||||
|
|
||||||
|
"0": ['b', 's', 'b', 'S', 'B', 's', 'B', 's', 'b'],
|
||||||
|
"J": ['b', 's', 'b', 's', 'B', 'S', 'B', 's', 'b'],
|
||||||
|
"T": ['b', 's', 'b', 's', 'B', 's', 'B', 'S', 'b'],
|
||||||
|
"*": ['b', 'S', 'b', 's', 'B', 's', 'B', 's', 'b'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const colors = [
|
||||||
|
'#BB282E',
|
||||||
|
'#FAE54A',
|
||||||
|
'#9AC653',
|
||||||
|
'#01A5E2',
|
||||||
|
'#9EAAB6',
|
||||||
|
'#D97E35',
|
||||||
|
'#E27B99',
|
||||||
|
'#67A945',
|
||||||
|
'#F6B855',
|
||||||
|
'#705A81',
|
||||||
|
];
|
||||||
|
|
||||||
|
const lto_label_width = 70;
|
||||||
|
const lto_label_height = 16.9;
|
||||||
|
|
||||||
|
function foreach_label(page_layout, callback) {
|
||||||
|
let count = 0;
|
||||||
|
let row = 0;
|
||||||
|
let height = page_layout.margin_top;
|
||||||
|
|
||||||
|
while ((height + page_layout.label_height) <= page_layout.page_height) {
|
||||||
|
let column = 0;
|
||||||
|
let width = page_layout.margin_left;
|
||||||
|
|
||||||
|
while ((width + page_layout.label_width) <= page_layout.page_width) {
|
||||||
|
callback(column, row, count, width, height);
|
||||||
|
count += 1;
|
||||||
|
|
||||||
|
column += 1;
|
||||||
|
width += page_layout.label_width;
|
||||||
|
width += page_layout.column_spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
row += 1;
|
||||||
|
height += page_layout.label_height;
|
||||||
|
height += page_layout.row_spacing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function compute_max_labels(page_layout) {
|
||||||
|
let max_labels = 0;
|
||||||
|
foreach_label(page_layout, function() { max_labels += 1; });
|
||||||
|
return max_labels;
|
||||||
|
}
|
||||||
|
|
||||||
|
function svg_label(mode, label, label_type, pagex, pagey, label_borders) {
|
||||||
|
let svg = "";
|
||||||
|
|
||||||
|
if (label.length !== 6) {
|
||||||
|
throw "wrong label length";
|
||||||
|
}
|
||||||
|
if (label_type.length !== 2) {
|
||||||
|
throw "wrong label_type length";
|
||||||
|
}
|
||||||
|
|
||||||
|
let ratio = 2.75;
|
||||||
|
let parts = 3*ratio + 6; // 3*wide + 6*small;
|
||||||
|
let barcode_width = (lto_label_width/12)*10; // 10*code + 2margin
|
||||||
|
let small = barcode_width/(parts*10 + 9);
|
||||||
|
let code_width = small*parts;
|
||||||
|
let wide = small*ratio;
|
||||||
|
let xpos = pagex + code_width;
|
||||||
|
let height = 12;
|
||||||
|
|
||||||
|
let label_rect = `x='${pagex}' y='${pagey}' width='${lto_label_width}' height='${lto_label_height}'`;
|
||||||
|
|
||||||
|
if (mode === 'placeholder') {
|
||||||
|
if (label_borders) {
|
||||||
|
svg += `<rect class='unprintable' ${label_rect} fill='none' style='stroke:black;stroke-width:0.1;'/>`;
|
||||||
|
}
|
||||||
|
return svg;
|
||||||
|
}
|
||||||
|
if (label_borders) {
|
||||||
|
svg += `<rect ${label_rect} fill='none' style='stroke:black;stroke-width:0.1;'/>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === "color" || mode === "frame") {
|
||||||
|
let w = lto_label_width/8;
|
||||||
|
let h = lto_label_height - height;
|
||||||
|
for (let i = 0; i < 7; i++) {
|
||||||
|
let textx = w/2 + pagex + i*w;
|
||||||
|
let texty = pagey;
|
||||||
|
|
||||||
|
let fill = "none";
|
||||||
|
if (mode === "color" && (i < 6)) {
|
||||||
|
let letter = label.charAt(i);
|
||||||
|
if (letter >= '0' && letter <= '9') {
|
||||||
|
fill = colors[parseInt(letter, 10)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svg += `<rect x='${textx}' y='${texty}' width='${w}' height='${h}' style='stroke:black;stroke-width:0.2;fill:${fill};'/>`;
|
||||||
|
|
||||||
|
if (i == 6) {
|
||||||
|
textx += 3;
|
||||||
|
texty += 3.7;
|
||||||
|
svg += `<text x='${textx}' y='${texty}' style='font-weight:bold;font-size:3px;font-family:sans-serif;'>${label_type}</text>`;
|
||||||
|
} else {
|
||||||
|
let letter = label.charAt(i);
|
||||||
|
textx += 3.5;
|
||||||
|
texty += 4;
|
||||||
|
svg += `<text x='${textx}' y='${texty}' style='font-weight:bold;font-size:4px;font-family:sans-serif;'>${letter}</text>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let raw_label = `*${label}${label_type}*`;
|
||||||
|
|
||||||
|
for (let i = 0; i < raw_label.length; i++) {
|
||||||
|
let letter = raw_label.charAt(i);
|
||||||
|
|
||||||
|
let code = code39_codes[letter];
|
||||||
|
if (code === undefined) {
|
||||||
|
throw `unable to encode letter '${letter}' with code39`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === "simple") {
|
||||||
|
let textx = xpos + code_width/2;
|
||||||
|
let texty = pagey + 4;
|
||||||
|
|
||||||
|
if (i > 0 && (i+1) < raw_label.length) {
|
||||||
|
svg += `<text x='${textx}' y='${texty}' style='font-weight:bold;font-size:4px;font-family:sans-serif;'>${letter}</text>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let c of code) {
|
||||||
|
if (c === 's') {
|
||||||
|
xpos += small;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (c === 'S') {
|
||||||
|
xpos += wide;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let w = c === 'B' ? wide : small;
|
||||||
|
let ypos = pagey + lto_label_height - height;
|
||||||
|
|
||||||
|
svg += `<rect x='${xpos}' y='${ypos}' width='${w}' height='${height}' style='fill:black'/>`;
|
||||||
|
xpos = xpos + w;
|
||||||
|
}
|
||||||
|
xpos += small;
|
||||||
|
}
|
||||||
|
|
||||||
|
return svg;
|
||||||
|
}
|
||||||
|
|
||||||
|
function html_page_header() {
|
||||||
|
let html = "<html5>";
|
||||||
|
|
||||||
|
html += "<style>";
|
||||||
|
|
||||||
|
/* no page margins */
|
||||||
|
html += "@page{margin-left: 0px;margin-right: 0px;margin-top: 0px;margin-bottom: 0px;}";
|
||||||
|
/* to hide things on printed page */
|
||||||
|
html += "@media print { .unprintable { visibility: hidden; } }";
|
||||||
|
|
||||||
|
html += "</style>";
|
||||||
|
|
||||||
|
//html += "<body onload='window.print()'>";
|
||||||
|
html += "<body style='background-color: white;'>";
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function svg_page_header(page_width, page_height) {
|
||||||
|
let svg = "<svg version='1.1' xmlns='http://www.w3.org/2000/svg'";
|
||||||
|
svg += ` width='${page_width}mm' height='${page_height}mm' viewBox='0 0 ${page_width} ${page_height}'>`;
|
||||||
|
|
||||||
|
return svg;
|
||||||
|
}
|
||||||
|
|
||||||
|
function printBarcodePage() {
|
||||||
|
let frame = document.getElementById("print_frame");
|
||||||
|
|
||||||
|
let window = frame.contentWindow;
|
||||||
|
window.print();
|
||||||
|
}
|
||||||
|
|
||||||
|
function generate_barcode_page(target_id, page_layout, label_list, calibration) {
|
||||||
|
let svg = svg_page_header(page_layout.page_width, page_layout.page_height);
|
||||||
|
|
||||||
|
let c = calibration;
|
||||||
|
|
||||||
|
console.log(calibration);
|
||||||
|
|
||||||
|
svg += "<g id='barcode_page'";
|
||||||
|
if (c !== undefined) {
|
||||||
|
svg += ` transform='scale(${c.scalex}, ${c.scaley}),translate(${c.offsetx}, ${c.offsety})'`;
|
||||||
|
}
|
||||||
|
svg += '>';
|
||||||
|
|
||||||
|
foreach_label(page_layout, function(column, row, count, xpos, ypos) {
|
||||||
|
if (count >= label_list.length) { return; }
|
||||||
|
|
||||||
|
let item = label_list[count];
|
||||||
|
|
||||||
|
svg += svg_label(item.mode, item.label, item.tape_type, xpos, ypos, page_layout.label_borders);
|
||||||
|
});
|
||||||
|
|
||||||
|
svg += "</g>";
|
||||||
|
svg += "</svg>";
|
||||||
|
|
||||||
|
let html = html_page_header();
|
||||||
|
html += svg;
|
||||||
|
html += "</body>";
|
||||||
|
html += "</html>";
|
||||||
|
|
||||||
|
let frame = document.getElementById(target_id);
|
||||||
|
|
||||||
|
setupPrintFrame(frame, page_layout.page_width, page_layout.page_height);
|
||||||
|
|
||||||
|
let fwindow = frame.contentWindow;
|
||||||
|
|
||||||
|
fwindow.document.open();
|
||||||
|
fwindow.document.write(html);
|
||||||
|
fwindow.document.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupPrintFrame(frame, page_width, page_height) {
|
||||||
|
let dpi = 98;
|
||||||
|
|
||||||
|
let dpr = window.devicePixelRatio;
|
||||||
|
if (dpr !== undefined) {
|
||||||
|
dpi = dpi*dpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ppmm = dpi/25.4;
|
||||||
|
|
||||||
|
frame.width = page_width*ppmm;
|
||||||
|
frame.height = page_height*ppmm;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generate_calibration_page(target_id, page_layout, calibration) {
|
||||||
|
let frame = document.getElementById(target_id);
|
||||||
|
|
||||||
|
setupPrintFrame(frame, page_layout.page_width, page_layout.page_height);
|
||||||
|
|
||||||
|
let svg = svg_page_header(page_layout.page_width, page_layout.page_height);
|
||||||
|
|
||||||
|
svg += "<defs>";
|
||||||
|
svg += "<marker id='endarrow' markerWidth='10' markerHeight='7' ";
|
||||||
|
svg += "refX='10' refY='3.5' orient='auto'><polygon points='0 0, 10 3.5, 0 7' />";
|
||||||
|
svg += "</marker>";
|
||||||
|
|
||||||
|
svg += "<marker id='startarrow' markerWidth='10' markerHeight='7' ";
|
||||||
|
svg += "refX='0' refY='3.5' orient='auto'><polygon points='10 0, 10 7, 0 3.5' />";
|
||||||
|
svg += "</marker>";
|
||||||
|
svg += "</defs>";
|
||||||
|
|
||||||
|
svg += "<rect x='50' y='50' width='100' height='100' style='fill:none;stroke-width:0.05;stroke:rgb(0,0,0)'/>";
|
||||||
|
|
||||||
|
let text_style = "style='font-weight:bold;font-size:4;font-family:sans-serif;'";
|
||||||
|
|
||||||
|
svg += `<text x='10' y='99' ${text_style}>Sx = 50mm</text>`;
|
||||||
|
svg += "<line x1='0' y1='100' x2='50' y2='100' stroke='#000' marker-end='url(#endarrow)' stroke-width='.25'/>";
|
||||||
|
|
||||||
|
svg += `<text x='60' y='99' ${text_style}>Dx = 100mm</text>`;
|
||||||
|
svg += "<line x1='50' y1='100' x2='150' y2='100' stroke='#000' marker-start='url(#startarrow)' marker-end='url(#endarrow)' stroke-width='.25'/>";
|
||||||
|
|
||||||
|
svg += `<text x='142' y='10' ${text_style} writing-mode='tb'>Sy = 50mm</text>`;
|
||||||
|
svg += "<line x1='140' y1='0' x2='140' y2='50' stroke='#000' marker-end='url(#endarrow)' stroke-width='.25'/>";
|
||||||
|
|
||||||
|
svg += `<text x='142' y='60' ${text_style} writing-mode='tb'>Dy = 100mm</text>`;
|
||||||
|
svg += "<line x1='140' y1='50' x2='140' y2='150' stroke='#000' marker-start='url(#startarrow)' marker-end='url(#endarrow)' stroke-width='.25'/>";
|
||||||
|
|
||||||
|
let c = calibration;
|
||||||
|
if (c !== undefined) {
|
||||||
|
svg += `<rect x='50' y='50' width='100' height='100' style='fill:none;stroke-width:0.05;stroke:rgb(255,0,0)' `;
|
||||||
|
svg += `transform='scale(${c.scalex}, ${c.scaley}),translate(${c.offsetx}, ${c.offsety})'/>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg += "</svg>";
|
||||||
|
|
||||||
|
let html = html_page_header();
|
||||||
|
html += svg;
|
||||||
|
html += "</body>";
|
||||||
|
html += "</html>";
|
||||||
|
|
||||||
|
let fwindow = frame.contentWindow;
|
||||||
|
|
||||||
|
fwindow.document.open();
|
||||||
|
fwindow.document.write(html);
|
||||||
|
fwindow.document.close();
|
||||||
|
}
|
51
docs/lto-barcode/index.html
Normal file
51
docs/lto-barcode/index.html
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||||
|
<title>Proxmox LTO Barcode Label Generator</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="extjs/theme-crisp/resources/theme-crisp-all.css">
|
||||||
|
<style>
|
||||||
|
/* fix action column icons */
|
||||||
|
.x-action-col-icon {
|
||||||
|
font-size: 13px;
|
||||||
|
height: 13px;
|
||||||
|
}
|
||||||
|
.x-grid-cell-inner-action-col {
|
||||||
|
padding: 6px 10px 5px;
|
||||||
|
}
|
||||||
|
.x-action-col-icon:before {
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
.x-action-col-icon {
|
||||||
|
color: #21BF4B;
|
||||||
|
}
|
||||||
|
.x-action-col-icon {
|
||||||
|
margin: 0 1px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.x-action-col-icon:before, .x-action-col-icon:after {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.x-action-col-icon:hover:before, .x-action-col-icon:hover:after {
|
||||||
|
text-shadow: 1px 1px 1px #AAA;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<link rel="stylesheet" type="text/css" href="font-awesome/css/font-awesome.css"/>
|
||||||
|
<script type="text/javascript" src="extjs/ext-all.js"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="code39.js"></script>
|
||||||
|
<script type="text/javascript" src="prefix-field.js"></script>
|
||||||
|
<script type="text/javascript" src="label-style.js"></script>
|
||||||
|
<script type="text/javascript" src="tape-type.js"></script>
|
||||||
|
<script type="text/javascript" src="paper-size.js"></script>
|
||||||
|
<script type="text/javascript" src="page-layout.js"></script>
|
||||||
|
<script type="text/javascript" src="page-calibration.js"></script>
|
||||||
|
<script type="text/javascript" src="label-list.js"></script>
|
||||||
|
<script type="text/javascript" src="label-setup.js"></script>
|
||||||
|
<script type="text/javascript" src="lto-barcode.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
140
docs/lto-barcode/label-list.js
Normal file
140
docs/lto-barcode/label-list.js
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
Ext.define('LabelList', {
|
||||||
|
extend: 'Ext.grid.Panel',
|
||||||
|
alias: 'widget.labelList',
|
||||||
|
|
||||||
|
plugins: {
|
||||||
|
ptype: 'cellediting',
|
||||||
|
clicksToEdit: 1,
|
||||||
|
},
|
||||||
|
|
||||||
|
selModel: 'cellmodel',
|
||||||
|
|
||||||
|
store: {
|
||||||
|
field: [
|
||||||
|
'prefix',
|
||||||
|
'tape_type',
|
||||||
|
{
|
||||||
|
type: 'integer',
|
||||||
|
name: 'start',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'integer',
|
||||||
|
name: 'end',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
data: [],
|
||||||
|
},
|
||||||
|
|
||||||
|
listeners: {
|
||||||
|
validateedit: function(editor, context) {
|
||||||
|
console.log(context.field);
|
||||||
|
console.log(context.value);
|
||||||
|
context.record.set(context.field, context.value);
|
||||||
|
context.record.commit();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
text: 'Prefix',
|
||||||
|
dataIndex: 'prefix',
|
||||||
|
flex: 1,
|
||||||
|
editor: {
|
||||||
|
xtype: 'prefixfield',
|
||||||
|
allowBlank: false,
|
||||||
|
},
|
||||||
|
renderer: function(value, metaData, record) {
|
||||||
|
console.log(record);
|
||||||
|
if (record.data.mode === 'placeholder') {
|
||||||
|
return "-";
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Type',
|
||||||
|
dataIndex: 'tape_type',
|
||||||
|
flex: 1,
|
||||||
|
editor: {
|
||||||
|
xtype: 'ltoTapeType',
|
||||||
|
allowBlank: false,
|
||||||
|
},
|
||||||
|
renderer: function(value, metaData, record) {
|
||||||
|
console.log(record);
|
||||||
|
if (record.data.mode === 'placeholder') {
|
||||||
|
return "-";
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Mode',
|
||||||
|
dataIndex: 'mode',
|
||||||
|
flex: 1,
|
||||||
|
editor: {
|
||||||
|
xtype: 'ltoLabelStyle',
|
||||||
|
allowBlank: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Start',
|
||||||
|
dataIndex: 'start',
|
||||||
|
flex: 1,
|
||||||
|
editor: {
|
||||||
|
xtype: 'numberfield',
|
||||||
|
allowBlank: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'End',
|
||||||
|
dataIndex: 'end',
|
||||||
|
flex: 1,
|
||||||
|
editor: {
|
||||||
|
xtype: 'numberfield',
|
||||||
|
},
|
||||||
|
renderer: function(value) {
|
||||||
|
if (value === null || value === '' || value === undefined) {
|
||||||
|
return "Fill";
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'actioncolumn',
|
||||||
|
width: 75,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
tooltip: 'Move Up',
|
||||||
|
iconCls: 'fa fa-arrow-up',
|
||||||
|
handler: function(grid, rowIndex) {
|
||||||
|
if (rowIndex < 1) { return; }
|
||||||
|
let store = grid.getStore();
|
||||||
|
let record = store.getAt(rowIndex);
|
||||||
|
store.removeAt(rowIndex);
|
||||||
|
store.insert(rowIndex - 1, record);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tooltip: 'Move Down',
|
||||||
|
iconCls: 'fa fa-arrow-down',
|
||||||
|
handler: function(grid, rowIndex) {
|
||||||
|
let store = grid.getStore();
|
||||||
|
if (rowIndex >= store.getCount()) { return; }
|
||||||
|
let record = store.getAt(rowIndex);
|
||||||
|
store.removeAt(rowIndex);
|
||||||
|
store.insert(rowIndex + 1, record);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tooltip: 'Delete',
|
||||||
|
iconCls: 'fa fa-scissors',
|
||||||
|
//iconCls: 'fa critical fa-trash-o',
|
||||||
|
handler: function(grid, rowIndex) {
|
||||||
|
grid.getStore().removeAt(rowIndex);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
107
docs/lto-barcode/label-setup.js
Normal file
107
docs/lto-barcode/label-setup.js
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
Ext.define('LabelSetupPanel', {
|
||||||
|
extend: 'Ext.panel.Panel',
|
||||||
|
alias: 'widget.labelSetupPanel',
|
||||||
|
|
||||||
|
layout: {
|
||||||
|
type: 'hbox',
|
||||||
|
align: 'stretch',
|
||||||
|
pack: 'start',
|
||||||
|
},
|
||||||
|
|
||||||
|
getValues: function() {
|
||||||
|
let me = this;
|
||||||
|
|
||||||
|
let values = {};
|
||||||
|
|
||||||
|
Ext.Array.each(me.query('[isFormField]'), function(field) {
|
||||||
|
let data = field.getSubmitData();
|
||||||
|
Ext.Object.each(data, function(name, val) {
|
||||||
|
let parsed = parseInt(val, 10);
|
||||||
|
values[name] = isNaN(parsed) ? val : parsed;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return values;
|
||||||
|
},
|
||||||
|
|
||||||
|
controller: {
|
||||||
|
xclass: 'Ext.app.ViewController',
|
||||||
|
|
||||||
|
init: function() {
|
||||||
|
let me = this;
|
||||||
|
let view = me.getView();
|
||||||
|
let list = view.down("labelList");
|
||||||
|
let store = list.getStore();
|
||||||
|
store.on('datachanged', function(store) {
|
||||||
|
view.fireEvent("listchanged", store);
|
||||||
|
});
|
||||||
|
store.on('update', function(store) {
|
||||||
|
view.fireEvent("listchanged", store);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onAdd: function() {
|
||||||
|
let list = this.lookupReference('label_list');
|
||||||
|
let view = this.getView();
|
||||||
|
let params = view.getValues();
|
||||||
|
list.getStore().add(params);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
border: false,
|
||||||
|
layout: {
|
||||||
|
type: 'vbox',
|
||||||
|
align: 'stretch',
|
||||||
|
pack: 'start',
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
xtype: 'prefixfield',
|
||||||
|
name: 'prefix',
|
||||||
|
value: 'TEST',
|
||||||
|
fieldLabel: 'Prefix',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'ltoTapeType',
|
||||||
|
name: 'tape_type',
|
||||||
|
fieldLabel: 'Type',
|
||||||
|
value: 'L8',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'ltoLabelStyle',
|
||||||
|
name: 'mode',
|
||||||
|
fieldLabel: 'Mode',
|
||||||
|
value: 'color',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'numberfield',
|
||||||
|
name: 'start',
|
||||||
|
fieldLabel: 'Start',
|
||||||
|
minValue: 0,
|
||||||
|
allowBlank: false,
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'numberfield',
|
||||||
|
name: 'end',
|
||||||
|
fieldLabel: 'End',
|
||||||
|
minValue: 0,
|
||||||
|
emptyText: 'Fill',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'button',
|
||||||
|
text: 'Add',
|
||||||
|
handler: 'onAdd',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
margin: "0 0 0 10",
|
||||||
|
xtype: 'labelList',
|
||||||
|
reference: 'label_list',
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
20
docs/lto-barcode/label-style.js
Normal file
20
docs/lto-barcode/label-style.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
Ext.define('LtoLabelStyle', {
|
||||||
|
extend: 'Ext.form.field.ComboBox',
|
||||||
|
alias: 'widget.ltoLabelStyle',
|
||||||
|
|
||||||
|
editable: false,
|
||||||
|
|
||||||
|
displayField: 'text',
|
||||||
|
valueField: 'value',
|
||||||
|
queryMode: 'local',
|
||||||
|
|
||||||
|
store: {
|
||||||
|
field: ['value', 'text'],
|
||||||
|
data: [
|
||||||
|
{ value: 'simple', text: "Simple" },
|
||||||
|
{ value: 'color', text: 'Color (frames with color)' },
|
||||||
|
{ value: 'frame', text: 'Frame (no color)' },
|
||||||
|
{ value: 'placeholder', text: 'Placeholder (empty)' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
206
docs/lto-barcode/lto-barcode.js
Normal file
206
docs/lto-barcode/lto-barcode.js
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
// FIXME: HACK! Makes scrolling in number spinner work again. fixed in ExtJS >= 6.1
|
||||||
|
if (Ext.isFirefox) {
|
||||||
|
Ext.$eventNameMap.DOMMouseScroll = 'DOMMouseScroll';
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw_labels(target_id, label_list, page_layout, calibration) {
|
||||||
|
let max_labels = compute_max_labels(page_layout);
|
||||||
|
|
||||||
|
let count_fixed = 0;
|
||||||
|
let count_fill = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < label_list.length; i++) {
|
||||||
|
let item = label_list[i];
|
||||||
|
if (item.end === null || item.end === '' || item.end === undefined) {
|
||||||
|
count_fill += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (item.end <= item.start) {
|
||||||
|
count_fixed += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
count_fixed += (item.end - item.start) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rest = max_labels - count_fixed;
|
||||||
|
let fill_size = 1;
|
||||||
|
if (rest >= count_fill) {
|
||||||
|
fill_size = Math.floor(rest/count_fill);
|
||||||
|
}
|
||||||
|
|
||||||
|
let list = [];
|
||||||
|
|
||||||
|
let count_fill_2 = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < label_list.length; i++) {
|
||||||
|
let item = label_list[i];
|
||||||
|
let count;
|
||||||
|
if (item.end === null || item.end === '' || item.end === undefined) {
|
||||||
|
count_fill_2 += 1;
|
||||||
|
if (count_fill_2 === count_fill) {
|
||||||
|
count = rest;
|
||||||
|
} else {
|
||||||
|
count = fill_size;
|
||||||
|
}
|
||||||
|
rest -= count;
|
||||||
|
} else if (item.end <= item.start) {
|
||||||
|
count = 1;
|
||||||
|
} else {
|
||||||
|
count = (item.end - item.start) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < count; j++) {
|
||||||
|
let id = item.start + j;
|
||||||
|
|
||||||
|
if (item.prefix.length == 6) {
|
||||||
|
list.push({
|
||||||
|
label: item.prefix,
|
||||||
|
tape_type: item.tape_type,
|
||||||
|
mode: item.mode,
|
||||||
|
id: id,
|
||||||
|
});
|
||||||
|
rest += count - j - 1;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
let pad_len = 6-item.prefix.length;
|
||||||
|
let label = item.prefix + id.toString().padStart(pad_len, 0);
|
||||||
|
|
||||||
|
if (label.length != 6) {
|
||||||
|
rest += count - j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.push({
|
||||||
|
label: label,
|
||||||
|
tape_type: item.tape_type,
|
||||||
|
mode: item.mode,
|
||||||
|
id: id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_barcode_page(target_id, page_layout, list, calibration);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ext.define('MainView', {
|
||||||
|
extend: 'Ext.container.Viewport',
|
||||||
|
alias: 'widget.mainview',
|
||||||
|
|
||||||
|
layout: {
|
||||||
|
type: 'vbox',
|
||||||
|
align: 'stretch',
|
||||||
|
pack: 'start',
|
||||||
|
},
|
||||||
|
width: 800,
|
||||||
|
|
||||||
|
controller: {
|
||||||
|
xclass: 'Ext.app.ViewController',
|
||||||
|
|
||||||
|
update_barcode_preview: function() {
|
||||||
|
let me = this;
|
||||||
|
let view = me.getView();
|
||||||
|
let list_view = view.down("labelList");
|
||||||
|
|
||||||
|
let store = list_view.getStore();
|
||||||
|
let label_list = [];
|
||||||
|
store.each((record) => {
|
||||||
|
label_list.push(record.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
let page_layout_view = view.down("pageLayoutPanel");
|
||||||
|
let page_layout = page_layout_view.getValues();
|
||||||
|
|
||||||
|
let calibration_view = view.down("pageCalibration");
|
||||||
|
let page_calibration = calibration_view.getValues();
|
||||||
|
|
||||||
|
draw_labels("print_frame", label_list, page_layout, page_calibration);
|
||||||
|
},
|
||||||
|
|
||||||
|
update_calibration_preview: function() {
|
||||||
|
let me = this;
|
||||||
|
let view = me.getView();
|
||||||
|
let page_layout_view = view.down("pageLayoutPanel");
|
||||||
|
let page_layout = page_layout_view.getValues();
|
||||||
|
|
||||||
|
let calibration_view = view.down("pageCalibration");
|
||||||
|
let page_calibration = calibration_view.getValues();
|
||||||
|
console.log(page_calibration);
|
||||||
|
generate_calibration_page('print_frame', page_layout, page_calibration);
|
||||||
|
},
|
||||||
|
|
||||||
|
control: {
|
||||||
|
labelSetupPanel: {
|
||||||
|
listchanged: function(store) {
|
||||||
|
this.update_barcode_preview();
|
||||||
|
},
|
||||||
|
activate: function() {
|
||||||
|
this.update_barcode_preview();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pageLayoutPanel: {
|
||||||
|
pagechanged: function(layout) {
|
||||||
|
this.update_barcode_preview();
|
||||||
|
},
|
||||||
|
activate: function() {
|
||||||
|
this.update_barcode_preview();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pageCalibration: {
|
||||||
|
calibrationchanged: function() {
|
||||||
|
this.update_calibration_preview();
|
||||||
|
},
|
||||||
|
activate: function() {
|
||||||
|
this.update_calibration_preview();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
xtype: 'tabpanel',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
xtype: 'labelSetupPanel',
|
||||||
|
title: 'Proxmox LTO Barcode Label Generator',
|
||||||
|
bodyPadding: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'pageLayoutPanel',
|
||||||
|
title: 'Page Layout',
|
||||||
|
bodyPadding: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'pageCalibration',
|
||||||
|
title: 'Printer Calibration',
|
||||||
|
bodyPadding: 10,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'panel',
|
||||||
|
layout: "center",
|
||||||
|
title: 'Print Preview',
|
||||||
|
bodyStyle: "background-color: grey;",
|
||||||
|
bodyPadding: 10,
|
||||||
|
html: '<center><iframe id="print_frame" frameBorder="0"></iframe></center>',
|
||||||
|
border: false,
|
||||||
|
flex: 1,
|
||||||
|
scrollable: true,
|
||||||
|
tools: [{
|
||||||
|
type: 'print',
|
||||||
|
tooltip: 'Open Print Dialog',
|
||||||
|
handler: function(event, toolEl, panelHeader) {
|
||||||
|
printBarcodePage();
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
Ext.onReady(function() {
|
||||||
|
Ext.create('MainView', {
|
||||||
|
renderTo: Ext.getBody(),
|
||||||
|
});
|
||||||
|
});
|
142
docs/lto-barcode/page-calibration.js
Normal file
142
docs/lto-barcode/page-calibration.js
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
Ext.define('PageCalibration', {
|
||||||
|
extend: 'Ext.panel.Panel',
|
||||||
|
alias: 'widget.pageCalibration',
|
||||||
|
|
||||||
|
layout: {
|
||||||
|
type: 'hbox',
|
||||||
|
align: 'stretch',
|
||||||
|
pack: 'start',
|
||||||
|
},
|
||||||
|
|
||||||
|
getValues: function() {
|
||||||
|
let me = this;
|
||||||
|
|
||||||
|
let values = {};
|
||||||
|
|
||||||
|
Ext.Array.each(me.query('[isFormField]'), function(field) {
|
||||||
|
if (field.isValid()) {
|
||||||
|
let data = field.getSubmitData();
|
||||||
|
Ext.Object.each(data, function(name, val) {
|
||||||
|
let parsed = parseFloat(val, 10);
|
||||||
|
values[name] = isNaN(parsed) ? val : parsed;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (values.d_x === undefined) { return; }
|
||||||
|
if (values.d_y === undefined) { return; }
|
||||||
|
if (values.s_x === undefined) { return; }
|
||||||
|
if (values.s_y === undefined) { return; }
|
||||||
|
|
||||||
|
scalex = 100/values.d_x;
|
||||||
|
scaley = 100/values.d_y;
|
||||||
|
|
||||||
|
let offsetx = ((50 - values.s_x) - (50*scalex - 50))/scalex;
|
||||||
|
let offsety = ((50 - values.s_y) - (50*scaley - 50))/scaley;
|
||||||
|
|
||||||
|
return {
|
||||||
|
scalex: scalex,
|
||||||
|
scaley: scaley,
|
||||||
|
offsetx: offsetx,
|
||||||
|
offsety: offsety,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
controller: {
|
||||||
|
xclass: 'Ext.app.ViewController',
|
||||||
|
|
||||||
|
control: {
|
||||||
|
'field': {
|
||||||
|
change: function() {
|
||||||
|
let view = this.getView();
|
||||||
|
let param = view.getValues();
|
||||||
|
view.fireEvent("calibrationchanged", param);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
border: false,
|
||||||
|
layout: {
|
||||||
|
type: 'vbox',
|
||||||
|
align: 'stretch',
|
||||||
|
pack: 'start',
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
xtype: 'displayfield',
|
||||||
|
value: 'a4',
|
||||||
|
fieldLabel: 'Start Offset Sx (mm)',
|
||||||
|
labelWidth: 150,
|
||||||
|
value: 50,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'displayfield',
|
||||||
|
value: 'a4',
|
||||||
|
fieldLabel: 'Length Dx (mm)',
|
||||||
|
labelWidth: 150,
|
||||||
|
value: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'displayfield',
|
||||||
|
value: 'a4',
|
||||||
|
fieldLabel: 'Start Offset Sy (mm)',
|
||||||
|
labelWidth: 150,
|
||||||
|
value: 50,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'displayfield',
|
||||||
|
value: 'a4',
|
||||||
|
fieldLabel: 'Length Dy (mm)',
|
||||||
|
labelWidth: 150,
|
||||||
|
value: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
border: false,
|
||||||
|
margin: '0 0 0 20',
|
||||||
|
layout: {
|
||||||
|
type: 'vbox',
|
||||||
|
align: 'stretch',
|
||||||
|
pack: 'start',
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
xtype: 'numberfield',
|
||||||
|
value: 'a4',
|
||||||
|
name: 's_x',
|
||||||
|
fieldLabel: 'Meassured Start Offset Sx (mm)',
|
||||||
|
allowBlank: false,
|
||||||
|
labelWidth: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'numberfield',
|
||||||
|
value: 'a4',
|
||||||
|
name: 'd_x',
|
||||||
|
fieldLabel: 'Meassured Length Dx (mm)',
|
||||||
|
allowBlank: false,
|
||||||
|
labelWidth: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'numberfield',
|
||||||
|
value: 'a4',
|
||||||
|
name: 's_y',
|
||||||
|
fieldLabel: 'Meassured Start Offset Sy (mm)',
|
||||||
|
allowBlank: false,
|
||||||
|
labelWidth: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'numberfield',
|
||||||
|
value: 'a4',
|
||||||
|
name: 'd_y',
|
||||||
|
fieldLabel: 'Meassured Length Dy (mm)',
|
||||||
|
allowBlank: false,
|
||||||
|
labelWidth: 200,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
167
docs/lto-barcode/page-layout.js
Normal file
167
docs/lto-barcode/page-layout.js
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
Ext.define('PageLayoutPanel', {
|
||||||
|
extend: 'Ext.panel.Panel',
|
||||||
|
alias: 'widget.pageLayoutPanel',
|
||||||
|
|
||||||
|
layout: {
|
||||||
|
type: 'hbox',
|
||||||
|
align: 'stretch',
|
||||||
|
pack: 'start',
|
||||||
|
},
|
||||||
|
|
||||||
|
getValues: function() {
|
||||||
|
let me = this;
|
||||||
|
|
||||||
|
let values = {};
|
||||||
|
|
||||||
|
Ext.Array.each(me.query('[isFormField]'), function(field) {
|
||||||
|
if (field.isValid()) {
|
||||||
|
let data = field.getSubmitData();
|
||||||
|
Ext.Object.each(data, function(name, val) {
|
||||||
|
values[name] = val;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let paper_size = values.paper_size || 'a4';
|
||||||
|
|
||||||
|
let param = Ext.apply({}, paper_sizes[paper_size]);
|
||||||
|
if (param === undefined) {
|
||||||
|
throw `unknown paper size ${paper_size}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
param.paper_size = paper_size;
|
||||||
|
|
||||||
|
Ext.Object.each(values, function(name, val) {
|
||||||
|
let parsed = parseFloat(val, 10);
|
||||||
|
param[name] = isNaN(parsed) ? val : parsed;
|
||||||
|
});
|
||||||
|
|
||||||
|
return param;
|
||||||
|
},
|
||||||
|
|
||||||
|
controller: {
|
||||||
|
xclass: 'Ext.app.ViewController',
|
||||||
|
|
||||||
|
control: {
|
||||||
|
'paperSize': {
|
||||||
|
change: function(field, paper_size) {
|
||||||
|
let view = this.getView();
|
||||||
|
let defaults = paper_sizes[paper_size];
|
||||||
|
|
||||||
|
let names = [
|
||||||
|
'label_width',
|
||||||
|
'label_height',
|
||||||
|
'margin_left',
|
||||||
|
'margin_top',
|
||||||
|
'column_spacing',
|
||||||
|
'row_spacing',
|
||||||
|
];
|
||||||
|
for (i = 0; i < names.length; i++) {
|
||||||
|
let name = names[i];
|
||||||
|
let f = view.down(`field[name=${name}]`);
|
||||||
|
let v = defaults[name];
|
||||||
|
if (v != undefined) {
|
||||||
|
f.setValue(v);
|
||||||
|
f.setDisabled(defaults.fixed);
|
||||||
|
} else {
|
||||||
|
f.setDisabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'field': {
|
||||||
|
change: function() {
|
||||||
|
let view = this.getView();
|
||||||
|
let param = view.getValues();
|
||||||
|
view.fireEvent("pagechanged", param);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
border: false,
|
||||||
|
layout: {
|
||||||
|
type: 'vbox',
|
||||||
|
align: 'stretch',
|
||||||
|
pack: 'start',
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
xtype: 'paperSize',
|
||||||
|
name: 'paper_size',
|
||||||
|
value: 'a4',
|
||||||
|
fieldLabel: 'Paper Size',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'numberfield',
|
||||||
|
name: 'label_width',
|
||||||
|
fieldLabel: 'Label width',
|
||||||
|
minValue: 70,
|
||||||
|
allowBlank: false,
|
||||||
|
value: 70,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'numberfield',
|
||||||
|
name: 'label_height',
|
||||||
|
fieldLabel: 'Label height',
|
||||||
|
minValue: 15,
|
||||||
|
allowBlank: false,
|
||||||
|
value: 17,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'checkbox',
|
||||||
|
name: 'label_borders',
|
||||||
|
fieldLabel: 'Label borders',
|
||||||
|
value: true,
|
||||||
|
inputValue: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
border: false,
|
||||||
|
margin: '0 0 0 10',
|
||||||
|
layout: {
|
||||||
|
type: 'vbox',
|
||||||
|
align: 'stretch',
|
||||||
|
pack: 'start',
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
xtype: 'numberfield',
|
||||||
|
name: 'margin_left',
|
||||||
|
fieldLabel: 'Left margin',
|
||||||
|
minValue: 0,
|
||||||
|
allowBlank: false,
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'numberfield',
|
||||||
|
name: 'margin_top',
|
||||||
|
fieldLabel: 'Top margin',
|
||||||
|
minValue: 0,
|
||||||
|
allowBlank: false,
|
||||||
|
value: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'numberfield',
|
||||||
|
name: 'column_spacing',
|
||||||
|
fieldLabel: 'Column spacing',
|
||||||
|
minValue: 0,
|
||||||
|
allowBlank: false,
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
xtype: 'numberfield',
|
||||||
|
name: 'row_spacing',
|
||||||
|
fieldLabel: 'Row spacing',
|
||||||
|
minValue: 0,
|
||||||
|
allowBlank: false,
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
});
|
49
docs/lto-barcode/paper-size.js
Normal file
49
docs/lto-barcode/paper-size.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
const paper_sizes = {
|
||||||
|
a4: {
|
||||||
|
comment: 'A4 (plain)',
|
||||||
|
page_width: 210,
|
||||||
|
page_height: 297,
|
||||||
|
},
|
||||||
|
letter: {
|
||||||
|
comment: 'Letter (plain)',
|
||||||
|
page_width: 215.9,
|
||||||
|
page_height: 279.4,
|
||||||
|
},
|
||||||
|
avery3420: {
|
||||||
|
fixed: true,
|
||||||
|
comment: 'Avery Zweckform 3420',
|
||||||
|
page_width: 210,
|
||||||
|
page_height: 297,
|
||||||
|
label_width: 70,
|
||||||
|
label_height: 16.9,
|
||||||
|
margin_left: 0,
|
||||||
|
margin_top: 5,
|
||||||
|
column_spacing: 0,
|
||||||
|
row_spacing: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function paper_size_combo_data() {
|
||||||
|
let data = [];
|
||||||
|
|
||||||
|
for (let [key, value] of Object.entries(paper_sizes)) {
|
||||||
|
data.push({ value: key, text: value.comment });
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ext.define('PaperSize', {
|
||||||
|
extend: 'Ext.form.field.ComboBox',
|
||||||
|
alias: 'widget.paperSize',
|
||||||
|
|
||||||
|
editable: false,
|
||||||
|
|
||||||
|
displayField: 'text',
|
||||||
|
valueField: 'value',
|
||||||
|
queryMode: 'local',
|
||||||
|
|
||||||
|
store: {
|
||||||
|
field: ['value', 'text'],
|
||||||
|
data: paper_size_combo_data(),
|
||||||
|
},
|
||||||
|
});
|
15
docs/lto-barcode/prefix-field.js
Normal file
15
docs/lto-barcode/prefix-field.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
Ext.define('PrefixField', {
|
||||||
|
extend: 'Ext.form.field.Text',
|
||||||
|
alias: 'widget.prefixfield',
|
||||||
|
|
||||||
|
maxLength: 6,
|
||||||
|
allowBlank: false,
|
||||||
|
|
||||||
|
maskRe: /([A-Za-z]+)$/,
|
||||||
|
|
||||||
|
listeners: {
|
||||||
|
change: function(field) {
|
||||||
|
field.setValue(field.getValue().toUpperCase());
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
23
docs/lto-barcode/tape-type.js
Normal file
23
docs/lto-barcode/tape-type.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
Ext.define('LtoTapeType', {
|
||||||
|
extend: 'Ext.form.field.ComboBox',
|
||||||
|
alias: 'widget.ltoTapeType',
|
||||||
|
|
||||||
|
editable: false,
|
||||||
|
|
||||||
|
displayField: 'text',
|
||||||
|
valueField: 'value',
|
||||||
|
queryMode: 'local',
|
||||||
|
|
||||||
|
store: {
|
||||||
|
field: ['value', 'text'],
|
||||||
|
data: [
|
||||||
|
{ value: 'L8', text: "LTO-8" },
|
||||||
|
{ value: 'L7', text: "LTO-7" },
|
||||||
|
{ value: 'L6', text: "LTO-6" },
|
||||||
|
{ value: 'L5', text: "LTO-5" },
|
||||||
|
{ value: 'L4', text: "LTO-4" },
|
||||||
|
{ value: 'L3', text: "LTO-3" },
|
||||||
|
{ value: 'CU', text: "Cleaning Unit" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
@ -118,11 +118,11 @@ high, but you cannot recreate backup snapshots from the past.
|
|||||||
Garbage Collection
|
Garbage Collection
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
You can monitor and run :ref:`garbage collection <garbage-collection>` on the
|
You can monitor and run :ref:`garbage collection <client_garbage-collection>` on the
|
||||||
Proxmox Backup Server using the ``garbage-collection`` subcommand of
|
Proxmox Backup Server using the ``garbage-collection`` subcommand of
|
||||||
``proxmox-backup-manager``. You can use the ``start`` subcommand to manually
|
``proxmox-backup-manager``. You can use the ``start`` subcommand to manually
|
||||||
start garbage collection on an entire datastore and the ``status`` subcommand to
|
start garbage collection on an entire datastore and the ``status`` subcommand to
|
||||||
see attributes relating to the :ref:`garbage collection <garbage-collection>`.
|
see attributes relating to the :ref:`garbage collection <client_garbage-collection>`.
|
||||||
|
|
||||||
This functionality can also be accessed in the GUI, by navigating to **Prune &
|
This functionality can also be accessed in the GUI, by navigating to **Prune &
|
||||||
GC** from the top panel. From here, you can edit the schedule at which garbage
|
GC** from the top panel. From here, you can edit the schedule at which garbage
|
||||||
@ -142,7 +142,7 @@ Verification
|
|||||||
Proxmox Backup offers various verification options to ensure that backup data is
|
Proxmox Backup offers various verification options to ensure that backup data is
|
||||||
intact. Verification is generally carried out through the creation of verify
|
intact. Verification is generally carried out through the creation of verify
|
||||||
jobs. These are scheduled tasks that run verification at a given interval (see
|
jobs. These are scheduled tasks that run verification at a given interval (see
|
||||||
:ref:`calendar-events`). With these, you can set whether already verified
|
:ref:`calendar-event-scheduling`). With these, you can set whether already verified
|
||||||
snapshots are ignored, as well as set a time period, after which verified jobs
|
snapshots are ignored, as well as set a time period, after which verified jobs
|
||||||
are checked again. The interface for creating verify jobs can be found under the
|
are checked again. The interface for creating verify jobs can be found under the
|
||||||
**Verify Jobs** tab of the datastore.
|
**Verify Jobs** tab of the datastore.
|
||||||
|
@ -65,7 +65,7 @@ the ``proxmox-backup-manager sync-job`` command. The configuration information
|
|||||||
for sync jobs is stored at ``/etc/proxmox-backup/sync.cfg``. To create a new
|
for sync jobs is stored at ``/etc/proxmox-backup/sync.cfg``. To create a new
|
||||||
sync job, click the add button in the GUI, or use the ``create`` subcommand.
|
sync job, click the add button in the GUI, or use the ``create`` subcommand.
|
||||||
After creating a sync job, you can either start it manually from the GUI or
|
After creating a sync job, you can either start it manually from the GUI or
|
||||||
provide it with a schedule (see :ref:`calendar-events`) to run regularly.
|
provide it with a schedule (see :ref:`calendar-event-scheduling`) to run regularly.
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
|
24
docs/output-format.rst
Normal file
24
docs/output-format.rst
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
Most commands producing output supports the ``--output-format``
|
||||||
|
parameter. It accepts the following values:
|
||||||
|
|
||||||
|
:``text``: Text format (default). Structured data is rendered as a table.
|
||||||
|
|
||||||
|
:``json``: JSON (single line).
|
||||||
|
|
||||||
|
:``json-pretty``: JSON (multiple lines, nicely formatted).
|
||||||
|
|
||||||
|
|
||||||
|
Also, the following environment variables can modify output behavior:
|
||||||
|
|
||||||
|
``PROXMOX_OUTPUT_FORMAT``
|
||||||
|
Defines the default output format.
|
||||||
|
|
||||||
|
``PROXMOX_OUTPUT_NO_BORDER``
|
||||||
|
If set (to any value), do not render table borders.
|
||||||
|
|
||||||
|
``PROXMOX_OUTPUT_NO_HEADER``
|
||||||
|
If set (to any value), do not render table headers.
|
||||||
|
|
||||||
|
.. note:: The ``text`` format is designed to be human readable, and
|
||||||
|
not meant to be parsed by automation tools. Please use the ``json``
|
||||||
|
format if you need to process the output.
|
@ -69,10 +69,12 @@ Here, the output should be:
|
|||||||
|
|
||||||
f3f6c5a3a67baf38ad178e5ff1ee270c /etc/apt/trusted.gpg.d/proxmox-ve-release-6.x.gpg
|
f3f6c5a3a67baf38ad178e5ff1ee270c /etc/apt/trusted.gpg.d/proxmox-ve-release-6.x.gpg
|
||||||
|
|
||||||
|
.. _sysadmin_package_repos_enterprise:
|
||||||
|
|
||||||
`Proxmox Backup`_ Enterprise Repository
|
`Proxmox Backup`_ Enterprise Repository
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
This will be the default, stable, and recommended repository. It is available for
|
This is the stable, recommended repository. It is available for
|
||||||
all `Proxmox Backup`_ subscription users. It contains the most stable packages,
|
all `Proxmox Backup`_ subscription users. It contains the most stable packages,
|
||||||
and is suitable for production use. The ``pbs-enterprise`` repository is
|
and is suitable for production use. The ``pbs-enterprise`` repository is
|
||||||
enabled by default:
|
enabled by default:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
Copyright and Disclaimer
|
Copyright and Disclaimer
|
||||||
========================
|
========================
|
||||||
|
|
||||||
Copyright (C) 2007-2019 Proxmox Server Solutions GmbH
|
Copyright (C) 2007-2021 Proxmox Server Solutions GmbH
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License as
|
it under the terms of the GNU Affero General Public License as
|
||||||
|
2
docs/pmt/description.rst
Normal file
2
docs/pmt/description.rst
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
The ``pmt`` command controls Linux tape devices.
|
||||||
|
|
42
docs/pmt/man1.rst
Normal file
42
docs/pmt/man1.rst
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
==========================
|
||||||
|
pmt
|
||||||
|
==========================
|
||||||
|
|
||||||
|
.. include:: ../epilog.rst
|
||||||
|
|
||||||
|
-------------------------------------------------------------
|
||||||
|
Control Linux Tape Devices
|
||||||
|
-------------------------------------------------------------
|
||||||
|
|
||||||
|
:Author: |AUTHOR|
|
||||||
|
:Version: Version |VERSION|
|
||||||
|
:Manual section: 1
|
||||||
|
|
||||||
|
|
||||||
|
Synopsis
|
||||||
|
========
|
||||||
|
|
||||||
|
.. include:: synopsis.rst
|
||||||
|
|
||||||
|
|
||||||
|
Common Options
|
||||||
|
==============
|
||||||
|
|
||||||
|
.. include:: options.rst
|
||||||
|
|
||||||
|
|
||||||
|
Description
|
||||||
|
===========
|
||||||
|
|
||||||
|
.. include:: description.rst
|
||||||
|
|
||||||
|
|
||||||
|
ENVIRONMENT
|
||||||
|
===========
|
||||||
|
|
||||||
|
:TAPE: If set, replaces the `--device` option.
|
||||||
|
|
||||||
|
:PROXMOX_TAPE_DRIVE: If set, replaces the `--drive` option.
|
||||||
|
|
||||||
|
|
||||||
|
.. include:: ../pbs-copyright.rst
|
51
docs/pmt/options.rst
Normal file
51
docs/pmt/options.rst
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
All commands support the following parameters to specify the tape device:
|
||||||
|
|
||||||
|
--device <path> Path to the Linux tape device
|
||||||
|
|
||||||
|
--drive <name> Use drive from Proxmox Backup Server configuration.
|
||||||
|
|
||||||
|
|
||||||
|
Commands which generate output support the ``--output-format``
|
||||||
|
parameter. It accepts the following values:
|
||||||
|
|
||||||
|
:``text``: Text format (default). Human readable.
|
||||||
|
|
||||||
|
:``json``: JSON (single line).
|
||||||
|
|
||||||
|
:``json-pretty``: JSON (multiple lines, nicely formatted).
|
||||||
|
|
||||||
|
|
||||||
|
Device driver options can be specified as integer numbers (see
|
||||||
|
``/usr/include/linux/mtio.h``), or using symbolic names:
|
||||||
|
|
||||||
|
:``buffer-writes``: Enable buffered writes
|
||||||
|
|
||||||
|
:``async-writes``: Enable async writes
|
||||||
|
|
||||||
|
:``read-ahead``: Use read-ahead for fixed block size
|
||||||
|
|
||||||
|
:``debugging``: Enable debugging if compiled into the driver
|
||||||
|
|
||||||
|
:``two-fm``: Write two file marks when closing the file
|
||||||
|
|
||||||
|
:``fast-mteom``: Space directly to eod (and lose file number)
|
||||||
|
|
||||||
|
:``auto-lock``: Automatically lock/unlock drive door
|
||||||
|
|
||||||
|
:``def-writes``: Defaults are meant only for writes
|
||||||
|
|
||||||
|
:``can-bsr``: Indicates that the drive can space backwards
|
||||||
|
|
||||||
|
:``no-blklims``: Drive does not support read block limits
|
||||||
|
|
||||||
|
:``can-partitions``: Drive can handle partitioned tapes
|
||||||
|
|
||||||
|
:``scsi2locical``: Seek and tell use SCSI-2 logical block addresses
|
||||||
|
|
||||||
|
:``sysv``: Enable the System V semantics
|
||||||
|
|
||||||
|
:``nowait``: Do not wait for rewind, etc. to complete
|
||||||
|
|
||||||
|
:``sili``: Enables setting the SILI bit in SCSI commands when reading
|
||||||
|
in variable block mode to enhance performance when reading blocks
|
||||||
|
shorter than the byte count
|
3
docs/pmtx/description.rst
Normal file
3
docs/pmtx/description.rst
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
The ``pmtx`` command controls SCSI media changer devices (tape
|
||||||
|
autoloader).
|
||||||
|
|
57
docs/pmtx/man1.rst
Normal file
57
docs/pmtx/man1.rst
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
==========================
|
||||||
|
pmtx
|
||||||
|
==========================
|
||||||
|
|
||||||
|
.. include:: ../epilog.rst
|
||||||
|
|
||||||
|
-------------------------------------------------------------
|
||||||
|
Control SCSI media changer devices (tape autoloaders)
|
||||||
|
-------------------------------------------------------------
|
||||||
|
|
||||||
|
:Author: |AUTHOR|
|
||||||
|
:Version: Version |VERSION|
|
||||||
|
:Manual section: 1
|
||||||
|
|
||||||
|
|
||||||
|
Synopsis
|
||||||
|
==========
|
||||||
|
|
||||||
|
.. include:: synopsis.rst
|
||||||
|
|
||||||
|
|
||||||
|
Common Options
|
||||||
|
==============
|
||||||
|
|
||||||
|
All command supports the following parameters to specify the changer device:
|
||||||
|
|
||||||
|
--device <path> Path to Linux generic SCSI device (e.g. '/dev/sg4')
|
||||||
|
|
||||||
|
--changer <name> Use changer from Proxmox Backup Server configuration.
|
||||||
|
|
||||||
|
|
||||||
|
Commands generating output supports the ``--output-format``
|
||||||
|
parameter. It accepts the following values:
|
||||||
|
|
||||||
|
:``text``: Text format (default). Human readable.
|
||||||
|
|
||||||
|
:``json``: JSON (single line).
|
||||||
|
|
||||||
|
:``json-pretty``: JSON (multiple lines, nicely formatted).
|
||||||
|
|
||||||
|
|
||||||
|
Description
|
||||||
|
============
|
||||||
|
|
||||||
|
.. include:: description.rst
|
||||||
|
|
||||||
|
|
||||||
|
ENVIRONMENT
|
||||||
|
===========
|
||||||
|
|
||||||
|
:CHANGER: If set, replaces the `--device` option
|
||||||
|
|
||||||
|
:PROXMOX_TAPE_DRIVE: If set, use the Proxmox Backup Server
|
||||||
|
configuration to find the associcated changer device.
|
||||||
|
|
||||||
|
|
||||||
|
.. include:: ../pbs-copyright.rst
|
@ -1,4 +1,4 @@
|
|||||||
This is just a test.
|
This tool implements a backup server client, i.e. it can connect to a
|
||||||
|
backup servers to issue management commands and to create or restore
|
||||||
.. NOTE:: No further info.
|
backups.
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ proxmox-backup-client
|
|||||||
.. include:: ../epilog.rst
|
.. include:: ../epilog.rst
|
||||||
|
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
Command line toot for Backup and Restore
|
Command line tool for Backup and Restore
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
|
|
||||||
:Author: |AUTHOR|
|
:Author: |AUTHOR|
|
||||||
@ -31,6 +31,12 @@ Those command are available when you start an intercative restore shell:
|
|||||||
.. include:: catalog-shell-synopsis.rst
|
.. include:: catalog-shell-synopsis.rst
|
||||||
|
|
||||||
|
|
||||||
|
Common Options
|
||||||
|
==============
|
||||||
|
|
||||||
|
.. include:: ../output-format.rst
|
||||||
|
|
||||||
|
|
||||||
Description
|
Description
|
||||||
============
|
============
|
||||||
|
|
||||||
|
@ -1,4 +1,2 @@
|
|||||||
This is just a test.
|
This tool exposes the whole backup server management API on the
|
||||||
|
command line.
|
||||||
.. NOTE:: No further info.
|
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
This is just a test.
|
This daemon exposes the whole Proxmox Backup Server API on TCP port
|
||||||
|
8007 using HTTPS. It runs as user ``backup`` and has very limited
|
||||||
.. NOTE:: No further info.
|
permissions. Operation requiring more permissions are forwarded to
|
||||||
|
the local ``proxmox-backup`` service.
|
||||||
|
|
||||||
|
7
docs/proxmox-backup/description.rst
Normal file
7
docs/proxmox-backup/description.rst
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
This daemon exposes the Proxmox Backup Server management API on
|
||||||
|
``127.0.0.1:82``. It runs as ``root`` and has permission to do all
|
||||||
|
privileged operations.
|
||||||
|
|
||||||
|
NOTE: The daemon listens to a local address only, so you cannot access
|
||||||
|
it from outside. The ``proxmox-backup-proxy`` daemon exposes the API
|
||||||
|
to the outside world.
|
41
docs/proxmox-backup/man1.rst
Normal file
41
docs/proxmox-backup/man1.rst
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
==========================
|
||||||
|
proxmox-backup
|
||||||
|
==========================
|
||||||
|
|
||||||
|
.. include:: ../epilog.rst
|
||||||
|
|
||||||
|
-------------------------------------------------------------
|
||||||
|
Proxmox Backup Local API Server
|
||||||
|
-------------------------------------------------------------
|
||||||
|
|
||||||
|
:Author: |AUTHOR|
|
||||||
|
:Version: Version |VERSION|
|
||||||
|
:Manual section: 1
|
||||||
|
|
||||||
|
|
||||||
|
Synopsis
|
||||||
|
==========
|
||||||
|
|
||||||
|
This daemon is normally started and managed as ``systemd`` service::
|
||||||
|
|
||||||
|
systemctl start proxmox-backup
|
||||||
|
|
||||||
|
systemctl stop proxmox-backup
|
||||||
|
|
||||||
|
systemctl status proxmox-backup
|
||||||
|
|
||||||
|
For debugging, you can start the daemon in foreground using::
|
||||||
|
|
||||||
|
proxmox-backup-api
|
||||||
|
|
||||||
|
.. NOTE:: You need to stop the service before starting the daemon in
|
||||||
|
foreground.
|
||||||
|
|
||||||
|
|
||||||
|
Description
|
||||||
|
============
|
||||||
|
|
||||||
|
.. include:: description.rst
|
||||||
|
|
||||||
|
|
||||||
|
.. include:: ../pbs-copyright.rst
|
1
docs/proxmox-tape/description.rst
Normal file
1
docs/proxmox-tape/description.rst
Normal file
@ -0,0 +1 @@
|
|||||||
|
This tool can configure and manage tape backups.
|
28
docs/proxmox-tape/man1.rst
Normal file
28
docs/proxmox-tape/man1.rst
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
==========================
|
||||||
|
proxmox-tape
|
||||||
|
==========================
|
||||||
|
|
||||||
|
.. include:: ../epilog.rst
|
||||||
|
|
||||||
|
-------------------------------------------------------------
|
||||||
|
Proxmox Tape Backup Command Line Tool
|
||||||
|
-------------------------------------------------------------
|
||||||
|
|
||||||
|
:Author: |AUTHOR|
|
||||||
|
:Version: Version |VERSION|
|
||||||
|
:Manual section: 1
|
||||||
|
|
||||||
|
|
||||||
|
Synopsis
|
||||||
|
========
|
||||||
|
|
||||||
|
.. include:: synopsis.rst
|
||||||
|
|
||||||
|
Description
|
||||||
|
===========
|
||||||
|
|
||||||
|
.. include:: description.rst
|
||||||
|
|
||||||
|
|
||||||
|
.. include:: ../pbs-copyright.rst
|
||||||
|
|
@ -449,11 +449,8 @@ Ext.onReady(function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ordering here and iterating backwards through days
|
// sort recent times first, backups array below is ordered now -> past
|
||||||
// ensures that everything is ordered
|
timesOnSingleDay.sort((a, b) => b - a);
|
||||||
timesOnSingleDay.sort(function(a, b) {
|
|
||||||
return a < b;
|
|
||||||
});
|
|
||||||
|
|
||||||
let backups = [];
|
let backups = [];
|
||||||
|
|
||||||
@ -485,16 +482,17 @@ Ext.onReady(function() {
|
|||||||
|
|
||||||
backups.forEach(function(backup) {
|
backups.forEach(function(backup) {
|
||||||
let mark = backup.mark;
|
let mark = backup.mark;
|
||||||
|
if (mark && mark === 'keep') {
|
||||||
let id = idFunc(backup);
|
let id = idFunc(backup);
|
||||||
|
|
||||||
if (finished || alreadyIncluded[id]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mark) {
|
|
||||||
if (mark === 'keep') {
|
|
||||||
alreadyIncluded[id] = true;
|
alreadyIncluded[id] = true;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
backups.forEach(function(backup) {
|
||||||
|
let mark = backup.mark;
|
||||||
|
let id = idFunc(backup);
|
||||||
|
|
||||||
|
if (finished || alreadyIncluded[id] || mark) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
Description
|
|
||||||
^^^^^^^^^^^
|
|
||||||
|
|
||||||
``pxar`` is a command line utility to create and manipulate archives in the
|
``pxar`` is a command line utility to create and manipulate archives in the
|
||||||
:ref:`pxar-format`.
|
:ref:`pxar-format`.
|
||||||
It is inspired by `casync file archive format
|
It is inspired by `casync file archive format
|
||||||
@ -80,7 +77,7 @@ These files must contain one pattern per line, again later patterns win over
|
|||||||
previous ones.
|
previous ones.
|
||||||
The patterns control file exclusions of files present within the given directory
|
The patterns control file exclusions of files present within the given directory
|
||||||
or further below it in the tree.
|
or further below it in the tree.
|
||||||
The behavior is the same as described in :ref:`creating-backups`.
|
The behavior is the same as described in :ref:`client_creating_backups`.
|
||||||
|
|
||||||
Extracting an Archive
|
Extracting an Archive
|
||||||
^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -4,6 +4,9 @@ pxar
|
|||||||
|
|
||||||
.. include:: ../epilog.rst
|
.. include:: ../epilog.rst
|
||||||
|
|
||||||
|
.. Avoid errors with sphinx ref role
|
||||||
|
.. role:: ref(emphasis)
|
||||||
|
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
Proxmox File Archive Command Line Tool
|
Proxmox File Archive Command Line Tool
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
@ -25,4 +28,3 @@ Description
|
|||||||
|
|
||||||
|
|
||||||
.. include:: ../pbs-copyright.rst
|
.. include:: ../pbs-copyright.rst
|
||||||
|
|
||||||
|
@ -6,3 +6,9 @@ Service Daemons
|
|||||||
|
|
||||||
.. include:: proxmox-backup-proxy/description.rst
|
.. include:: proxmox-backup-proxy/description.rst
|
||||||
|
|
||||||
|
|
||||||
|
``proxmox-backup``
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. include:: proxmox-backup/description.rst
|
||||||
|
|
||||||
|
@ -119,8 +119,8 @@ directory on the filesystem. Each datastore also has associated retention
|
|||||||
settings of how many backup snapshots for each interval of ``hourly``,
|
settings of how many backup snapshots for each interval of ``hourly``,
|
||||||
``daily``, ``weekly``, ``monthly``, ``yearly`` as well as a time-independent
|
``daily``, ``weekly``, ``monthly``, ``yearly`` as well as a time-independent
|
||||||
number of backups to keep in that store. :ref:`backup-pruning` and
|
number of backups to keep in that store. :ref:`backup-pruning` and
|
||||||
:ref:`garbage collection <garbage-collection>` can also be configured to run
|
:ref:`garbage collection <client_garbage-collection>` can also be configured to run
|
||||||
periodically based on a configured schedule (see :ref:`calendar-events`) per datastore.
|
periodically based on a configured schedule (see :ref:`calendar-event-scheduling`) per datastore.
|
||||||
|
|
||||||
|
|
||||||
.. _storage_datastore_create:
|
.. _storage_datastore_create:
|
||||||
|
@ -25,4 +25,7 @@ either explain things which are different on `Proxmox Backup`_, or
|
|||||||
tasks which are commonly used on `Proxmox Backup`_. For other topics,
|
tasks which are commonly used on `Proxmox Backup`_. For other topics,
|
||||||
please refer to the standard Debian documentation.
|
please refer to the standard Debian documentation.
|
||||||
|
|
||||||
|
|
||||||
.. include:: local-zfs.rst
|
.. include:: local-zfs.rst
|
||||||
|
|
||||||
|
.. include:: services.rst
|
||||||
|
960
docs/tape-backup.rst
Normal file
960
docs/tape-backup.rst
Normal file
@ -0,0 +1,960 @@
|
|||||||
|
.. _tape_backup:
|
||||||
|
|
||||||
|
Tape Backup
|
||||||
|
===========
|
||||||
|
|
||||||
|
.. CAUTION:: Tape Backup is a technical preview feature, not meant for
|
||||||
|
production use. To enable it in the GUI, you need to issue the
|
||||||
|
following command (as root user on the console):
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# touch /etc/proxmox-backup/tape.cfg
|
||||||
|
|
||||||
|
Proxmox tape backup provides an easy way to store datastore content
|
||||||
|
onto magnetic tapes. This increases data safety because you get:
|
||||||
|
|
||||||
|
- an additional copy of the data,
|
||||||
|
- on a different media type (tape),
|
||||||
|
- to an additional location (you can move tapes off-site)
|
||||||
|
|
||||||
|
In most restore jobs, only data from the last backup job is restored.
|
||||||
|
Restore requests further decline, the older the data
|
||||||
|
gets. Considering this, tape backup may also help to reduce disk
|
||||||
|
usage, because you can safely remove data from disk, once it's archived on
|
||||||
|
tape. This is especially true if you need to retain data for several
|
||||||
|
years.
|
||||||
|
|
||||||
|
Tape backups do not provide random access to the stored data. Instead,
|
||||||
|
you need to restore the data to disk, before you can access it
|
||||||
|
again. Also, if you store your tapes off-site (using some kind of tape
|
||||||
|
vaulting service), you need to bring them back on-site, before you can do any
|
||||||
|
restores. So please consider that restoring from tape can take much
|
||||||
|
longer than restoring from disk.
|
||||||
|
|
||||||
|
|
||||||
|
Tape Technology Primer
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
.. _Linear Tape-Open: https://en.wikipedia.org/wiki/Linear_Tape-Open
|
||||||
|
|
||||||
|
As of 2021, the only widely available tape technology standard is
|
||||||
|
`Linear Tape-Open`_ (LTO). Different vendors offer LTO Ultrium tape
|
||||||
|
drives, auto-loaders, and LTO tape cartridges.
|
||||||
|
|
||||||
|
There are a few vendors that offer proprietary drives with
|
||||||
|
slight advantages in performance and capacity. Nevertheless, they have
|
||||||
|
significant disadvantages:
|
||||||
|
|
||||||
|
- proprietary (single vendor)
|
||||||
|
- a much higher purchase cost
|
||||||
|
|
||||||
|
So we currently do not test such drives.
|
||||||
|
|
||||||
|
In general, LTO tapes offer the following advantages:
|
||||||
|
|
||||||
|
- Durability (30 year lifespan)
|
||||||
|
- High Capacity (12 TB)
|
||||||
|
- Relatively low cost per TB
|
||||||
|
- Cold Media
|
||||||
|
- Movable (storable inside vault)
|
||||||
|
- Multiple vendors (for both media and drives)
|
||||||
|
- Build in AES-GCM Encryption engine
|
||||||
|
|
||||||
|
Note that `Proxmox Backup Server` already stores compressed data, so using the
|
||||||
|
tape compression feature has no advantage.
|
||||||
|
|
||||||
|
|
||||||
|
Supported Hardware
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Proxmox Backup Server supports `Linear Tape-Open`_ generation 4 (LTO-4)
|
||||||
|
or later. In general, all SCSI-2 tape drives supported by the Linux
|
||||||
|
kernel should work, but features like hardware encryption need LTO-4
|
||||||
|
or later.
|
||||||
|
|
||||||
|
Tape changing is carried out using the Linux 'mtx' command line
|
||||||
|
tool, so any changer device supported by this tool should work.
|
||||||
|
|
||||||
|
|
||||||
|
Drive Performance
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Current LTO-8 tapes provide read/write speeds of up to 360 MB/s. This means,
|
||||||
|
that it still takes a minimum of 9 hours to completely write or
|
||||||
|
read a single tape (even at maximum speed).
|
||||||
|
|
||||||
|
The only way to speed up that data rate is to use more than one
|
||||||
|
drive. That way, you can run several backup jobs in parallel, or run
|
||||||
|
restore jobs while the other dives are used for backups.
|
||||||
|
|
||||||
|
Also consider that you first need to read data from your datastore
|
||||||
|
(disk). However, a single spinning disk is unable to deliver data at this
|
||||||
|
rate. We measured a maximum rate of about 60MB/s to 100MB/s in practice,
|
||||||
|
so it takes 33 hours to read the 12TB needed to fill up an LTO-8 tape. If you want
|
||||||
|
to write to your tape at full speed, please make sure that the source
|
||||||
|
datastore is able to deliver that performance (e.g, by using SSDs).
|
||||||
|
|
||||||
|
|
||||||
|
Terminology
|
||||||
|
-----------
|
||||||
|
|
||||||
|
:Tape Labels: are used to uniquely identify a tape. You would normally apply a
|
||||||
|
sticky paper label to the front of the cartridge. We additionally store the
|
||||||
|
label text magnetically on the tape (first file on tape).
|
||||||
|
|
||||||
|
.. _Code 39: https://en.wikipedia.org/wiki/Code_39
|
||||||
|
|
||||||
|
.. _LTO Ultrium Cartridge Label Specification: https://www.ibm.com/support/pages/ibm-lto-ultrium-cartridge-label-specification
|
||||||
|
|
||||||
|
.. _LTO Barcode Generator: lto-barcode/index.html
|
||||||
|
|
||||||
|
:Barcodes: are a special form of tape labels, which are electronically
|
||||||
|
readable. Most LTO tape robots use an 8 character string encoded as
|
||||||
|
`Code 39`_, as defined in the `LTO Ultrium Cartridge Label
|
||||||
|
Specification`_.
|
||||||
|
|
||||||
|
You can either buy such barcode labels from your cartridge vendor,
|
||||||
|
or print them yourself. You can use our `LTO Barcode Generator`_
|
||||||
|
app, if you would like to print them yourself.
|
||||||
|
|
||||||
|
.. Note:: Physical labels and the associated adhesive should have an
|
||||||
|
environmental performance to match or exceed the environmental
|
||||||
|
specifications of the cartridge to which it is applied.
|
||||||
|
|
||||||
|
:Media Pools: A media pool is a logical container for tapes. A backup
|
||||||
|
job targets one media pool, so a job only uses tapes from that
|
||||||
|
pool. The pool additionally defines how long a backup job can
|
||||||
|
append data to tapes (allocation policy) and how long you want to
|
||||||
|
keep the data (retention policy).
|
||||||
|
|
||||||
|
:Media Set: A group of continuously written tapes (all from the same
|
||||||
|
media pool).
|
||||||
|
|
||||||
|
:Tape drive: The device used to read and write data to the tape. There
|
||||||
|
are standalone drives, but drives are usually shipped within tape libraries.
|
||||||
|
|
||||||
|
:Tape changer: A device which can change the tapes inside a tape drive
|
||||||
|
(tape robot). They are usually part of a tape library.
|
||||||
|
|
||||||
|
.. _Tape Library: https://en.wikipedia.org/wiki/Tape_library
|
||||||
|
|
||||||
|
:`Tape library`_: A storage device that contains one or more tape drives,
|
||||||
|
a number of slots to hold tape cartridges, a barcode reader to
|
||||||
|
identify tape cartridges, and an automated method for loading tapes
|
||||||
|
(a robot).
|
||||||
|
|
||||||
|
This is also commonly known as an 'autoloader', 'tape robot' or 'tape jukebox'.
|
||||||
|
|
||||||
|
:Inventory: The inventory stores the list of known tapes (with
|
||||||
|
additional status information).
|
||||||
|
|
||||||
|
:Catalog: A media catalog stores information about the media content.
|
||||||
|
|
||||||
|
|
||||||
|
Tape Quick Start
|
||||||
|
---------------
|
||||||
|
|
||||||
|
1. Configure your tape hardware (drives and changers)
|
||||||
|
|
||||||
|
2. Configure one or more media pools
|
||||||
|
|
||||||
|
3. Label your tape cartridges
|
||||||
|
|
||||||
|
4. Start your first tape backup job ...
|
||||||
|
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Please note that you can configure anything using the graphical user
|
||||||
|
interface or the command line interface. Both methods result in the
|
||||||
|
same configuration.
|
||||||
|
|
||||||
|
.. _tape_changer_config:
|
||||||
|
|
||||||
|
Tape changers
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Tape changers (robots) are part of a `Tape Library`_. You can skip
|
||||||
|
this step if you are using a standalone drive.
|
||||||
|
|
||||||
|
Linux is able to auto detect these devices, and you can get a list
|
||||||
|
of available devices using:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape changer scan
|
||||||
|
┌─────────────────────────────┬─────────┬──────────────┬────────┐
|
||||||
|
│ path │ vendor │ model │ serial │
|
||||||
|
╞═════════════════════════════╪═════════╪══════════════╪════════╡
|
||||||
|
│ /dev/tape/by-id/scsi-CC2C52 │ Quantum │ Superloader3 │ CC2C52 │
|
||||||
|
└─────────────────────────────┴─────────┴──────────────┴────────┘
|
||||||
|
|
||||||
|
In order to use a device with Proxmox Backup Server, you need to create a
|
||||||
|
configuration entry:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape changer create sl3 --path /dev/tape/by-id/scsi-CC2C52
|
||||||
|
|
||||||
|
Where ``sl3`` is an arbitrary name you can choose.
|
||||||
|
|
||||||
|
.. Note:: Please use the persistent device path names from inside
|
||||||
|
``/dev/tape/by-id/``. Names like ``/dev/sg0`` may point to a
|
||||||
|
different device after reboot, and that is not what you want.
|
||||||
|
|
||||||
|
You can display the final configuration with:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape changer config sl3
|
||||||
|
┌──────┬─────────────────────────────┐
|
||||||
|
│ Name │ Value │
|
||||||
|
╞══════╪═════════════════════════════╡
|
||||||
|
│ name │ sl3 │
|
||||||
|
├──────┼─────────────────────────────┤
|
||||||
|
│ path │ /dev/tape/by-id/scsi-CC2C52 │
|
||||||
|
└──────┴─────────────────────────────┘
|
||||||
|
|
||||||
|
Or simply list all configured changer devices:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape changer list
|
||||||
|
┌──────┬─────────────────────────────┬─────────┬──────────────┬────────────┐
|
||||||
|
│ name │ path │ vendor │ model │ serial │
|
||||||
|
╞══════╪═════════════════════════════╪═════════╪══════════════╪════════════╡
|
||||||
|
│ sl3 │ /dev/tape/by-id/scsi-CC2C52 │ Quantum │ Superloader3 │ CC2C52 │
|
||||||
|
└──────┴─────────────────────────────┴─────────┴──────────────┴────────────┘
|
||||||
|
|
||||||
|
The Vendor, Model and Serial number are auto detected, but only shown
|
||||||
|
if the device is online.
|
||||||
|
|
||||||
|
To test your setup, please query the status of the changer device with:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape changer status sl3
|
||||||
|
┌───────────────┬──────────┬────────────┬─────────────┐
|
||||||
|
│ entry-kind │ entry-id │ changer-id │ loaded-slot │
|
||||||
|
╞═══════════════╪══════════╪════════════╪═════════════╡
|
||||||
|
│ drive │ 0 │ vtape1 │ 1 │
|
||||||
|
├───────────────┼──────────┼────────────┼─────────────┤
|
||||||
|
│ slot │ 1 │ │ │
|
||||||
|
├───────────────┼──────────┼────────────┼─────────────┤
|
||||||
|
│ slot │ 2 │ vtape2 │ │
|
||||||
|
├───────────────┼──────────┼────────────┼─────────────┤
|
||||||
|
│ ... │ ... │ │ │
|
||||||
|
├───────────────┼──────────┼────────────┼─────────────┤
|
||||||
|
│ slot │ 16 │ │ │
|
||||||
|
└───────────────┴──────────┴────────────┴─────────────┘
|
||||||
|
|
||||||
|
Tape libraries usually provide some special import/export slots (also
|
||||||
|
called "mail slots"). Tapes inside those slots are accessible from
|
||||||
|
outside, making it easy to add/remove tapes to/from the library. Those
|
||||||
|
tapes are considered to be "offline", so backup jobs will not use
|
||||||
|
them. Those special slots are auto-detected and marked as an
|
||||||
|
``import-export`` slot in the status command.
|
||||||
|
|
||||||
|
It's worth noting that some of the smaller tape libraries don't have
|
||||||
|
such slots. While they have something called a "Mail Slot", that slot
|
||||||
|
is just a way to grab the tape from the gripper. They are unable
|
||||||
|
to hold media while the robot does other things. They also do not
|
||||||
|
expose that "Mail Slot" over the SCSI interface, so you wont see them in
|
||||||
|
the status output.
|
||||||
|
|
||||||
|
As a workaround, you can mark some of the normal slots as export
|
||||||
|
slot. The software treats those slots like real ``import-export``
|
||||||
|
slots, and the media inside those slots is considered to be 'offline'
|
||||||
|
(not available for backup):
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape changer update sl3 --export-slots 15,16
|
||||||
|
|
||||||
|
After that, you can see those artificial ``import-export`` slots in
|
||||||
|
the status output:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape changer status sl3
|
||||||
|
┌───────────────┬──────────┬────────────┬─────────────┐
|
||||||
|
│ entry-kind │ entry-id │ changer-id │ loaded-slot │
|
||||||
|
╞═══════════════╪══════════╪════════════╪═════════════╡
|
||||||
|
│ drive │ 0 │ vtape1 │ 1 │
|
||||||
|
├───────────────┼──────────┼────────────┼─────────────┤
|
||||||
|
│ import-export │ 15 │ │ │
|
||||||
|
├───────────────┼──────────┼────────────┼─────────────┤
|
||||||
|
│ import-export │ 16 │ │ │
|
||||||
|
├───────────────┼──────────┼────────────┼─────────────┤
|
||||||
|
│ slot │ 1 │ │ │
|
||||||
|
├───────────────┼──────────┼────────────┼─────────────┤
|
||||||
|
│ slot │ 2 │ vtape2 │ │
|
||||||
|
├───────────────┼──────────┼────────────┼─────────────┤
|
||||||
|
│ ... │ ... │ │ │
|
||||||
|
├───────────────┼──────────┼────────────┼─────────────┤
|
||||||
|
│ slot │ 14 │ │ │
|
||||||
|
└───────────────┴──────────┴────────────┴─────────────┘
|
||||||
|
|
||||||
|
.. _tape_drive_config:
|
||||||
|
|
||||||
|
Tape drives
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
Linux is able to auto detect tape drives, and you can get a list
|
||||||
|
of available tape drives using:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape drive scan
|
||||||
|
┌────────────────────────────────┬────────┬─────────────┬────────┐
|
||||||
|
│ path │ vendor │ model │ serial │
|
||||||
|
╞════════════════════════════════╪════════╪═════════════╪════════╡
|
||||||
|
│ /dev/tape/by-id/scsi-12345-nst │ IBM │ ULT3580-TD4 │ 12345 │
|
||||||
|
└────────────────────────────────┴────────┴─────────────┴────────┘
|
||||||
|
|
||||||
|
In order to use that drive with Proxmox, you need to create a
|
||||||
|
configuration entry:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape drive create mydrive --path /dev/tape/by-id/scsi-12345-nst
|
||||||
|
|
||||||
|
.. Note:: Please use the persistent device path names from inside
|
||||||
|
``/dev/tape/by-id/``. Names like ``/dev/nst0`` may point to a
|
||||||
|
different device after reboot, and that is not what you want.
|
||||||
|
|
||||||
|
If you have a tape library, you also need to set the associated
|
||||||
|
changer device:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape drive update mydrive --changer sl3 --changer-drivenum 0
|
||||||
|
|
||||||
|
The ``--changer-drivenum`` is only necessary if the tape library
|
||||||
|
includes more than one drive (the changer status command lists all
|
||||||
|
drive numbers).
|
||||||
|
|
||||||
|
You can display the final configuration with:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape drive config mydrive
|
||||||
|
┌─────────┬────────────────────────────────┐
|
||||||
|
│ Name │ Value │
|
||||||
|
╞═════════╪════════════════════════════════╡
|
||||||
|
│ name │ mydrive │
|
||||||
|
├─────────┼────────────────────────────────┤
|
||||||
|
│ path │ /dev/tape/by-id/scsi-12345-nst │
|
||||||
|
├─────────┼────────────────────────────────┤
|
||||||
|
│ changer │ sl3 │
|
||||||
|
└─────────┴────────────────────────────────┘
|
||||||
|
|
||||||
|
.. NOTE:: The ``changer-drivenum`` value 0 is not stored in the
|
||||||
|
configuration, because it is the default.
|
||||||
|
|
||||||
|
To list all configured drives use:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape drive list
|
||||||
|
┌──────────┬────────────────────────────────┬─────────┬────────┬─────────────┬────────┐
|
||||||
|
│ name │ path │ changer │ vendor │ model │ serial │
|
||||||
|
╞══════════╪════════════════════════════════╪═════════╪════════╪═════════════╪════════╡
|
||||||
|
│ mydrive │ /dev/tape/by-id/scsi-12345-nst │ sl3 │ IBM │ ULT3580-TD4 │ 12345 │
|
||||||
|
└──────────┴────────────────────────────────┴─────────┴────────┴─────────────┴────────┘
|
||||||
|
|
||||||
|
The Vendor, Model and Serial number are auto detected, but only shown
|
||||||
|
if the device is online.
|
||||||
|
|
||||||
|
For testing, you can simply query the drive status with:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape status --drive mydrive
|
||||||
|
┌───────────┬────────────────────────┐
|
||||||
|
│ Name │ Value │
|
||||||
|
╞═══════════╪════════════════════════╡
|
||||||
|
│ blocksize │ 0 │
|
||||||
|
├───────────┼────────────────────────┤
|
||||||
|
│ status │ DRIVE_OPEN | IM_REP_EN │
|
||||||
|
└───────────┴────────────────────────┘
|
||||||
|
|
||||||
|
.. NOTE:: Blocksize should always be 0 (variable block size
|
||||||
|
mode). This is the default anyway.
|
||||||
|
|
||||||
|
|
||||||
|
.. _tape_media_pool_config:
|
||||||
|
|
||||||
|
Media Pools
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
A media pool is a logical container for tapes. A backup job targets
|
||||||
|
one media pool, so a job only uses tapes from that pool.
|
||||||
|
|
||||||
|
.. topic:: Media Set
|
||||||
|
|
||||||
|
A media set is a group of continuously written tapes, used to split
|
||||||
|
the larger pool into smaller, restorable units. One or more backup
|
||||||
|
jobs write to a media set, producing an ordered group of
|
||||||
|
tapes. Media sets are identified by a unique ID. That ID and the
|
||||||
|
sequence number are stored on each tape of that set (tape label).
|
||||||
|
|
||||||
|
Media sets are the basic unit for restore tasks. This means that you need
|
||||||
|
every tape in the set to restore the media set contents. Data is fully
|
||||||
|
deduplicated inside a media set.
|
||||||
|
|
||||||
|
|
||||||
|
.. topic:: Media Set Allocation Policy
|
||||||
|
|
||||||
|
The pool additionally defines how long backup jobs can append data
|
||||||
|
to a media set. The following settings are possible:
|
||||||
|
|
||||||
|
- Try to use the current media set.
|
||||||
|
|
||||||
|
This setting produces one large media set. While this is very
|
||||||
|
space efficient (deduplication, no unused space), it can lead to
|
||||||
|
long restore times, because restore jobs need to read all tapes in the
|
||||||
|
set.
|
||||||
|
|
||||||
|
.. NOTE:: Data is fully deduplicated inside a media set. This
|
||||||
|
also means that data is randomly distributed over the tapes in
|
||||||
|
the set. Thus, even if you restore a single VM, data may have to be
|
||||||
|
read from all tapes inside the media set.
|
||||||
|
|
||||||
|
Larger media sets are also more error-prone, because a single
|
||||||
|
damaged tape makes the restore fail.
|
||||||
|
|
||||||
|
Usage scenario: Mostly used with tape libraries. You manually
|
||||||
|
trigger new set creation by running a backup job with the
|
||||||
|
``--export`` option.
|
||||||
|
|
||||||
|
.. NOTE:: Retention period starts with the existence of a newer
|
||||||
|
media set.
|
||||||
|
|
||||||
|
- Always create a new media set.
|
||||||
|
|
||||||
|
With this setting, each backup job creates a new media set. This
|
||||||
|
is less space efficient, because the media from the last set
|
||||||
|
may not be fully written, leaving the remaining space unused.
|
||||||
|
|
||||||
|
The advantage is that this procudes media sets of minimal
|
||||||
|
size. Small sets are easier to handle, can be moved more conveniently
|
||||||
|
to an off-site vault, and can be restored much faster.
|
||||||
|
|
||||||
|
.. NOTE:: Retention period starts with the creation time of the
|
||||||
|
media set.
|
||||||
|
|
||||||
|
- Create a new set when the specified Calendar Event triggers.
|
||||||
|
|
||||||
|
.. _systemd.time manpage: https://manpages.debian.org/buster/systemd/systemd.time.7.en.html
|
||||||
|
|
||||||
|
This allows you to specify points in time by using systemd like
|
||||||
|
Calendar Event specifications (see `systemd.time manpage`_).
|
||||||
|
|
||||||
|
For example, the value ``weekly`` (or ``Mon *-*-* 00:00:00``)
|
||||||
|
will create a new set each week.
|
||||||
|
|
||||||
|
This balances between space efficiency and media count.
|
||||||
|
|
||||||
|
.. NOTE:: Retention period starts when the calendar event
|
||||||
|
triggers.
|
||||||
|
|
||||||
|
Additionally, the following events may allocate a new media set:
|
||||||
|
|
||||||
|
- Required tape is offline (and you use a tape library).
|
||||||
|
|
||||||
|
- Current set contains damaged or retired tapes.
|
||||||
|
|
||||||
|
- Media pool encryption has changed
|
||||||
|
|
||||||
|
- Database consistency errors, for example, if the inventory does not
|
||||||
|
contain the required media information, or it contains conflicting
|
||||||
|
information (outdated data).
|
||||||
|
|
||||||
|
.. topic:: Retention Policy
|
||||||
|
|
||||||
|
Defines how long we want to keep the data.
|
||||||
|
|
||||||
|
- Always overwrite media.
|
||||||
|
|
||||||
|
- Protect data for the duration specified.
|
||||||
|
|
||||||
|
We use systemd like time spans to specify durations, e.g. ``2
|
||||||
|
weeks`` (see `systemd.time manpage`_).
|
||||||
|
|
||||||
|
- Never overwrite data.
|
||||||
|
|
||||||
|
.. topic:: Hardware Encryption
|
||||||
|
|
||||||
|
LTO-4 (or later) tape drives support hardware encryption. If you
|
||||||
|
configure the media pool to use encryption, all data written to the
|
||||||
|
tapes is encrypted using the configured key.
|
||||||
|
|
||||||
|
This way, unauthorized users cannot read data from the media,
|
||||||
|
for example, if you loose a tape while shipping to an offsite location.
|
||||||
|
|
||||||
|
.. Note:: If the backup client also encrypts data, data on the tape
|
||||||
|
will be double encrypted.
|
||||||
|
|
||||||
|
The password protected key is stored on each medium, so that it is
|
||||||
|
possbible to `restore the key <tape_restore_encryption_key_>`_ using
|
||||||
|
the password. Please make sure to remember the password, in case
|
||||||
|
you need to restore the key.
|
||||||
|
|
||||||
|
|
||||||
|
.. NOTE:: We use global content namespace, meaning we do not store the
|
||||||
|
source datastore name. Because of this, it is impossible to distinguish
|
||||||
|
store1:/vm/100 from store2:/vm/100. Please use different media pools
|
||||||
|
if the sources are from different namespaces with conflicting names
|
||||||
|
(for example, if the sources are from different Proxmox VE clusters).
|
||||||
|
|
||||||
|
|
||||||
|
The following command creates a new media pool:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
// proxmox-tape pool create <name> --drive <string> [OPTIONS]
|
||||||
|
|
||||||
|
# proxmox-tape pool create daily --drive mydrive
|
||||||
|
|
||||||
|
|
||||||
|
Additional option can be set later, using the update command:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape pool update daily --allocation daily --retention 7days
|
||||||
|
|
||||||
|
|
||||||
|
To list all configured pools use:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape pool list
|
||||||
|
┌───────┬──────────┬────────────┬───────────┬──────────┐
|
||||||
|
│ name │ drive │ allocation │ retention │ template │
|
||||||
|
╞═══════╪══════════╪════════════╪═══════════╪══════════╡
|
||||||
|
│ daily │ mydrive │ daily │ 7days │ │
|
||||||
|
└───────┴──────────┴────────────┴───────────┴──────────┘
|
||||||
|
|
||||||
|
.. _tape_backup_job_config:
|
||||||
|
|
||||||
|
Tape Backup Jobs
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
To automate tape backup, you can configure tape backup jobs which
|
||||||
|
write datastore content to a media pool, based on a specific time schedule.
|
||||||
|
The required settings are:
|
||||||
|
|
||||||
|
- ``store``: The datastore you want to backup
|
||||||
|
|
||||||
|
- ``pool``: The media pool - only tape cartridges from that pool are
|
||||||
|
used.
|
||||||
|
|
||||||
|
- ``drive``: The tape drive.
|
||||||
|
|
||||||
|
- ``schedule``: Job schedule (see :ref:`calendar-event-scheduling`)
|
||||||
|
|
||||||
|
For example, to configure a tape backup job for datastore ``vmstore1``
|
||||||
|
use:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape backup-job create job2 --store vmstore1 \
|
||||||
|
--pool yourpool --drive yourdrive --schedule daily
|
||||||
|
|
||||||
|
The backup includes all snapshots from a backup group by default. You can
|
||||||
|
set the ``latest-only`` flag to include only the latest snapshots:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape backup-job update job2 --latest-only
|
||||||
|
|
||||||
|
Backup jobs can use email to send tape request notifications or
|
||||||
|
report errors. You can set the notification user with:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape backup-job update job2 --notify-user root@pam
|
||||||
|
|
||||||
|
.. Note:: The email address is a property of the user (see :ref:`user_mgmt`).
|
||||||
|
|
||||||
|
It is sometimes useful to eject the tape from the drive after a
|
||||||
|
backup. For a standalone drive, the ``eject-media`` option ejects the
|
||||||
|
tape, making sure that the following backup cannot use the tape
|
||||||
|
(unless someone manually loads the tape again). For tape libraries,
|
||||||
|
this option unloads the tape to a free slot, which provides better
|
||||||
|
dust protection than inside a drive:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape backup-job update job2 --eject-media
|
||||||
|
|
||||||
|
.. Note:: For failed jobs, the tape remains in the drive.
|
||||||
|
|
||||||
|
For tape libraries, the ``export-media`` option moves all tapes from
|
||||||
|
the media set to an export slot, making sure that the following backup
|
||||||
|
cannot use the tapes. An operator can pick up those tapes and move them
|
||||||
|
to a vault.
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape backup-job update job2 --export-media
|
||||||
|
|
||||||
|
.. Note:: The ``export-media`` option can be used to force the start
|
||||||
|
of a new media set, because tapes from the current set are no
|
||||||
|
longer online.
|
||||||
|
|
||||||
|
It is also possible to run backup jobs manually:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape backup-job run job2
|
||||||
|
|
||||||
|
To remove a job, please use:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape backup-job remove job2
|
||||||
|
|
||||||
|
|
||||||
|
Administration
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Many sub-commands of the ``proxmox-tape`` command line tools take a
|
||||||
|
parameter called ``--drive``, which specifies the tape drive you want
|
||||||
|
to work on. For convenience, you can set this in an environment
|
||||||
|
variable:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# export PROXMOX_TAPE_DRIVE=mydrive
|
||||||
|
|
||||||
|
You can then omit the ``--drive`` parameter from the command. If the
|
||||||
|
drive has an associated changer device, you may also omit the changer
|
||||||
|
parameter from commands that needs a changer device, for example:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape changer status
|
||||||
|
|
||||||
|
should display the changer status of the changer device associated with
|
||||||
|
drive ``mydrive``.
|
||||||
|
|
||||||
|
|
||||||
|
Label Tapes
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
By default, tape cartridges all look the same, so you need to put a
|
||||||
|
label on them for unique identification. First, put a sticky paper
|
||||||
|
label with some human readable text on the cartridge.
|
||||||
|
|
||||||
|
If you use a `Tape Library`_, you should use an 8 character string
|
||||||
|
encoded as `Code 39`_, as defined in the `LTO Ultrium Cartridge Label
|
||||||
|
Specification`_. You can either buy such barcode labels from your
|
||||||
|
cartridge vendor, or print them yourself. You can use our `LTO Barcode
|
||||||
|
Generator`_ app to print them.
|
||||||
|
|
||||||
|
Next, you need to write that same label text to the tape, so that the
|
||||||
|
software can uniquely identify the tape too.
|
||||||
|
|
||||||
|
For a standalone drive, manually insert the new tape cartridge into the
|
||||||
|
drive and run:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape label --changer-id <label-text> [--pool <pool-name>]
|
||||||
|
|
||||||
|
You may omit the ``--pool`` argument to allow the tape to be used by any pool.
|
||||||
|
|
||||||
|
.. Note:: For safety reasons, this command fails if the tape contains
|
||||||
|
any data. If you want to overwrite it anyway, erase the tape first.
|
||||||
|
|
||||||
|
You can verify success by reading back the label:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape read-label
|
||||||
|
┌─────────────────┬──────────────────────────────────────┐
|
||||||
|
│ Name │ Value │
|
||||||
|
╞═════════════════╪══════════════════════════════════════╡
|
||||||
|
│ changer-id │ vtape1 │
|
||||||
|
├─────────────────┼──────────────────────────────────────┤
|
||||||
|
│ uuid │ 7f42c4dd-9626-4d89-9f2b-c7bc6da7d533 │
|
||||||
|
├─────────────────┼──────────────────────────────────────┤
|
||||||
|
│ ctime │ Wed Jan 6 09:07:51 2021 │
|
||||||
|
├─────────────────┼──────────────────────────────────────┤
|
||||||
|
│ pool │ daily │
|
||||||
|
├─────────────────┼──────────────────────────────────────┤
|
||||||
|
│ media-set-uuid │ 00000000-0000-0000-0000-000000000000 │
|
||||||
|
├─────────────────┼──────────────────────────────────────┤
|
||||||
|
│ media-set-ctime │ Wed Jan 6 09:07:51 2021 │
|
||||||
|
└─────────────────┴──────────────────────────────────────┘
|
||||||
|
|
||||||
|
.. NOTE:: The ``media-set-uuid`` using all zeros indicates an empty
|
||||||
|
tape (not used by any media set).
|
||||||
|
|
||||||
|
If you have a tape library, apply the sticky barcode label to the tape
|
||||||
|
cartridges first. Then load those empty tapes into the library. You
|
||||||
|
can then label all unlabeled tapes with a single command:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape barcode-label [--pool <pool-name>]
|
||||||
|
|
||||||
|
|
||||||
|
Run Tape Backups
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
To manually run a backup job use:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape backup <store> <pool> [OPTIONS]
|
||||||
|
|
||||||
|
The following options are available:
|
||||||
|
|
||||||
|
--eject-media Eject media upon job completion.
|
||||||
|
|
||||||
|
It is normally good practice to eject the tape after use. This unmounts the
|
||||||
|
tape from the drive and prevents the tape from getting dusty.
|
||||||
|
|
||||||
|
--export-media-set Export media set upon job completion.
|
||||||
|
|
||||||
|
After a successful backup job, this moves all tapes from the used
|
||||||
|
media set into import-export slots. The operator can then pick up
|
||||||
|
those tapes and move them to a media vault.
|
||||||
|
|
||||||
|
|
||||||
|
Restore from Tape
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Restore is done at media-set granularity, so you first need to find
|
||||||
|
out which media set contains the data you want to restore. This
|
||||||
|
information is stored in the media catalog. If you do not have media
|
||||||
|
catalogs, you need to restore them first. Please note that you need
|
||||||
|
the catalog to find your data, but restoring a complete media-set does
|
||||||
|
not need media catalogs.
|
||||||
|
|
||||||
|
The following command lists the media content (from catalog):
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape media content
|
||||||
|
┌────────────┬──────┬──────────────────────────┬────────┬────────────────────────────────┬──────────────────────────────────────┐
|
||||||
|
│ label-text │ pool │ media-set-name │ seq-nr │ snapshot │ media-set-uuid │
|
||||||
|
╞════════════╪══════╪══════════════════════════╪════════╪════════════════════════════════╪══════════════════════════════════════╡
|
||||||
|
│ TEST01L8 │ p2 │ Wed Jan 13 13:55:55 2021 │ 0 │ vm/201/2021-01-11T10:43:48Z │ 9da37a55-aac7-4deb-91c6-482b3b675f30 │
|
||||||
|
├────────────┼──────┼──────────────────────────┼────────┼────────────────────────────────┼──────────────────────────────────────┤
|
||||||
|
│ ... │ ... │ ... │ ... │ ... │ ... │
|
||||||
|
└────────────┴──────┴──────────────────────────┴────────┴────────────────────────────────┴──────────────────────────────────────┘
|
||||||
|
|
||||||
|
|
||||||
|
A restore job reads the data from the media set and moves it back to
|
||||||
|
data disk (datastore):
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
// proxmox-tape restore <media-set-uuid> <datastore>
|
||||||
|
|
||||||
|
# proxmox-tape restore 9da37a55-aac7-4deb-91c6-482b3b675f30 mystore
|
||||||
|
|
||||||
|
|
||||||
|
Update Inventory
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
||||||
|
Restore Catalog
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
||||||
|
Encryption Key Management
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Creating a new encryption key:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape key create --hint "tape pw 2020"
|
||||||
|
Tape Encryption Key Password: **********
|
||||||
|
Verify Password: **********
|
||||||
|
"14:f8:79:b9:f5:13:e5:dc:bf:b6:f9:88:48:51:81:dc:79:bf:a0:22:68:47:d1:73:35:2d:b6:20:e1:7f:f5:0f"
|
||||||
|
|
||||||
|
List existing encryption keys:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape key list
|
||||||
|
┌───────────────────────────────────────────────────┬───────────────┐
|
||||||
|
│ fingerprint │ hint │
|
||||||
|
╞═══════════════════════════════════════════════════╪═══════════════╡
|
||||||
|
│ 14:f8:79:b9:f5:13:e5:dc: ... :b6:20:e1:7f:f5:0f │ tape pw 2020 │
|
||||||
|
└───────────────────────────────────────────────────┴───────────────┘
|
||||||
|
|
||||||
|
To show encryption key details:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape key show 14:f8:79:b9:f5:13:e5:dc:...:b6:20:e1:7f:f5:0f
|
||||||
|
┌─────────────┬───────────────────────────────────────────────┐
|
||||||
|
│ Name │ Value │
|
||||||
|
╞═════════════╪═══════════════════════════════════════════════╡
|
||||||
|
│ kdf │ scrypt │
|
||||||
|
├─────────────┼───────────────────────────────────────────────┤
|
||||||
|
│ created │ Sat Jan 23 14:47:21 2021 │
|
||||||
|
├─────────────┼───────────────────────────────────────────────┤
|
||||||
|
│ modified │ Sat Jan 23 14:47:21 2021 │
|
||||||
|
├─────────────┼───────────────────────────────────────────────┤
|
||||||
|
│ fingerprint │ 14:f8:79:b9:f5:13:e5:dc:...:b6:20:e1:7f:f5:0f │
|
||||||
|
├─────────────┼───────────────────────────────────────────────┤
|
||||||
|
│ hint │ tape pw 2020 │
|
||||||
|
└─────────────┴───────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
The ``paperkey`` subcommand can be used to create a QR encoded
|
||||||
|
version of a tape encryption key. The following command sends the output of the
|
||||||
|
``paperkey`` command to a text file, for easy printing:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
proxmox-tape key paperkey <fingerprint> --output-format text > qrkey.txt
|
||||||
|
|
||||||
|
|
||||||
|
.. _tape_restore_encryption_key:
|
||||||
|
|
||||||
|
Restoring Encryption Keys
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
You can restore the encryption key from the tape, using the password
|
||||||
|
used to generate the key. First, load the tape you want to restore
|
||||||
|
into the drive. Then run:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape key restore
|
||||||
|
Tepe Encryption Key Password: ***********
|
||||||
|
|
||||||
|
If the password is correct, the key will get imported to the
|
||||||
|
database. Further restore jobs automatically use any available key.
|
||||||
|
|
||||||
|
|
||||||
|
Tape Cleaning
|
||||||
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
LTO tape drives require regular cleaning. This is done by loading a
|
||||||
|
cleaning cartridge into the drive, which is a manual task for
|
||||||
|
standalone drives.
|
||||||
|
|
||||||
|
For tape libraries, cleaning cartridges are identified using special
|
||||||
|
labels starting with letters "CLN". For example, our tape library has a
|
||||||
|
cleaning cartridge inside slot 3:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape changer status sl3
|
||||||
|
┌───────────────┬──────────┬────────────┬─────────────┐
|
||||||
|
│ entry-kind │ entry-id │ changer-id │ loaded-slot │
|
||||||
|
╞═══════════════╪══════════╪════════════╪═════════════╡
|
||||||
|
│ drive │ 0 │ vtape1 │ 1 │
|
||||||
|
├───────────────┼──────────┼────────────┼─────────────┤
|
||||||
|
│ slot │ 1 │ │ │
|
||||||
|
├───────────────┼──────────┼────────────┼─────────────┤
|
||||||
|
│ slot │ 2 │ vtape2 │ │
|
||||||
|
├───────────────┼──────────┼────────────┼─────────────┤
|
||||||
|
│ slot │ 3 │ CLN001CU │ │
|
||||||
|
├───────────────┼──────────┼────────────┼─────────────┤
|
||||||
|
│ ... │ ... │ │ │
|
||||||
|
└───────────────┴──────────┴────────────┴─────────────┘
|
||||||
|
|
||||||
|
To initiate a cleaning operation simply run:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
# proxmox-tape clean
|
||||||
|
|
||||||
|
This command does the following:
|
||||||
|
|
||||||
|
- find the cleaning tape (in slot 3)
|
||||||
|
|
||||||
|
- unload the current media from the drive (back to slot 1)
|
||||||
|
|
||||||
|
- load the cleaning tape into the drive
|
||||||
|
|
||||||
|
- run drive cleaning operation
|
||||||
|
|
||||||
|
- unload the cleaning tape (to slot 3)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Configuration Files
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
``media-pool.cfg``
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
File Format
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. include:: config/media-pool/format.rst
|
||||||
|
|
||||||
|
|
||||||
|
Options
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
.. include:: config/media-pool/config.rst
|
||||||
|
|
||||||
|
|
||||||
|
``tape.cfg``
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
File Format
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. include:: config/tape/format.rst
|
||||||
|
|
||||||
|
|
||||||
|
Options
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
.. include:: config/tape/config.rst
|
||||||
|
|
||||||
|
|
||||||
|
``tape-job.cfg``
|
||||||
|
~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
File Format
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. include:: config/tape-job/format.rst
|
||||||
|
|
||||||
|
|
||||||
|
Options
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
.. include:: config/tape-job/config.rst
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Command Syntax
|
||||||
|
--------------
|
||||||
|
|
||||||
|
``proxmox-tape``
|
||||||
|
----------------
|
||||||
|
|
||||||
|
.. include:: proxmox-tape/synopsis.rst
|
||||||
|
|
||||||
|
|
||||||
|
``pmt``
|
||||||
|
-------
|
||||||
|
|
||||||
|
.. include:: pmt/options.rst
|
||||||
|
|
||||||
|
....
|
||||||
|
|
||||||
|
.. include:: pmt/synopsis.rst
|
||||||
|
|
||||||
|
|
||||||
|
``pmtx``
|
||||||
|
--------
|
||||||
|
|
||||||
|
.. include:: pmtx/synopsis.rst
|
166
docs/technical-overview.rst
Normal file
166
docs/technical-overview.rst
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
.. _tech_design_overview:
|
||||||
|
|
||||||
|
Technical Overview
|
||||||
|
==================
|
||||||
|
|
||||||
|
Datastores
|
||||||
|
----------
|
||||||
|
|
||||||
|
A Datastore is the logical place where :ref:`Backup Snapshots
|
||||||
|
<term_backup_snapshot>` and their chunks are stored. Snapshots consist of a
|
||||||
|
manifest, blobs, dynamic- and fixed-indexes (see :ref:`terms`), and are
|
||||||
|
stored in the following directory structure:
|
||||||
|
|
||||||
|
<datastore-root>/<type>/<id>/<time>/
|
||||||
|
|
||||||
|
The deduplication of datastores is based on reusing chunks, which are
|
||||||
|
referenced by the indexes in a backup snapshot. This means that multiple
|
||||||
|
indexes can reference the same chunks, reducing the amount of space needed to
|
||||||
|
contain the data (even across backup snapshots).
|
||||||
|
|
||||||
|
Chunks
|
||||||
|
------
|
||||||
|
|
||||||
|
A chunk is some (possibly encrypted) data with a CRC-32 checksum at the end and
|
||||||
|
a type marker at the beginning. It is identified by the SHA-256 checksum of its
|
||||||
|
content.
|
||||||
|
|
||||||
|
To generate such chunks, backup data is split either into fixed-size or
|
||||||
|
dynamically sized chunks. The same content will be hashed to the same checksum.
|
||||||
|
|
||||||
|
The chunks of a datastore are found in
|
||||||
|
|
||||||
|
<datastore-root>/.chunks/
|
||||||
|
|
||||||
|
This chunk directory is further subdivided by the first four byte of the chunks
|
||||||
|
checksum, so the chunk with the checksum
|
||||||
|
|
||||||
|
a342e8151cbf439ce65f3df696b54c67a114982cc0aa751f2852c2f7acc19a8b
|
||||||
|
|
||||||
|
lives in
|
||||||
|
|
||||||
|
<datastore-root>/.chunks/a342/
|
||||||
|
|
||||||
|
This is done to reduce the number of files per directory, as having many files
|
||||||
|
per directory can be bad for file system performance.
|
||||||
|
|
||||||
|
These chunk directories ('0000'-'ffff') will be preallocated when a datastore
|
||||||
|
is created.
|
||||||
|
|
||||||
|
Fixed-sized Chunks
|
||||||
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
For block based backups (like VMs), fixed-sized chunks are used. The content
|
||||||
|
(disk image), is split into chunks of the same length (typically 4 MiB).
|
||||||
|
|
||||||
|
This works very well for VM images, since the file system on the guest most
|
||||||
|
often tries to allocate files in contiguous pieces, so new files get new
|
||||||
|
blocks, and changing existing files changes only their own blocks.
|
||||||
|
|
||||||
|
As an optimization, VMs in `Proxmox VE`_ can make use of 'dirty bitmaps', which
|
||||||
|
can track the changed blocks of an image. Since these bitmap are also a
|
||||||
|
representation of the image split into chunks, there is a direct relation
|
||||||
|
between dirty blocks of the image and chunks which need to get uploaded, so
|
||||||
|
only modified chunks of the disk have to be uploaded for a backup.
|
||||||
|
|
||||||
|
Since the image is always split into chunks of the same size, unchanged blocks
|
||||||
|
will result in identical checksums for those chunks, so such chunks do not need
|
||||||
|
to be backed up again. This way storage snapshots are not needed to find the
|
||||||
|
changed blocks.
|
||||||
|
|
||||||
|
For consistency, `Proxmox VE`_ uses a QEMU internal snapshot mechanism, that
|
||||||
|
does not rely on storage snapshots either.
|
||||||
|
|
||||||
|
Dynamically sized Chunks
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
If one does not want to backup block-based systems but rather file-based
|
||||||
|
systems, using fixed-sized chunks is not a good idea, since every time a file
|
||||||
|
would change in size, the remaining data gets shifted around and this would
|
||||||
|
result in many chunks changing, reducing the amount of deduplication.
|
||||||
|
|
||||||
|
To improve this, `Proxmox Backup`_ Server uses dynamically sized chunks
|
||||||
|
instead. Instead of splitting an image into fixed sizes, it first generates a
|
||||||
|
consistent file archive (:ref:`pxar <pxar-format>`) and uses a rolling hash
|
||||||
|
over this on-the-fly generated archive to calculate chunk boundaries.
|
||||||
|
|
||||||
|
We use a variant of Buzhash which is a cyclic polynomial algorithm. It works
|
||||||
|
by continuously calculating a checksum while iterating over the data, and on
|
||||||
|
certain conditions it triggers a hash boundary.
|
||||||
|
|
||||||
|
Assuming that most files of the system that is to be backed up have not
|
||||||
|
changed, eventually the algorithm triggers the boundary on the same data as a
|
||||||
|
previous backup, resulting in chunks that can be reused.
|
||||||
|
|
||||||
|
Encrypted Chunks
|
||||||
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Encrypted chunks are a special case. Both fixed- and dynamically sized chunks
|
||||||
|
can be encrypted, and they are handled in a slightly different manner than
|
||||||
|
normal chunks.
|
||||||
|
|
||||||
|
The hashes of encrypted chunks are calculated not with the actual (encrypted)
|
||||||
|
chunk content, but with the plaintext content concatenated with the encryption
|
||||||
|
key. This way, two chunks of the same data encrypted with different keys
|
||||||
|
generate two different checksums and no collisions occur for multiple
|
||||||
|
encryption keys.
|
||||||
|
|
||||||
|
This is done to speed up the client part of the backup, since it only needs to
|
||||||
|
encrypt chunks that are actually getting uploaded. Chunks that exist already in
|
||||||
|
the previous backup, do not need to be encrypted and uploaded.
|
||||||
|
|
||||||
|
Caveats and Limitations
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Notes on hash collisions
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Every hashing algorithm has a chance to produce collisions, meaning two (or
|
||||||
|
more) inputs generate the same checksum. For SHA-256, this chance is
|
||||||
|
negligible. To calculate such a collision, one can use the ideas of the
|
||||||
|
'birthday problem' from probability theory. For big numbers, this is actually
|
||||||
|
infeasible to calculate with regular computers, but there is a good
|
||||||
|
approximation:
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
p(n, d) = 1 - e^{-n^2/(2d)}
|
||||||
|
|
||||||
|
Where `n` is the number of tries, and `d` is the number of possibilities.
|
||||||
|
For a concrete example lets assume a large datastore of 1 PiB, and an average
|
||||||
|
chunk size of 4 MiB. That means :math:`n = 268435456` tries, and :math:`d =
|
||||||
|
2^{256}` possibilities. Inserting those values in the formula from earlier you
|
||||||
|
will see that the probability of a collision in that scenario is:
|
||||||
|
|
||||||
|
.. math::
|
||||||
|
|
||||||
|
3.1115 * 10^{-61}
|
||||||
|
|
||||||
|
For context, in a lottery game of guessing 6 out of 45, the chance to correctly
|
||||||
|
guess all 6 numbers is only :math:`1.2277 * 10^{-7}`, that means the chance of
|
||||||
|
collission is about the same as winning 13 such lotto games *in a row*.
|
||||||
|
|
||||||
|
In conclusion, it is extremely unlikely that such a collision would occur by
|
||||||
|
accident in a normal datastore.
|
||||||
|
|
||||||
|
Additionally, SHA-256 is prone to length extension attacks, but since there is
|
||||||
|
an upper limit for how big the chunk are, this is not a problem, since a
|
||||||
|
potential attacker cannot arbitrarily add content to the data beyond that
|
||||||
|
limit.
|
||||||
|
|
||||||
|
File-based Backup
|
||||||
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Since dynamically sized chunks (for file-based backups) are created on a custom
|
||||||
|
archive format (pxar) and not over the files directly, there is no relation
|
||||||
|
between files and the chunks. This means that the Proxmox Backup client has to
|
||||||
|
read all files again for every backup, otherwise it would not be possible to
|
||||||
|
generate a consistent independent pxar archive where the original chunks can be
|
||||||
|
reused. Note that there will be still only new or change chunks be uploaded.
|
||||||
|
|
||||||
|
Verification of encrypted chunks
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
For encrypted chunks, only the checksum of the original (plaintext) data is
|
||||||
|
available, making it impossible for the server (without the encryption key), to
|
||||||
|
verify its content against it. Instead only the CRC-32 checksum gets checked.
|
@ -1,3 +1,5 @@
|
|||||||
|
.. _terms:
|
||||||
|
|
||||||
Terminology
|
Terminology
|
||||||
===========
|
===========
|
||||||
|
|
||||||
@ -99,6 +101,7 @@ Backup Group
|
|||||||
The tuple ``<type>/<ID>`` is called a backup group. Such a group
|
The tuple ``<type>/<ID>`` is called a backup group. Such a group
|
||||||
may contain one or more backup snapshots.
|
may contain one or more backup snapshots.
|
||||||
|
|
||||||
|
.. _term_backup_snapshot:
|
||||||
|
|
||||||
Backup Snapshot
|
Backup Snapshot
|
||||||
---------------
|
---------------
|
||||||
|
@ -284,3 +284,109 @@ you can use the ``proxmox-backup-manager user permission`` command:
|
|||||||
|
|
||||||
Path: /datastore/store1
|
Path: /datastore/store1
|
||||||
- Datastore.Backup (*)
|
- Datastore.Backup (*)
|
||||||
|
|
||||||
|
.. _user_tfa:
|
||||||
|
|
||||||
|
Two-factor authentication
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
With simple authentication, only a password (single factor) is required to
|
||||||
|
successfully claim an identity (authenticate), for example, to be able to log in
|
||||||
|
as `root@pam` on a specific instance of Proxmox Backup Server. In this case, if
|
||||||
|
the password gets stolen or leaked, anybody can use it to log in - even if they
|
||||||
|
should not be allowed to do so.
|
||||||
|
|
||||||
|
With two-factor authentication (TFA), a user is asked for an additional factor
|
||||||
|
to verify their authenticity. Rather than relying on something only the user
|
||||||
|
knows (a password), this extra factor requires something only the user has, for
|
||||||
|
example, a piece of hardware (security key) or a secret saved on the user's
|
||||||
|
smartphone. This prevents a remote user from gaining unauthorized access to an
|
||||||
|
account, as even if they have the password, they will not have access to the
|
||||||
|
physical object (second factor).
|
||||||
|
|
||||||
|
.. image:: images/screenshots/pbs-gui-tfa-login.png
|
||||||
|
:align: right
|
||||||
|
:alt: Add a new user
|
||||||
|
|
||||||
|
Available Second Factors
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
You can set up multiple second factors, in order to avoid a situation in which
|
||||||
|
losing your smartphone or security key locks you out of your account
|
||||||
|
permanently.
|
||||||
|
|
||||||
|
Proxmox Backup Server supports three different two-factor authentication
|
||||||
|
methods:
|
||||||
|
|
||||||
|
* TOTP (`Time-based One-Time Password <https://en.wikipedia.org/wiki/Time-based_One-Time_Password>`_).
|
||||||
|
A short code derived from a shared secret and the current time, it changes
|
||||||
|
every 30 seconds.
|
||||||
|
|
||||||
|
* WebAuthn (`Web Authentication <https://en.wikipedia.org/wiki/WebAuthn>`_).
|
||||||
|
A general standard for authentication. It is implemented by various security
|
||||||
|
devices, like hardware keys or trusted platform modules (TPM) from a computer
|
||||||
|
or smart phone.
|
||||||
|
|
||||||
|
* Single use Recovery Keys. A list of keys which should either be printed out
|
||||||
|
and locked in a secure place or saved digitally in an electronic vault.
|
||||||
|
Each key can be used only once. These are perfect for ensuring that you are
|
||||||
|
not locked out, even if all of your other second factors are lost or corrupt.
|
||||||
|
|
||||||
|
|
||||||
|
Setup
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
.. _user_tfa_setup_totp:
|
||||||
|
|
||||||
|
TOTP
|
||||||
|
^^^^
|
||||||
|
|
||||||
|
.. image:: images/screenshots/pbs-gui-tfa-add-totp.png
|
||||||
|
:align: right
|
||||||
|
:alt: Add a new user
|
||||||
|
|
||||||
|
There is no server setup required. Simply install a TOTP app on your
|
||||||
|
smartphone (for example, `FreeOTP <https://freeotp.github.io/>`_) and use the
|
||||||
|
Proxmox Backup Server web-interface to add a TOTP factor.
|
||||||
|
|
||||||
|
.. _user_tfa_setup_webauthn:
|
||||||
|
|
||||||
|
WebAuthn
|
||||||
|
^^^^^^^^
|
||||||
|
|
||||||
|
For WebAuthn to work, you need to have two things:
|
||||||
|
|
||||||
|
* a trusted HTTPS certificate (for example, by using `Let's Encrypt
|
||||||
|
<https://pbs.proxmox.com/wiki/index.php/HTTPS_Certificate_Configuration>`_)
|
||||||
|
|
||||||
|
* setup the WebAuthn configuration (see *Configuration -> Authentication* in the
|
||||||
|
Proxmox Backup Server web-interface). This can be auto-filled in most setups.
|
||||||
|
|
||||||
|
Once you have fulfilled both of these requirements, you can add a WebAuthn
|
||||||
|
configuration in the *Access Control* panel.
|
||||||
|
|
||||||
|
.. _user_tfa_setup_recovery_keys:
|
||||||
|
|
||||||
|
Recovery Keys
|
||||||
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. image:: images/screenshots/pbs-gui-tfa-add-recovery-keys.png
|
||||||
|
:align: right
|
||||||
|
:alt: Add a new user
|
||||||
|
|
||||||
|
Recovery key codes do not need any preparation; you can simply create a set of
|
||||||
|
recovery keys in the *Access Control* panel.
|
||||||
|
|
||||||
|
.. note:: There can only be one set of single-use recovery keys per user at any
|
||||||
|
time.
|
||||||
|
|
||||||
|
TFA and Automated Access
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Two-factor authentication is only implemented for the web-interface. You should
|
||||||
|
use :ref:`API Tokens <user_tokens>` for all other use cases, especially
|
||||||
|
non-interactive ones (for example, adding a Proxmox Backup Server to Proxmox VE
|
||||||
|
as a storage).
|
||||||
|
@ -28,7 +28,7 @@ async fn run() -> Result<(), Error> {
|
|||||||
|
|
||||||
let auth_id = Authid::root_auth_id();
|
let auth_id = Authid::root_auth_id();
|
||||||
|
|
||||||
let options = HttpClientOptions::new()
|
let options = HttpClientOptions::default()
|
||||||
.interactive(true)
|
.interactive(true)
|
||||||
.ticket_cache(true);
|
.ticket_cache(true);
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ use std::future::Future;
|
|||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
use anyhow::{Error};
|
use anyhow::Error;
|
||||||
use futures::future::TryFutureExt;
|
use futures::future::TryFutureExt;
|
||||||
use futures::stream::Stream;
|
use futures::stream::Stream;
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
@ -38,11 +38,11 @@ impl Future for Process {
|
|||||||
this.body.flow_control().release_capacity(chunk.len())?;
|
this.body.flow_control().release_capacity(chunk.len())?;
|
||||||
this.bytes += chunk.len();
|
this.bytes += chunk.len();
|
||||||
// println!("GOT FRAME {}", chunk.len());
|
// println!("GOT FRAME {}", chunk.len());
|
||||||
},
|
}
|
||||||
Some(Err(err)) => return Poll::Ready(Err(Error::from(err))),
|
Some(Err(err)) => return Poll::Ready(Err(Error::from(err))),
|
||||||
None => {
|
None => {
|
||||||
this.trailers = true;
|
this.trailers = true;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -52,7 +52,6 @@ impl Future for Process {
|
|||||||
fn send_request(
|
fn send_request(
|
||||||
mut client: h2::client::SendRequest<bytes::Bytes>,
|
mut client: h2::client::SendRequest<bytes::Bytes>,
|
||||||
) -> impl Future<Output = Result<usize, Error>> {
|
) -> impl Future<Output = Result<usize, Error>> {
|
||||||
|
|
||||||
println!("sending request");
|
println!("sending request");
|
||||||
|
|
||||||
let request = http::Request::builder()
|
let request = http::Request::builder()
|
||||||
@ -62,10 +61,10 @@ fn send_request(
|
|||||||
|
|
||||||
let (response, _stream) = client.send_request(request, true).unwrap();
|
let (response, _stream) = client.send_request(request, true).unwrap();
|
||||||
|
|
||||||
response
|
response.map_err(Error::from).and_then(|response| Process {
|
||||||
.map_err(Error::from)
|
body: response.into_body(),
|
||||||
.and_then(|response| {
|
trailers: false,
|
||||||
Process { body: response.into_body(), trailers: false, bytes: 0 }
|
bytes: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,11 +73,10 @@ fn main() -> Result<(), Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn run() -> Result<(), Error> {
|
async fn run() -> Result<(), Error> {
|
||||||
|
|
||||||
let start = std::time::SystemTime::now();
|
let start = std::time::SystemTime::now();
|
||||||
|
|
||||||
let conn = TcpStream::connect(std::net::SocketAddr::from(([127,0,0,1], 8008)))
|
let conn = TcpStream::connect(std::net::SocketAddr::from(([127, 0, 0, 1], 8008))).await?;
|
||||||
.await?;
|
conn.set_nodelay(true).unwrap();
|
||||||
|
|
||||||
let (client, h2) = h2::client::Builder::new()
|
let (client, h2) = h2::client::Builder::new()
|
||||||
.initial_connection_window_size(1024 * 1024 * 1024)
|
.initial_connection_window_size(1024 * 1024 * 1024)
|
||||||
@ -99,10 +97,13 @@ async fn run() -> Result<(), Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let elapsed = start.elapsed().unwrap();
|
let elapsed = start.elapsed().unwrap();
|
||||||
let elapsed = (elapsed.as_secs() as f64) +
|
let elapsed = (elapsed.as_secs() as f64) + (elapsed.subsec_millis() as f64) / 1000.0;
|
||||||
(elapsed.subsec_millis() as f64)/1000.0;
|
|
||||||
|
|
||||||
println!("Downloaded {} bytes, {} MB/s", bytes, (bytes as f64)/(elapsed*1024.0*1024.0));
|
println!(
|
||||||
|
"Downloaded {} bytes, {} MB/s",
|
||||||
|
bytes,
|
||||||
|
(bytes as f64) / (elapsed * 1024.0 * 1024.0)
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ use std::task::{Context, Poll};
|
|||||||
use anyhow::{format_err, Error};
|
use anyhow::{format_err, Error};
|
||||||
use futures::future::TryFutureExt;
|
use futures::future::TryFutureExt;
|
||||||
use futures::stream::Stream;
|
use futures::stream::Stream;
|
||||||
|
use tokio::net::TcpStream;
|
||||||
|
|
||||||
// Simple H2 client to test H2 download speed using h2s-server.rs
|
// Simple H2 client to test H2 download speed using h2s-server.rs
|
||||||
|
|
||||||
@ -37,11 +38,11 @@ impl Future for Process {
|
|||||||
this.body.flow_control().release_capacity(chunk.len())?;
|
this.body.flow_control().release_capacity(chunk.len())?;
|
||||||
this.bytes += chunk.len();
|
this.bytes += chunk.len();
|
||||||
// println!("GOT FRAME {}", chunk.len());
|
// println!("GOT FRAME {}", chunk.len());
|
||||||
},
|
}
|
||||||
Some(Err(err)) => return Poll::Ready(Err(Error::from(err))),
|
Some(Err(err)) => return Poll::Ready(Err(Error::from(err))),
|
||||||
None => {
|
None => {
|
||||||
this.trailers = true;
|
this.trailers = true;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,10 +61,10 @@ fn send_request(
|
|||||||
|
|
||||||
let (response, _stream) = client.send_request(request, true).unwrap();
|
let (response, _stream) = client.send_request(request, true).unwrap();
|
||||||
|
|
||||||
response
|
response.map_err(Error::from).and_then(|response| Process {
|
||||||
.map_err(Error::from)
|
body: response.into_body(),
|
||||||
.and_then(|response| {
|
trailers: false,
|
||||||
Process { body: response.into_body(), trailers: false, bytes: 0 }
|
bytes: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,22 +75,22 @@ fn main() -> Result<(), Error> {
|
|||||||
async fn run() -> Result<(), Error> {
|
async fn run() -> Result<(), Error> {
|
||||||
let start = std::time::SystemTime::now();
|
let start = std::time::SystemTime::now();
|
||||||
|
|
||||||
let conn =
|
let conn = TcpStream::connect(std::net::SocketAddr::from(([127, 0, 0, 1], 8008))).await?;
|
||||||
tokio::net::TcpStream::connect(std::net::SocketAddr::from(([127,0,0,1], 8008))).await?;
|
|
||||||
|
|
||||||
conn.set_nodelay(true).unwrap();
|
conn.set_nodelay(true).unwrap();
|
||||||
conn.set_recv_buffer_size(1024*1024).unwrap();
|
|
||||||
|
|
||||||
use openssl::ssl::{SslConnector, SslMethod};
|
use openssl::ssl::{SslConnector, SslMethod};
|
||||||
|
|
||||||
let mut ssl_connector_builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
let mut ssl_connector_builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
||||||
ssl_connector_builder.set_verify(openssl::ssl::SslVerifyMode::NONE);
|
ssl_connector_builder.set_verify(openssl::ssl::SslVerifyMode::NONE);
|
||||||
let conn =
|
let ssl = ssl_connector_builder
|
||||||
tokio_openssl::connect(
|
.build()
|
||||||
ssl_connector_builder.build().configure()?,
|
.configure()?
|
||||||
"localhost",
|
.into_ssl("localhost")?;
|
||||||
conn,
|
|
||||||
)
|
let conn = tokio_openssl::SslStream::new(ssl, conn)?;
|
||||||
|
let mut conn = Box::pin(conn);
|
||||||
|
conn.as_mut()
|
||||||
|
.connect()
|
||||||
.await
|
.await
|
||||||
.map_err(|err| format_err!("connect failed - {}", err))?;
|
.map_err(|err| format_err!("connect failed - {}", err))?;
|
||||||
|
|
||||||
@ -100,31 +101,25 @@ async fn run() -> Result<(), Error> {
|
|||||||
.handshake(conn)
|
.handshake(conn)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Spawn a task to run the conn...
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
if let Err(e) = h2.await {
|
if let Err(err) = h2.await {
|
||||||
println!("GOT ERR={:?}", e);
|
println!("GOT ERR={:?}", err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut bytes = 0;
|
let mut bytes = 0;
|
||||||
for _ in 0..100 {
|
for _ in 0..2000 {
|
||||||
match send_request(client.clone()).await {
|
bytes += send_request(client.clone()).await?;
|
||||||
Ok(b) => {
|
|
||||||
bytes += b;
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("ERROR {}", e);
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let elapsed = start.elapsed().unwrap();
|
let elapsed = start.elapsed().unwrap();
|
||||||
let elapsed = (elapsed.as_secs() as f64) +
|
let elapsed = (elapsed.as_secs() as f64) + (elapsed.subsec_millis() as f64) / 1000.0;
|
||||||
(elapsed.subsec_millis() as f64)/1000.0;
|
|
||||||
|
|
||||||
println!("Downloaded {} bytes, {} MB/s", bytes, (bytes as f64)/(elapsed*1024.0*1024.0));
|
println!(
|
||||||
|
"Downloaded {} bytes, {} MB/s",
|
||||||
|
bytes,
|
||||||
|
(bytes as f64) / (elapsed * 1024.0 * 1024.0)
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,12 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use anyhow::{format_err, Error};
|
use anyhow::{format_err, Error};
|
||||||
use futures::*;
|
use futures::*;
|
||||||
use hyper::{Request, Response, Body};
|
use hyper::{Body, Request, Response};
|
||||||
use openssl::ssl::{SslMethod, SslAcceptor, SslFiletype};
|
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
|
||||||
use tokio::net::{TcpListener, TcpStream};
|
use tokio::net::{TcpListener, TcpStream};
|
||||||
|
|
||||||
use proxmox_backup::configdir;
|
use proxmox_backup::configdir;
|
||||||
|
|
||||||
// Simple H2 server to test H2 speed with h2s-client.rs
|
|
||||||
|
|
||||||
fn main() -> Result<(), Error> {
|
fn main() -> Result<(), Error> {
|
||||||
proxmox_backup::tools::runtime::main(run())
|
proxmox_backup::tools::runtime::main(run())
|
||||||
}
|
}
|
||||||
@ -19,22 +17,23 @@ async fn run() -> Result<(), Error> {
|
|||||||
let cert_path = configdir!("/proxy.pem");
|
let cert_path = configdir!("/proxy.pem");
|
||||||
|
|
||||||
let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
|
let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
|
||||||
acceptor.set_private_key_file(key_path, SslFiletype::PEM)
|
acceptor
|
||||||
|
.set_private_key_file(key_path, SslFiletype::PEM)
|
||||||
.map_err(|err| format_err!("unable to read proxy key {} - {}", key_path, err))?;
|
.map_err(|err| format_err!("unable to read proxy key {} - {}", key_path, err))?;
|
||||||
acceptor.set_certificate_chain_file(cert_path)
|
acceptor
|
||||||
|
.set_certificate_chain_file(cert_path)
|
||||||
.map_err(|err| format_err!("unable to read proxy cert {} - {}", cert_path, err))?;
|
.map_err(|err| format_err!("unable to read proxy cert {} - {}", cert_path, err))?;
|
||||||
acceptor.check_private_key().unwrap();
|
acceptor.check_private_key().unwrap();
|
||||||
|
|
||||||
let acceptor = Arc::new(acceptor.build());
|
let acceptor = Arc::new(acceptor.build());
|
||||||
|
|
||||||
let mut listener = TcpListener::bind(std::net::SocketAddr::from(([127,0,0,1], 8008))).await?;
|
let listener = TcpListener::bind(std::net::SocketAddr::from(([127, 0, 0, 1], 8008))).await?;
|
||||||
|
|
||||||
println!("listening on {:?}", listener.local_addr());
|
println!("listening on {:?}", listener.local_addr());
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let (socket, _addr) = listener.accept().await?;
|
let (socket, _addr) = listener.accept().await?;
|
||||||
tokio::spawn(handle_connection(socket, Arc::clone(&acceptor))
|
tokio::spawn(handle_connection(socket, Arc::clone(&acceptor)).map(|res| {
|
||||||
.map(|res| {
|
|
||||||
if let Err(err) = res {
|
if let Err(err) = res {
|
||||||
eprintln!("Error: {}", err);
|
eprintln!("Error: {}", err);
|
||||||
}
|
}
|
||||||
@ -42,15 +41,14 @@ async fn run() -> Result<(), Error> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_connection(
|
async fn handle_connection(socket: TcpStream, acceptor: Arc<SslAcceptor>) -> Result<(), Error> {
|
||||||
socket: TcpStream,
|
|
||||||
acceptor: Arc<SslAcceptor>,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
socket.set_nodelay(true).unwrap();
|
socket.set_nodelay(true).unwrap();
|
||||||
socket.set_send_buffer_size(1024*1024).unwrap();
|
|
||||||
socket.set_recv_buffer_size(1024*1024).unwrap();
|
|
||||||
|
|
||||||
let socket = tokio_openssl::accept(acceptor.as_ref(), socket).await?;
|
let ssl = openssl::ssl::Ssl::new(acceptor.context())?;
|
||||||
|
let stream = tokio_openssl::SslStream::new(ssl, socket)?;
|
||||||
|
let mut stream = Box::pin(stream);
|
||||||
|
|
||||||
|
stream.as_mut().accept().await?;
|
||||||
|
|
||||||
let mut http = hyper::server::conn::Http::new();
|
let mut http = hyper::server::conn::Http::new();
|
||||||
http.http2_only(true);
|
http.http2_only(true);
|
||||||
@ -61,7 +59,7 @@ async fn handle_connection(
|
|||||||
|
|
||||||
let service = hyper::service::service_fn(|_req: Request<Body>| {
|
let service = hyper::service::service_fn(|_req: Request<Body>| {
|
||||||
println!("Got request");
|
println!("Got request");
|
||||||
let buffer = vec![65u8; 1024*1024]; // nonsense [A,A,A,A...]
|
let buffer = vec![65u8; 4 * 1024 * 1024]; // nonsense [A,A,A,A...]
|
||||||
let body = Body::from(buffer);
|
let body = Body::from(buffer);
|
||||||
|
|
||||||
let response = Response::builder()
|
let response = Response::builder()
|
||||||
@ -72,7 +70,7 @@ async fn handle_connection(
|
|||||||
future::ok::<_, Error>(response)
|
future::ok::<_, Error>(response)
|
||||||
});
|
});
|
||||||
|
|
||||||
http.serve_connection(socket, service)
|
http.serve_connection(stream, service)
|
||||||
.map_err(Error::from)
|
.map_err(Error::from)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -1,26 +1,21 @@
|
|||||||
use anyhow::{Error};
|
use anyhow::Error;
|
||||||
use futures::*;
|
use futures::*;
|
||||||
|
use hyper::{Body, Request, Response};
|
||||||
|
|
||||||
// Simple H2 server to test H2 speed with h2client.rs
|
use tokio::net::{TcpListener, TcpStream};
|
||||||
|
|
||||||
use tokio::net::TcpListener;
|
|
||||||
use tokio::io::{AsyncRead, AsyncWrite};
|
|
||||||
|
|
||||||
use proxmox_backup::client::pipe_to_stream::PipeToSendStream;
|
|
||||||
|
|
||||||
fn main() -> Result<(), Error> {
|
fn main() -> Result<(), Error> {
|
||||||
proxmox_backup::tools::runtime::main(run())
|
proxmox_backup::tools::runtime::main(run())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run() -> Result<(), Error> {
|
async fn run() -> Result<(), Error> {
|
||||||
let mut listener = TcpListener::bind(std::net::SocketAddr::from(([127,0,0,1], 8008))).await?;
|
let listener = TcpListener::bind(std::net::SocketAddr::from(([127, 0, 0, 1], 8008))).await?;
|
||||||
|
|
||||||
println!("listening on {:?}", listener.local_addr());
|
println!("listening on {:?}", listener.local_addr());
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let (socket, _addr) = listener.accept().await?;
|
let (socket, _addr) = listener.accept().await?;
|
||||||
tokio::spawn(handle_connection(socket)
|
tokio::spawn(handle_connection(socket).map(|res| {
|
||||||
.map(|res| {
|
|
||||||
if let Err(err) = res {
|
if let Err(err) = res {
|
||||||
eprintln!("Error: {}", err);
|
eprintln!("Error: {}", err);
|
||||||
}
|
}
|
||||||
@ -28,24 +23,33 @@ async fn run() -> Result<(), Error> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_connection<T: AsyncRead + AsyncWrite + Unpin>(socket: T) -> Result<(), Error> {
|
async fn handle_connection(socket: TcpStream) -> Result<(), Error> {
|
||||||
let mut conn = h2::server::handshake(socket).await?;
|
socket.set_nodelay(true).unwrap();
|
||||||
|
|
||||||
println!("H2 connection bound");
|
let mut http = hyper::server::conn::Http::new();
|
||||||
|
http.http2_only(true);
|
||||||
|
// increase window size: todo - find optiomal size
|
||||||
|
let max_window_size = (1 << 31) - 2;
|
||||||
|
http.http2_initial_stream_window_size(max_window_size);
|
||||||
|
http.http2_initial_connection_window_size(max_window_size);
|
||||||
|
|
||||||
while let Some((request, mut respond)) = conn.try_next().await? {
|
let service = hyper::service::service_fn(|_req: Request<Body>| {
|
||||||
println!("GOT request: {:?}", request);
|
println!("Got request");
|
||||||
|
let buffer = vec![65u8; 4 * 1024 * 1024]; // nonsense [A,A,A,A...]
|
||||||
|
let body = Body::from(buffer);
|
||||||
|
|
||||||
let response = http::Response::builder()
|
let response = Response::builder()
|
||||||
.status(http::StatusCode::OK)
|
.status(http::StatusCode::OK)
|
||||||
.body(())
|
.header(http::header::CONTENT_TYPE, "application/octet-stream")
|
||||||
|
.body(body)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
future::ok::<_, Error>(response)
|
||||||
|
});
|
||||||
|
|
||||||
let send = respond.send_response(response, false).unwrap();
|
http.serve_connection(socket, service)
|
||||||
let data = vec![65u8; 1024*1024];
|
.map_err(Error::from)
|
||||||
PipeToSendStream::new(bytes::Bytes::from(data), send).await?;
|
.await?;
|
||||||
println!("DATA SENT");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
println!("H2 connection CLOSE !");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user