forked from vitalif/vitastor
Compare commits
481 Commits
http-hello
...
fix-iterat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b51c05200d | ||
| 6d1b9165a2 | |||
| e58b37b284 | |||
| 53c7da3e2d | |||
| 75a35c3b36 | |||
| a375f43a93 | |||
| 22ae66b95f | |||
| 456fad7fa7 | |||
| 06a5d24240 | |||
| a6a99a5967 | |||
| eba8ca7192 | |||
|
|
e1d9121870 | ||
| 72620ebee5 | |||
| d0e3ee4c2a | |||
| 328bf0cba1 | |||
| 16ce85d685 | |||
| 606dd820cd | |||
| f9f0b35311 | |||
| 778a3469b4 | |||
| a8ed25ddf5 | |||
| 49877a0fa9 | |||
| 4e11fd256e | |||
| 1a1c338cc9 | |||
| 7f88846a5d | |||
| d5f1f6967a | |||
| c486a14f6a | |||
| df3c8cf72b | |||
| a8f5d7cafe | |||
| c5efed2061 | |||
| d5e56827c7 | |||
| a788add518 | |||
| d5ebc93e7d | |||
| 9d4067bcf0 | |||
| 0be4cba1e6 | |||
| 2179f6ab55 | |||
| 1dd4cbd7c3 | |||
| 174b36cf46 | |||
| eb6328d443 | |||
| c560f3883d | |||
| 4d96f73738 | |||
| 7cbdd831c7 | |||
| 820a226b1d | |||
| a25625a1d3 | |||
| db0b6ec926 | |||
| 097afe5d8b | |||
| fbf48a86be | |||
| 2cb32f704d | |||
| d397d7ac3e | |||
| 9d36831ae7 | |||
| 85976c2f31 | |||
| 517c016324 | |||
|
|
2b5078823a | ||
|
|
b2d5206613 | ||
|
|
c67843e6b5 | ||
|
|
9353a58446 | ||
| 7e0ba6d312 | |||
| beb7854e39 | |||
| 4d567dd0e6 | |||
|
|
4b95d248bf | ||
| cee6882d6e | |||
| 5c45425235 | |||
| dd9b9f926d | |||
| 806ebf5bcd | |||
| 5f4e394eb0 | |||
| 50e36a04df | |||
| 9fc2781798 | |||
| 26c6251888 | |||
| 145942fc3c | |||
| 2a68ad2a05 | |||
| 10a6782bdf | |||
| 5f2b46bb57 | |||
| f9a247df42 | |||
| 6f55e36998 | |||
| 1cbe466c39 | |||
| 38a0a9b9b7 | |||
| c826cbb735 | |||
| 6038c537da | |||
| 77481dd824 | |||
| 9ff24abc0e | |||
| c2dc2e5840 | |||
| df9a285019 | |||
| a55cca724e | |||
| 74c5047412 | |||
| 157ef711d4 | |||
| 96983a4718 | |||
| 1af744d1e3 | |||
| db86731851 | |||
| c14135cb7a | |||
| cef3f16cca | |||
| 145c279f82 | |||
| d153583c7f | |||
| 0e9fe3a19f | |||
| ae8d47bffa | |||
| 2b25bb9488 | |||
| cb89e17255 | |||
| 2ed098ea22 | |||
| 3cf7b1038e | |||
| 87a57f577b | |||
| 2233116023 | |||
| 9a4b61424a | |||
| cde8f5b2c3 | |||
| d1bbb4578f | |||
| bdfc7349b9 | |||
| 17a65f570d | |||
| 7bfe12a8f9 | |||
| 8f8b43d1d4 | |||
| 824762a04e | |||
| 090c28c7c5 | |||
| c35e101341 | |||
| 230460e937 | |||
| 2a38d5d117 | |||
| 43d3d6560f | |||
| 6d7223d9e9 | |||
| b88e2ea605 | |||
| e4a7e9bf74 | |||
| dde78a4f44 | |||
| 7784df8bcf | |||
| 24bdbc7f97 | |||
| 2a73f92852 | |||
| 31c749659f | |||
| 05114e576c | |||
|
|
caf834decd | ||
| 9c6ec459a1 | |||
| c244d85ef3 | |||
| 3fd779484d | |||
| 29b8eabbea | |||
| b5955ad131 | |||
| 2f7b58b276 | |||
|
|
426b9f920a | ||
| 58cf24dc74 | |||
| aefaa2eef9 | |||
| d9719c78e8 | |||
| 85c884fdc8 | |||
| a0459da150 | |||
| 6f3855fc24 | |||
| 7e816fadf2 | |||
| 435301c115 | |||
| bd5459be06 | |||
| 2e5e05bcaa | |||
| 0b9439f814 | |||
| 0f98db3a70 | |||
| e2a4c0b0e0 | |||
| 8c78bcf38b | |||
| 3d2416bcd1 | |||
| c7952bcd0c | |||
| dac30060e1 | |||
| 60bdd5a44f | |||
| aa183447ea | |||
| 6f218df51c | |||
| 9cb499f604 | |||
| fd02e59065 | |||
| 1e9ac287b4 | |||
| 862951c304 | |||
| c5a9ec1679 | |||
| f3a8968353 | |||
| eb9f51842e | |||
| 96e30e9849 | |||
| c4f8581334 | |||
| f8fd62e254 | |||
| 1227c115c1 | |||
| de2de86e0b | |||
| a6572c8452 | |||
| 077ee334af | |||
| 022a9b12dd | |||
| 8c684fd09a | |||
| 284055376e | |||
| 5a7fbc637a | |||
| 1a1834ff8a | |||
| 64da858cbd | |||
| a914e4da6c | |||
| a65022195d | |||
| cd797cf9b6 | |||
| 40dd5a2a46 | |||
| 9eccf8ca8c | |||
| b7d018878e | |||
| 283a6a8934 | |||
| 580fad8694 | |||
| 90ed7a800b | |||
| b28857c2e6 | |||
| d14c8b66ea | |||
| caab4ecf7c | |||
| ce6b41a74f | |||
| 1fd2093a6e | |||
| 449f6c2088 | |||
| 1dd414c035 | |||
| 533e9de334 | |||
| 09b2d90690 | |||
| 1f1bee724e | |||
| 46d22185ee | |||
| 6c828ae9f9 | |||
| 4f5cb882ff | |||
| abb3a30b94 | |||
| c9e2c32397 | |||
| 68c5d0f1ca | |||
| 489b148c5b | |||
| de5f80c670 | |||
| 6084130798 | |||
| 3745b477e1 | |||
| 6039e3688a | |||
| 448def9edf | |||
| f2db7f11ba | |||
| c6ff1fcfae | |||
| 8922344845 | |||
| 667689b9c9 | |||
| 318b2f3bbd | |||
| 07b640d019 | |||
| 039fd7b81c | |||
| e418b675e6 | |||
| c03049cd73 | |||
| 5087ae65cc | |||
| bfb53e3e2f | |||
| 7bdf149768 | |||
| 2ef1fe54a0 | |||
| 6a3feb2c8a | |||
| 16983b1cea | |||
| 8873041ffb | |||
| 53ade4af67 | |||
| f1077e06c2 | |||
| dc7b218291 | |||
| 0982f2a808 | |||
| 8d2e5c6c43 | |||
| aba4b0bced | |||
| 31bc62872d | |||
| e7414b01fe | |||
| 4f6591517d | |||
| 025f5c4e74 | |||
| 3b92b361a2 | |||
| 11331a8170 | |||
| 563c6756a5 | |||
| 3cc20739bb | |||
| 9a48ecf7f7 | |||
| 65c720b979 | |||
| 8ba10a3c34 | |||
| ddb0698a43 | |||
| 76bd792afe | |||
| 491ff88f6c | |||
| f1383b2eee | |||
| 75c65165b5 | |||
| d4cbf70521 | |||
| 47f8a98142 | |||
| 844bce3844 | |||
| 36c1cc5b41 | |||
| ef3535d6cf | |||
| 5f616c759d | |||
| a6a1a680d9 | |||
| 3f9f10ec5e | |||
| 20c961ae05 | |||
| 4f50623071 | |||
| 93f6fb9752 | |||
| 065372d9cd | |||
| 25d3f8100e | |||
| 7e2f8ff188 | |||
| 8454ee829a | |||
| 842a257a6f | |||
| b4bb09dd4b | |||
| cbaf3a8979 | |||
| 31df1dee98 | |||
| 60073919b5 | |||
| 4269dcb64f | |||
| d8e6486aa6 | |||
| 83029674c0 | |||
| 568301dd28 | |||
| 3bc5a18600 | |||
| 14b081744e | |||
| 11f1222aa3 | |||
| 5dec46c82e | |||
| 14aa842201 | |||
| 4974c213fe | |||
| dff723b2a2 | |||
| 931f753c85 | |||
| d978df1274 | |||
| 97abf2e53b | |||
| 539826067e | |||
| c7f2247b97 | |||
| 12c7810a4a | |||
| 3c9877003f | |||
| 4ff2034423 | |||
| 79b0ec5d79 | |||
| 13a4f156e2 | |||
| 9c80737bc6 | |||
| 622e073ab9 | |||
| 5e41c65214 | |||
| 2f46447800 | |||
| 5a856336a8 | |||
| e0ced36aa4 | |||
| 781ee54af6 | |||
| bb7c11a768 | |||
| 8bc6aa8ef3 | |||
| b3fecc8bf2 | |||
| 88ff27be60 | |||
| 4bb0c46380 | |||
| efe33acf1a | |||
| c77f01984c | |||
| 5dbc679e16 | |||
| d48864a6be | |||
| 462482d319 | |||
| 5ef9d78461 | |||
| 1b20010a69 | |||
| d750d00c6f | |||
| 66cb564e2c | |||
| f782decbd6 | |||
| 717582c4ba | |||
| cd62c0755f | |||
|
|
c6f5ab7d79 | ||
| 09607ddcbe | |||
| 278852b4d5 | |||
| 7b11c6e90d | |||
| 6251ce8b9a | |||
| d4a42f61cf | |||
| 6b003bcc34 | |||
| 1c945bcb41 | |||
| 716527b184 | |||
| 8c0486bd76 | |||
| d2cf271f64 | |||
| d93b488e32 | |||
| fbffec5abb | |||
| b6eb8f2055 | |||
| e373ea2163 | |||
| 5e12b4a1a5 | |||
| 78b067566f | |||
| cf3abdb9e3 | |||
| 826b35b369 | |||
| cdc730314b | |||
| 5248d7f324 | |||
| 4161f0bd01 | |||
| 5627977a9b | |||
| ec9cfa76c1 | |||
| 0c88884576 | |||
| ba7637d9ad | |||
| ac1025c7a5 | |||
| 2a81cef78a | |||
| bcf6a7c7d1 | |||
| 85c4be3957 | |||
| cf2ba05e4b | |||
| 04c2f8d408 | |||
| 0e528ca8f3 | |||
| c6f733b96a | |||
| 938ac09248 | |||
| 3becdbf5b9 | |||
| 9c49315fdf | |||
| 430d3cfb6f | |||
| c491db699c | |||
| e9d053e30f | |||
| 1be51f903c | |||
| cd51f14a90 | |||
| 9b8107875f | |||
| ca606570f7 | |||
| 22d094ccc6 | |||
| fd84d84279 | |||
| af2b1e28e3 | |||
| 9dda449f48 | |||
| e6d4b32629 | |||
| 4de22a08e2 | |||
| a403de46b3 | |||
| ac20f605f6 | |||
| 51ecbadb12 | |||
| 6a4627b625 | |||
| fd2b8b8792 | |||
| f3d662bac7 | |||
| 553cc8ef87 | |||
| 67fdf1142b | |||
| 02f6e564a6 | |||
| 33d14061d6 | |||
| 677e755e4e | |||
| 11a972cbfb | |||
| 1834743a0e | |||
| 213f76c66c | |||
| 91698404a7 | |||
| 27bd38d95e | |||
| ef0e61be1b | |||
| 26fb08d7da | |||
| 80fa3094b3 | |||
| df931b1e17 | |||
| 906294ae9a | |||
| 15f69719e4 | |||
| 43aa4cfff6 | |||
| 6901227390 | |||
| 7f718feaf6 | |||
| 236ffbb24e | |||
| 0e300f4c50 | |||
| 6d82a3daa3 | |||
| c0c01a8e57 | |||
| 334755e912 | |||
| c16f955a51 | |||
| c1dc14f5ee | |||
| ec6a70bbd3 | |||
| f236ed895a | |||
| 7d70c90196 | |||
| 0a04490043 | |||
| dce7ffde6f | |||
| f6bd1ff0e5 | |||
| ac00a06757 | |||
| 155cfb3c73 | |||
| 126891126a | |||
| 547a394be6 | |||
| 1a511acead | |||
| 5576a0d9ff | |||
| 879e9a32d1 | |||
| 747fd5c121 | |||
| b4aab7a78e | |||
| de26a995fc | |||
| 8418a9ad7b | |||
| 27be4ee2fa | |||
| 00517e2bac | |||
| e0a2615cbc | |||
| 63fe3c323a | |||
| a88465df05 | |||
| ad24be717a | |||
| 648e3b12f0 | |||
| a675993c74 | |||
| c9dfd0f67d | |||
| 84919a10a9 | |||
| 51ae4d6e24 | |||
| 572b20fedc | |||
| 4e2724b28f | |||
| 768b1675f8 | |||
| 38fa722725 | |||
| e56d83fb7f | |||
| ff95a85875 | |||
| 98203568a8 | |||
| 89df98ee08 | |||
| 0007a831b6 | |||
| 40517c335f | |||
| c9f7308b6a | |||
| 85c7e3bde0 | |||
| 4fb55b3535 | |||
| 912aca11a3 | |||
| 7b454bd16c | |||
| a0c8be46a4 | |||
| 53b4329fac | |||
| a7f41c4a12 | |||
| 5d78057ac3 | |||
| 8efc5a353f | |||
| 603b26b896 | |||
| a3b0fe0deb | |||
| f504e356d5 | |||
| 4ed17b7070 | |||
| 1fd2819724 | |||
| dcdabbc1ec | |||
| 625d5b7b9e | |||
| 9e507fd333 | |||
| c2b5118127 | |||
| 4b926e2223 | |||
| a5d9a6996a | |||
| 0ee03e7172 | |||
| 88b7d9afcd | |||
| f271c8450c | |||
| f78d7d4efc | |||
| fdaf7c88ff | |||
| 2fb6eb0c30 | |||
| 36d2b56208 | |||
| 14b22f2ba9 | |||
| fe8b1fe0cc | |||
| 1ec963e468 | |||
| 5100f822d8 | |||
| 7432494e88 | |||
| d0c0f3ea39 | |||
| f61190f31d | |||
| 3dc0ab5c33 | |||
| de96efed2f | |||
| 87a5230798 | |||
| 0c5e6d4346 | |||
| b278087410 | |||
| a8e821b13b | |||
| caa70317fa | |||
| b8eaaabfe4 | |||
| e4d80c415e | |||
| 553191c3ff | |||
| ab385252b5 | |||
| 041185c673 | |||
| b03ac80a57 | |||
| 2ba56074f9 | |||
| 4acfe149cb | |||
| 008ed5b269 | |||
| 4fffe0f032 | |||
| a76d5ccc0d | |||
| 8ed1e180e0 | |||
| 8832fc3b14 | |||
| 0134934c99 | |||
| 2e36f292bd | |||
| bcc6419760 |
@@ -23,7 +23,7 @@ RUN apt-get update
|
|||||||
RUN apt-get -y install etcd qemu-system-x86 qemu-block-extra qemu-utils fio libasan8 \
|
RUN apt-get -y install etcd qemu-system-x86 qemu-block-extra qemu-utils fio libasan8 \
|
||||||
libgoogle-perftools-dev devscripts libjerasure-dev cmake libibverbs-dev libisal-dev
|
libgoogle-perftools-dev devscripts libjerasure-dev cmake libibverbs-dev libisal-dev
|
||||||
RUN apt-get -y build-dep fio qemu=`dpkg -s qemu-system-x86|grep ^Version:|awk '{print $2}'`
|
RUN apt-get -y build-dep fio qemu=`dpkg -s qemu-system-x86|grep ^Version:|awk '{print $2}'`
|
||||||
RUN apt-get update && apt-get -y install jq lp-solve sudo nfs-common fdisk parted libc-ares-dev udev
|
RUN apt-get update && apt-get -y install jq lp-solve sudo nfs-common fdisk parted libc-ares-dev udev iproute2
|
||||||
RUN apt-get --download-only source fio qemu=`dpkg -s qemu-system-x86|grep ^Version:|awk '{print $2}'`
|
RUN apt-get --download-only source fio qemu=`dpkg -s qemu-system-x86|grep ^Version:|awk '{print $2}'`
|
||||||
|
|
||||||
RUN set -ex; \
|
RUN set -ex; \
|
||||||
|
|||||||
@@ -62,8 +62,13 @@ jobs:
|
|||||||
needs: build
|
needs: build
|
||||||
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
steps:
|
steps:
|
||||||
# leak sanitizer sometimes crashes
|
- name: Run tests
|
||||||
- run: cd /root/vitastor/build && ASAN_OPTIONS=detect_leaks=0 make -j16 build_tests test
|
id: test
|
||||||
|
run: cd /root/vitastor/build && make -j16 build_tests test
|
||||||
|
- name: Print logs
|
||||||
|
if: always() && steps.test.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
cat /root/vitastor/build/Testing/Temporary/LastTest.log
|
||||||
|
|
||||||
npm_lint:
|
npm_lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -162,6 +167,24 @@ jobs:
|
|||||||
echo ""
|
echo ""
|
||||||
done
|
done
|
||||||
|
|
||||||
|
test_change_pg_minsize:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 3
|
||||||
|
run: /root/vitastor/tests/test_change_pg_minsize.sh
|
||||||
|
- name: Print logs
|
||||||
|
if: always() && steps.test.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
|
||||||
|
echo "-------- $i --------"
|
||||||
|
cat $i
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
test_change_pg_size:
|
test_change_pg_size:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
needs: build
|
||||||
@@ -288,6 +311,42 @@ jobs:
|
|||||||
echo ""
|
echo ""
|
||||||
done
|
done
|
||||||
|
|
||||||
|
test_img_online_resize:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 3
|
||||||
|
run: /root/vitastor/tests/test_img_online_resize.sh
|
||||||
|
- name: Print logs
|
||||||
|
if: always() && steps.test.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
|
||||||
|
echo "-------- $i --------"
|
||||||
|
cat $i
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
test_no_uring:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 3
|
||||||
|
run: /root/vitastor/tests/test_no_uring.sh
|
||||||
|
- name: Print logs
|
||||||
|
if: always() && steps.test.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
|
||||||
|
echo "-------- $i --------"
|
||||||
|
cat $i
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
test_interrupted_rebalance:
|
test_interrupted_rebalance:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
needs: build
|
||||||
@@ -360,6 +419,78 @@ jobs:
|
|||||||
echo ""
|
echo ""
|
||||||
done
|
done
|
||||||
|
|
||||||
|
test_dump_load:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 3
|
||||||
|
run: /root/vitastor/tests/test_dump_load.sh
|
||||||
|
- name: Print logs
|
||||||
|
if: always() && steps.test.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
|
||||||
|
echo "-------- $i --------"
|
||||||
|
cat $i
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
test_dump_load_32k:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 3
|
||||||
|
run: TEST_NAME=32k OSD_ARGS="--data_csum_type crc32c --csum_block_size 32k" OFFSET_ARGS="$OSD_ARGS" /root/vitastor/tests/test_dump_load.sh
|
||||||
|
- name: Print logs
|
||||||
|
if: always() && steps.test.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
|
||||||
|
echo "-------- $i --------"
|
||||||
|
cat $i
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
test_old_dump_load:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 3
|
||||||
|
run: OLD=1 /root/vitastor/tests/test_dump_load.sh
|
||||||
|
- name: Print logs
|
||||||
|
if: always() && steps.test.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
|
||||||
|
echo "-------- $i --------"
|
||||||
|
cat $i
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
test_dump_load_old_32k:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 3
|
||||||
|
run: TEST_NAME=old_32k OLD=1 OSD_ARGS="--data_csum_type crc32c --csum_block_size 32k" OFFSET_ARGS="$OSD_ARGS" /root/vitastor/tests/test_dump_load.sh
|
||||||
|
- name: Print logs
|
||||||
|
if: always() && steps.test.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
|
||||||
|
echo "-------- $i --------"
|
||||||
|
cat $i
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
test_old_interrupted_rebalance:
|
test_old_interrupted_rebalance:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
needs: build
|
||||||
@@ -720,6 +851,24 @@ jobs:
|
|||||||
echo ""
|
echo ""
|
||||||
done
|
done
|
||||||
|
|
||||||
|
test_snapshot_chain_enc_gcm:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 3
|
||||||
|
run: TEST_NAME=enc_gcm ENCRYPTED=1 VITASTOR_CFG=',"proto_checksums":"gcm"' /root/vitastor/tests/test_snapshot_chain.sh
|
||||||
|
- name: Print logs
|
||||||
|
if: always() && steps.test.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
|
||||||
|
echo "-------- $i --------"
|
||||||
|
cat $i
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
test_old_snapshot_chain:
|
test_old_snapshot_chain:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
needs: build
|
||||||
@@ -1278,6 +1427,24 @@ jobs:
|
|||||||
echo ""
|
echo ""
|
||||||
done
|
done
|
||||||
|
|
||||||
|
test_heal_ec_rdma:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 10
|
||||||
|
run: TEST_NAME=ec_rdma RDMA=1 SCHEME=ec /root/vitastor/tests/test_heal.sh
|
||||||
|
- name: Print logs
|
||||||
|
if: always() && steps.test.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
|
||||||
|
echo "-------- $i --------"
|
||||||
|
cat $i
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
test_checksum:
|
test_checksum:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
needs: build
|
||||||
@@ -1548,6 +1715,24 @@ jobs:
|
|||||||
echo ""
|
echo ""
|
||||||
done
|
done
|
||||||
|
|
||||||
|
test_resize_last:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 3
|
||||||
|
run: /root/vitastor/tests/test_resize_last.sh
|
||||||
|
- name: Print logs
|
||||||
|
if: always() && steps.test.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
|
||||||
|
echo "-------- $i --------"
|
||||||
|
cat $i
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
test_resize_auto:
|
test_resize_auto:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
needs: build
|
||||||
@@ -1584,6 +1769,24 @@ jobs:
|
|||||||
echo ""
|
echo ""
|
||||||
done
|
done
|
||||||
|
|
||||||
|
test_old_resize_last:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 3
|
||||||
|
run: OLD=1 /root/vitastor/tests/test_resize_last.sh
|
||||||
|
- name: Print logs
|
||||||
|
if: always() && steps.test.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
|
||||||
|
echo "-------- $i --------"
|
||||||
|
cat $i
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
test_old_resize_auto:
|
test_old_resize_auto:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
needs: build
|
||||||
@@ -2142,6 +2345,24 @@ jobs:
|
|||||||
echo ""
|
echo ""
|
||||||
done
|
done
|
||||||
|
|
||||||
|
test_nfs_long_readdir:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 3
|
||||||
|
run: /root/vitastor/tests/test_nfs_long_readdir.sh
|
||||||
|
- name: Print logs
|
||||||
|
if: always() && steps.test.outcome == 'failure'
|
||||||
|
run: |
|
||||||
|
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
|
||||||
|
echo "-------- $i --------"
|
||||||
|
cat $i
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
test_write_encrypted:
|
test_write_encrypted:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
needs: build
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8...3.30)
|
|||||||
|
|
||||||
project(vitastor)
|
project(vitastor)
|
||||||
|
|
||||||
set(VITASTOR_VERSION "3.0.9")
|
set(VITASTOR_VERSION "3.2.1")
|
||||||
|
|
||||||
include(CTest)
|
include(CTest)
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ Vitastor поддерживает QEMU-драйвер, протоколы UBLK,
|
|||||||
- [NBD](docs/usage/nbd.ru.md) - старый интерфейс для монтирования ядром
|
- [NBD](docs/usage/nbd.ru.md) - старый интерфейс для монтирования ядром
|
||||||
- [QEMU, qemu-img и VDUSE](docs/usage/qemu.ru.md)
|
- [QEMU, qemu-img и VDUSE](docs/usage/qemu.ru.md)
|
||||||
- [NFS](docs/usage/nfs.ru.md) кластерная файловая система и псевдо-ФС прокси
|
- [NFS](docs/usage/nfs.ru.md) кластерная файловая система и псевдо-ФС прокси
|
||||||
|
- [Безопасность](docs/usage/security.ru.md)
|
||||||
- [Администрирование](docs/usage/admin.ru.md)
|
- [Администрирование](docs/usage/admin.ru.md)
|
||||||
- Производительность
|
- Производительность
|
||||||
- [Понимание сути производительности](docs/performance/understanding.ru.md)
|
- [Понимание сути производительности](docs/performance/understanding.ru.md)
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ Read more details in the documentation. You can start from here: [Quick Start](d
|
|||||||
- [NBD](docs/usage/nbd.en.md) - old interface for kernel mounts
|
- [NBD](docs/usage/nbd.en.md) - old interface for kernel mounts
|
||||||
- [QEMU, qemu-img and VDUSE](docs/usage/qemu.en.md)
|
- [QEMU, qemu-img and VDUSE](docs/usage/qemu.en.md)
|
||||||
- [NFS](docs/usage/nfs.en.md) clustered file system and pseudo-FS proxy
|
- [NFS](docs/usage/nfs.en.md) clustered file system and pseudo-FS proxy
|
||||||
|
- [Security](docs/usage/security.en.md)
|
||||||
- [Administration](docs/usage/admin.en.md)
|
- [Administration](docs/usage/admin.en.md)
|
||||||
- Performance
|
- Performance
|
||||||
- [Understanding storage performance](docs/performance/understanding.en.md)
|
- [Understanding storage performance](docs/performance/understanding.en.md)
|
||||||
|
|||||||
Submodule cpp-btree updated: ebe44c9b66...bea37ef717
@@ -37,8 +37,8 @@ RUN (echo deb http://vitastor.io/debian trixie main > /etc/apt/sources.list.d/vi
|
|||||||
wget -q -O /etc/apt/trusted.gpg.d/vitastor.gpg https://vitastor.io/debian/pubkey.gpg && \
|
wget -q -O /etc/apt/trusted.gpg.d/vitastor.gpg https://vitastor.io/debian/pubkey.gpg && \
|
||||||
apt-get update && \
|
apt-get update && \
|
||||||
apt-get install -y vitastor-client ibverbs-providers && \
|
apt-get install -y vitastor-client ibverbs-providers && \
|
||||||
wget https://vitastor.io/archive/qemu/qemu-trixie-10.0.2%2Bds-2%2Bvitastor1/qemu-utils_10.0.2%2Bds-2%2Bvitastor1_amd64.deb && \
|
wget https://vitastor.io/archive/qemu/qemu-trixie-10.0.11%2Bds-0%2Bdeb13u1%2Bvitastor2/qemu-utils_10.0.11%2Bds-0%2Bdeb13u1%2Bvitastor2_amd64.deb && \
|
||||||
wget https://vitastor.io/archive/qemu/qemu-trixie-10.0.2%2Bds-2%2Bvitastor1/qemu-block-extra_10.0.2%2Bds-2%2Bvitastor1_amd64.deb && \
|
wget https://vitastor.io/archive/qemu/qemu-trixie-10.0.11%2Bds-0%2Bdeb13u1%2Bvitastor2/qemu-block-extra_10.0.11%2Bds-0%2Bdeb13u1%2Bvitastor2_amd64.deb && \
|
||||||
dpkg -x qemu-utils*.deb tmp1 && \
|
dpkg -x qemu-utils*.deb tmp1 && \
|
||||||
dpkg -x qemu-block-extra*.deb tmp1 && \
|
dpkg -x qemu-block-extra*.deb tmp1 && \
|
||||||
cp -a tmp1/usr/bin/qemu-storage-daemon /usr/bin/ && \
|
cp -a tmp1/usr/bin/qemu-storage-daemon /usr/bin/ && \
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ RUN apt-get update && \
|
|||||||
# NFS mount dependencies
|
# NFS mount dependencies
|
||||||
nfs-common netbase \
|
nfs-common netbase \
|
||||||
# dependencies of qemu-storage-daemon
|
# dependencies of qemu-storage-daemon
|
||||||
libnuma1 liburing2 libglib2.0-0 libfuse3-3 libaio1 libzstd1 libnettle8 \
|
libaio1t64 libc6 libfuse3-4 libglib2.0-0t64 libgmp10 libgnutls30t64 \
|
||||||
libgmp10 libhogweed6 libp11-kit0 libidn2-0 libunistring2 libtasn1-6 libpcre2-8-0 libffi8 && \
|
libhogweed6t64 libnettle8t64 libnuma1 libselinux1 liburing2 libzstd1 zlib1g && \
|
||||||
apt-get clean && \
|
apt-get clean && \
|
||||||
(echo options nbd nbds_max=128 > /etc/modprobe.d/nbd.conf)
|
(echo options nbd nbds_max=128 > /etc/modprobe.d/nbd.conf)
|
||||||
|
|
||||||
@@ -36,8 +36,8 @@ ADD deb /deb
|
|||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get -y install /deb/vitastor-client_*.deb && \
|
apt-get -y install /deb/vitastor-client_*.deb && \
|
||||||
wget https://vitastor.io/archive/qemu/qemu-trixie-9.2.2%2Bds-1%2Bvitastor4/qemu-utils_9.2.2%2Bds-1%2Bvitastor4_amd64.deb && \
|
wget https://vitastor.io/archive/qemu/qemu-trixie-10.0.11%2Bds-0%2Bdeb13u1%2Bvitastor2/qemu-utils_10.0.11%2Bds-0%2Bdeb13u1%2Bvitastor2_amd64.deb && \
|
||||||
wget https://vitastor.io/archive/qemu/qemu-trixie-9.2.2%2Bds-1%2Bvitastor4/qemu-block-extra_9.2.2%2Bds-1%2Bvitastor4_amd64.deb && \
|
wget https://vitastor.io/archive/qemu/qemu-trixie-10.0.11%2Bds-0%2Bdeb13u1%2Bvitastor2/qemu-block-extra_10.0.11%2Bds-0%2Bdeb13u1%2Bvitastor2_amd64.deb && \
|
||||||
dpkg -x qemu-utils*.deb tmp1 && \
|
dpkg -x qemu-utils*.deb tmp1 && \
|
||||||
dpkg -x qemu-block-extra*.deb tmp1 && \
|
dpkg -x qemu-block-extra*.deb tmp1 && \
|
||||||
cp -a tmp1/usr/bin/qemu-storage-daemon /usr/bin/ && \
|
cp -a tmp1/usr/bin/qemu-storage-daemon /usr/bin/ && \
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
VITASTOR_VERSION ?= v3.0.9
|
VITASTOR_VERSION ?= v3.2.1
|
||||||
|
|
||||||
all: build push
|
all: build push
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ spec:
|
|||||||
capabilities:
|
capabilities:
|
||||||
add: ["SYS_ADMIN"]
|
add: ["SYS_ADMIN"]
|
||||||
allowPrivilegeEscalation: true
|
allowPrivilegeEscalation: true
|
||||||
image: vitalif/vitastor-csi:v3.0.9
|
image: vitalif/vitastor-csi:v3.2.1
|
||||||
args:
|
args:
|
||||||
- "--node=$(NODE_ID)"
|
- "--node=$(NODE_ID)"
|
||||||
- "--endpoint=$(CSI_ENDPOINT)"
|
- "--endpoint=$(CSI_ENDPOINT)"
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ spec:
|
|||||||
- name: socket-dir
|
- name: socket-dir
|
||||||
mountPath: /csi
|
mountPath: /csi
|
||||||
- name: csi-attacher
|
- name: csi-attacher
|
||||||
image: k8s.gcr.io/sig-storage/csi-attacher:v3.1.0
|
image: k8s.gcr.io/sig-storage/csi-attacher:v3.2.1
|
||||||
args:
|
args:
|
||||||
- "--v=5"
|
- "--v=5"
|
||||||
- "--csi-address=$(ADDRESS)"
|
- "--csi-address=$(ADDRESS)"
|
||||||
@@ -121,7 +121,7 @@ spec:
|
|||||||
privileged: true
|
privileged: true
|
||||||
capabilities:
|
capabilities:
|
||||||
add: ["SYS_ADMIN"]
|
add: ["SYS_ADMIN"]
|
||||||
image: vitalif/vitastor-csi:v3.0.9
|
image: vitalif/vitastor-csi:v3.2.1
|
||||||
args:
|
args:
|
||||||
- "--node=$(NODE_ID)"
|
- "--node=$(NODE_ID)"
|
||||||
- "--endpoint=$(CSI_ENDPOINT)"
|
- "--endpoint=$(CSI_ENDPOINT)"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ require (
|
|||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
|
||||||
github.com/kubernetes-csi/csi-lib-utils v0.9.1
|
github.com/kubernetes-csi/csi-lib-utils v0.9.1
|
||||||
golang.org/x/net v0.7.0
|
golang.org/x/net v0.7.0
|
||||||
|
golang.org/x/sys v0.5.0 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
google.golang.org/grpc v1.33.1
|
google.golang.org/grpc v1.33.1
|
||||||
google.golang.org/protobuf v1.24.0
|
google.golang.org/protobuf v1.24.0
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ package vitastor
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
vitastorCSIDriverName = "csi.vitastor.io"
|
vitastorCSIDriverName = "csi.vitastor.io"
|
||||||
vitastorCSIDriverVersion = "3.0.9"
|
vitastorCSIDriverVersion = "3.2.1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config struct fills the parameters of request or user input
|
// Config struct fills the parameters of request or user input
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
"os"
|
"os"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
||||||
@@ -27,7 +26,6 @@ const (
|
|||||||
MB int64 = 1024 * KB
|
MB int64 = 1024 * KB
|
||||||
GB int64 = 1024 * MB
|
GB int64 = 1024 * MB
|
||||||
TB int64 = 1024 * GB
|
TB int64 = 1024 * GB
|
||||||
ETCD_TIMEOUT time.Duration = 15*time.Second
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type InodeIndex struct
|
type InodeIndex struct
|
||||||
@@ -687,7 +685,7 @@ func (cs *ControllerServer) ControllerExpandVolume(ctx context.Context, req *csi
|
|||||||
|
|
||||||
return &csi.ControllerExpandVolumeResponse{
|
return &csi.ControllerExpandVolumeResponse{
|
||||||
CapacityBytes: int64(inodeCfg[0].Size),
|
CapacityBytes: int64(inodeCfg[0].Size),
|
||||||
NodeExpansionRequired: false,
|
NodeExpansionRequired: true,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,18 +96,16 @@ func NewNodeServer(driver *Driver) *NodeServer
|
|||||||
{
|
{
|
||||||
ns.restoreUblkDaemons()
|
ns.restoreUblkDaemons()
|
||||||
}
|
}
|
||||||
if (ns.method == MOUNT_VDUSE || ns.method == MOUNT_UBLK)
|
ns.restoreNfsDaemons()
|
||||||
|
dur, err := time.ParseDuration(os.Getenv("RESTART_INTERVAL"))
|
||||||
|
if (err != nil)
|
||||||
{
|
{
|
||||||
dur, err := time.ParseDuration(os.Getenv("RESTART_INTERVAL"))
|
dur = 10 * time.Second
|
||||||
if (err != nil)
|
}
|
||||||
{
|
ns.restartInterval = dur
|
||||||
dur = 10 * time.Second
|
if (ns.restartInterval != time.Duration(0))
|
||||||
}
|
{
|
||||||
ns.restartInterval = dur
|
go ns.restarter()
|
||||||
if (ns.restartInterval != time.Duration(0))
|
|
||||||
{
|
|
||||||
go ns.restarter()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ns
|
return ns
|
||||||
}
|
}
|
||||||
@@ -134,10 +132,8 @@ func (ns *NodeServer) unlockVolume(lockId string)
|
|||||||
|
|
||||||
func (ns *NodeServer) restarter()
|
func (ns *NodeServer) restarter()
|
||||||
{
|
{
|
||||||
// Restart dead VDUSE daemons at regular intervals
|
// Restart dead storage daemons at regular intervals. Otherwise volume I/O
|
||||||
// Otherwise volume I/O may hang in case of a qemu-storage-daemon crash
|
// may hang and may even lead to a kernel panic if panic_on_hung_task is set.
|
||||||
// Moreover, it may lead to a kernel panic of the kernel is configured to
|
|
||||||
// panic on hung tasks
|
|
||||||
ticker := time.NewTicker(ns.restartInterval)
|
ticker := time.NewTicker(ns.restartInterval)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
for
|
for
|
||||||
@@ -151,6 +147,7 @@ func (ns *NodeServer) restarter()
|
|||||||
{
|
{
|
||||||
ns.restoreUblkDaemons()
|
ns.restoreUblkDaemons()
|
||||||
}
|
}
|
||||||
|
ns.restoreNfsDaemons()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,15 +326,10 @@ func (ns *NodeServer) restoreNfsDaemons()
|
|||||||
{
|
{
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
activeNFS, err := ns.listActiveNFS()
|
|
||||||
if (err != nil)
|
|
||||||
{
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Check all state files and try to restore active mounts
|
// Check all state files and try to restore active mounts
|
||||||
for _, stateFile := range stateFiles
|
for _, stateFile := range stateFiles
|
||||||
{
|
{
|
||||||
ns.checkNfsState(stateFile, activeNFS)
|
ns.checkNfsState(stateFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,24 +355,43 @@ func (ns *NodeServer) readNfsState(stateFile string, allowNotExists bool) (*NfsS
|
|||||||
return &state, nil
|
return &state, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *NodeServer) checkNfsState(stateFile string, activeNfs map[int][]string)
|
func (ns *NodeServer) checkNfsState(stateFile string)
|
||||||
{
|
{
|
||||||
// Read state file
|
// Read state file to determine the lock ID
|
||||||
state, err := ns.readNfsState(stateFile, false)
|
state, err := ns.readNfsState(stateFile, false)
|
||||||
if (err != nil)
|
if (err != nil)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Lock FS
|
// Lock FS
|
||||||
ns.lockVolume(state.ConfigPath+":fs:"+state.FsName)
|
lockId := state.ConfigPath+":fs:"+state.FsName
|
||||||
defer ns.unlockVolume(state.ConfigPath+":fs:"+state.FsName)
|
ns.lockVolume(lockId)
|
||||||
// Check if NFS at this port is still mounted
|
defer ns.unlockVolume(lockId)
|
||||||
|
// Recheck state and mounts under the lock: publish/unpublish may have changed
|
||||||
|
// both while this recovery attempt was waiting for the lock.
|
||||||
|
state, err = ns.readNfsState(stateFile, true)
|
||||||
|
if (err != nil || state == nil)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
}
|
||||||
|
activeNfs, err := ns.listActiveNFS()
|
||||||
|
if (err != nil)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
}
|
||||||
pidFile := ns.stateDir + filepath.Base(stateFile)
|
pidFile := ns.stateDir + filepath.Base(stateFile)
|
||||||
pidFile = pidFile[0:len(pidFile)-5] + ".pid"
|
pidFile = pidFile[0:len(pidFile)-5] + ".pid"
|
||||||
if (len(activeNfs[state.Port]) == 0)
|
if (len(activeNfs[state.Port]) == 0)
|
||||||
{
|
{
|
||||||
// this is a stale state file, remove it
|
// This is a stale state file. Remove the root staging mount before
|
||||||
|
// stopping the daemon, otherwise a dead hidden NFS mount may remain.
|
||||||
klog.Warningf("state file %v contains stale mount at port %d, removing it", stateFile, state.Port)
|
klog.Warningf("state file %v contains stale mount at port %d, removing it", stateFile, state.Port)
|
||||||
|
err = mount.CleanupMountPoint(state.Path, ns.mounter, false)
|
||||||
|
if (err != nil)
|
||||||
|
{
|
||||||
|
klog.Errorf("failed to unmount stale NFS mount %v: %v", state.Path, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
ns.stopNFS(stateFile, pidFile)
|
ns.stopNFS(stateFile, pidFile)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -400,6 +411,7 @@ func (ns *NodeServer) checkNfsState(stateFile string, activeNfs map[int][]string
|
|||||||
"--pidfile", pidFile,
|
"--pidfile", pidFile,
|
||||||
"--bind", "127.0.0.1",
|
"--bind", "127.0.0.1",
|
||||||
"--port", fmt.Sprintf("%d", state.Port),
|
"--port", fmt.Sprintf("%d", state.Port),
|
||||||
|
"--config_path", state.ConfigPath,
|
||||||
"--fs", state.FsName,
|
"--fs", state.FsName,
|
||||||
"--pool", state.Pool,
|
"--pool", state.Pool,
|
||||||
"--portmap", "0",
|
"--portmap", "0",
|
||||||
@@ -725,6 +737,7 @@ func (ns *NodeServer) mountNFS(ctxVars map[string]string) (string, error)
|
|||||||
"--pidfile", pidFile,
|
"--pidfile", pidFile,
|
||||||
"--bind", "127.0.0.1",
|
"--bind", "127.0.0.1",
|
||||||
"--port", "auto",
|
"--port", "auto",
|
||||||
|
"--config_path", state.ConfigPath,
|
||||||
"--fs", state.FsName,
|
"--fs", state.FsName,
|
||||||
"--pool", state.Pool,
|
"--pool", state.Pool,
|
||||||
"--portmap", "0",
|
"--portmap", "0",
|
||||||
@@ -754,7 +767,7 @@ func (ns *NodeServer) mountNFS(ctxVars map[string]string) (string, error)
|
|||||||
// Mount NFS
|
// Mount NFS
|
||||||
_, _, err = system(
|
_, _, err = system(
|
||||||
"mount", "-t", "nfs", "127.0.0.1:/", state.Path,
|
"mount", "-t", "nfs", "127.0.0.1:/", state.Path,
|
||||||
"-o", fmt.Sprintf("port=%d,mountport=%d,nfsvers=3,soft,nolock,tcp", port, port),
|
"-o", fmt.Sprintf("port=%d,mountport=%d,nfsvers=3,soft,sync,nolock,tcp", port, port),
|
||||||
)
|
)
|
||||||
if (err != nil)
|
if (err != nil)
|
||||||
{
|
{
|
||||||
@@ -815,26 +828,38 @@ func (ns *NodeServer) listActiveNFS() (map[int][]string, error)
|
|||||||
klog.Errorf("failed to list mounts: %v", err)
|
klog.Errorf("failed to list mounts: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
return collectActiveNFS(mounts), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectActiveNFS(mounts []mount.MountInfo) map[int][]string
|
||||||
|
{
|
||||||
activeNFS := make(map[int][]string)
|
activeNFS := make(map[int][]string)
|
||||||
for _, mount := range mounts
|
for _, mnt := range mounts
|
||||||
{
|
{
|
||||||
// Volume mounts always refer to subpaths
|
// Only count PVC subdirectory mounts, not the root staging mount. Newer
|
||||||
if (mount.FsType == "nfs" && mount.Root != "/")
|
// kernels expose the subdirectory in Source while older ones expose it
|
||||||
|
// in Root. The staging mount may also be visible through several bind
|
||||||
|
// aliases in the CSI container, so its mount point isn't a reliable
|
||||||
|
// discriminator.
|
||||||
|
if (mnt.FsType != "nfs" || !strings.HasPrefix(mnt.Source, "127.0.0.1:/") ||
|
||||||
|
(mnt.Source == "127.0.0.1:/" && mnt.Root == "/"))
|
||||||
{
|
{
|
||||||
for _, opt := range mount.MountOptions
|
continue
|
||||||
|
}
|
||||||
|
// NFS-specific options, including port, are per-superblock options.
|
||||||
|
for _, opt := range mnt.SuperOptions
|
||||||
|
{
|
||||||
|
if (strings.HasPrefix(opt, "port="))
|
||||||
{
|
{
|
||||||
if (strings.HasPrefix(opt, "port="))
|
port64, err := strconv.ParseUint(opt[5:], 10, 16)
|
||||||
|
if (err == nil)
|
||||||
{
|
{
|
||||||
port64, err := strconv.ParseUint(opt[5:], 10, 16)
|
activeNFS[int(port64)] = append(activeNFS[int(port64)], mnt.MountPoint)
|
||||||
if (err == nil)
|
|
||||||
{
|
|
||||||
activeNFS[int(port64)] = append(activeNFS[int(port64)], mount.MountPoint)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return activeNFS, nil
|
return activeNFS
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodePublishVolume mounts the volume mounted to the staging path to the target path
|
// NodePublishVolume mounts the volume mounted to the staging path to the target path
|
||||||
@@ -1030,10 +1055,131 @@ func (ns *NodeServer) NodeGetVolumeStats(ctx context.Context, req *csi.NodeGetVo
|
|||||||
return nil, status.Error(codes.Unimplemented, "")
|
return nil, status.Error(codes.Unimplemented, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeExpandVolume expanding the file system on the node
|
// NodeExpandVolume expands the block device and, for filesystem volumes, the filesystem on the node
|
||||||
func (ns *NodeServer) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandVolumeRequest) (*csi.NodeExpandVolumeResponse, error)
|
func (ns *NodeServer) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandVolumeRequest) (*csi.NodeExpandVolumeResponse, error)
|
||||||
{
|
{
|
||||||
return nil, status.Error(codes.Unimplemented, "")
|
klog.Infof("received node expand volume request %+v", protosanitizer.StripSecrets(req))
|
||||||
|
if (req == nil)
|
||||||
|
{
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "request cannot be empty")
|
||||||
|
}
|
||||||
|
if (req.VolumeId == "")
|
||||||
|
{
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "VolumeId is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctxVars := make(map[string]string)
|
||||||
|
err := json.Unmarshal([]byte(req.VolumeId), &ctxVars)
|
||||||
|
if (err != nil)
|
||||||
|
{
|
||||||
|
return nil, status.Error(codes.Internal, "volume ID not in JSON format")
|
||||||
|
}
|
||||||
|
volName := ctxVars["name"]
|
||||||
|
|
||||||
|
if (ctxVars["vitastorfs"] != "")
|
||||||
|
{
|
||||||
|
// VitastorFS CSI volumes are NFS-backed and currently don't have per-volume quotas to resize here.
|
||||||
|
return &csi.NodeExpandVolumeResponse{CapacityBytes: req.GetCapacityRange().GetRequiredBytes()}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ns.lockVolume(ctxVars["configPath"]+":block:"+volName)
|
||||||
|
defer ns.unlockVolume(ctxVars["configPath"]+":block:"+volName)
|
||||||
|
|
||||||
|
targetPath := req.GetStagingTargetPath()
|
||||||
|
if (targetPath == "")
|
||||||
|
{
|
||||||
|
targetPath = req.GetVolumePath()
|
||||||
|
}
|
||||||
|
if (targetPath == "")
|
||||||
|
{
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "VolumePath or StagingTargetPath is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
devicePath, err := GetDeviceNameFromMount(targetPath)
|
||||||
|
if (err != nil)
|
||||||
|
{
|
||||||
|
if (os.IsNotExist(err))
|
||||||
|
{
|
||||||
|
return nil, status.Error(codes.NotFound, "Target path not found")
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if (devicePath == "")
|
||||||
|
{
|
||||||
|
return nil, status.Errorf(codes.NotFound, "%s is not a mountpoint", targetPath)
|
||||||
|
}
|
||||||
|
if (devicePath[0] != '/')
|
||||||
|
{
|
||||||
|
return nil, status.Errorf(codes.Internal, "failed to resolve device for %s: %s", targetPath, devicePath)
|
||||||
|
}
|
||||||
|
if (len(devicePath) < 5 || devicePath[0:5] != "/dev/")
|
||||||
|
{
|
||||||
|
// NodeExpandVolume may be called with the published target path. For block volumes it is
|
||||||
|
// usually a bind mount of the staging target, so resolve one more mount layer if needed.
|
||||||
|
var realDevicePath string
|
||||||
|
realDevicePath, err = GetDeviceNameFromMount(devicePath)
|
||||||
|
if (err == nil && realDevicePath != "")
|
||||||
|
{
|
||||||
|
devicePath = realDevicePath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All newest Vitastor driver versions (nbd, ublk and vduse) support online resize.
|
||||||
|
// So all we need to do is just wait a bit until the device is resized by the driver.
|
||||||
|
requiredBytes := req.GetCapacityRange().GetRequiredBytes()
|
||||||
|
if (requiredBytes > 0)
|
||||||
|
{
|
||||||
|
timeout := 30
|
||||||
|
var actualSize uint64
|
||||||
|
var err error
|
||||||
|
for i := 0; i < timeout && actualSize < uint64(requiredBytes); i++
|
||||||
|
{
|
||||||
|
actualSize, err = GetBlockDeviceSize(devicePath)
|
||||||
|
if (err != nil)
|
||||||
|
{
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
if (actualSize < uint64(requiredBytes))
|
||||||
|
{
|
||||||
|
return nil, status.Errorf(codes.NotFound, "Failed to wait for online resize of %s to %v bytes: actual size is still %v after %v seconds",
|
||||||
|
devicePath, requiredBytes, actualSize, timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isBlock := req.GetVolumeCapability().GetBlock() != nil
|
||||||
|
if (isBlock)
|
||||||
|
{
|
||||||
|
return &csi.NodeExpandVolumeResponse{CapacityBytes: requiredBytes}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
diskMounter := &mount.SafeFormatAndMount{Interface: ns.mounter, Exec: utilexec.New()}
|
||||||
|
fsType := req.GetVolumeCapability().GetMount().GetFsType()
|
||||||
|
if (fsType == "")
|
||||||
|
{
|
||||||
|
fsType, err = diskMounter.GetDiskFormat(devicePath)
|
||||||
|
if (err != nil)
|
||||||
|
{
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (fsType)
|
||||||
|
{
|
||||||
|
case "ext4":
|
||||||
|
_, err = systemCombined("resize2fs", devicePath)
|
||||||
|
case "xfs":
|
||||||
|
_, err = systemCombined("xfs_growfs", targetPath)
|
||||||
|
default:
|
||||||
|
return nil, status.Errorf(codes.InvalidArgument, "unsupported filesystem for resize: %s", fsType)
|
||||||
|
}
|
||||||
|
if (err != nil)
|
||||||
|
{
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &csi.NodeExpandVolumeResponse{CapacityBytes: requiredBytes}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodeGetCapabilities returns the supported capabilities of the node server
|
// NodeGetCapabilities returns the supported capabilities of the node server
|
||||||
@@ -1048,6 +1194,13 @@ func (ns *NodeServer) NodeGetCapabilities(ctx context.Context, req *csi.NodeGetC
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
&csi.NodeServiceCapability{
|
||||||
|
Type: &csi.NodeServiceCapability_Rpc{
|
||||||
|
Rpc: &csi.NodeServiceCapability_RPC{
|
||||||
|
Type: csi.NodeServiceCapability_RPC_EXPAND_VOLUME,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
73
csi/src/nodeserver_test.go
Normal file
73
csi/src/nodeserver_test.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
// Copyright (c) Vitaliy Filippov, 2019+
|
||||||
|
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||||
|
|
||||||
|
package vitastor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"k8s.io/utils/mount"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCollectActiveNFS(t *testing.T)
|
||||||
|
{
|
||||||
|
mounts := []mount.MountInfo{
|
||||||
|
{
|
||||||
|
Root: "/",
|
||||||
|
Source: "127.0.0.1:/",
|
||||||
|
MountPoint: "/var/lib/kubelet/plugins/csi.vitastor.io/nfs/hash",
|
||||||
|
FsType: "nfs",
|
||||||
|
MountOptions: []string{"rw", "relatime"},
|
||||||
|
SuperOptions: []string{"rw", "vers=3", "port=45059"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// This is how NFS subdirectory bind mounts appear on Linux 6.19:
|
||||||
|
// Root is still / and the subdirectory is only visible in Source.
|
||||||
|
Root: "/",
|
||||||
|
Source: "127.0.0.1:/pvc-one",
|
||||||
|
MountPoint: "/var/lib/kubelet/pods/pod-one/volumes/kubernetes.io~csi/pvc-one/mount",
|
||||||
|
FsType: "nfs",
|
||||||
|
MountOptions: []string{"rw", "relatime"},
|
||||||
|
SuperOptions: []string{"rw", "vers=3", "port=45059"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Root: "/",
|
||||||
|
Source: "127.0.0.1:/pvc-two",
|
||||||
|
MountPoint: "/var/lib/kubelet/pods/pod-two/volumes/kubernetes.io~csi/pvc-two/mount",
|
||||||
|
FsType: "nfs",
|
||||||
|
MountOptions: []string{"rw", "relatime"},
|
||||||
|
SuperOptions: []string{"rw", "vers=3", "port=45059"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Older kernels expose the bind-mounted subdirectory in Root.
|
||||||
|
Root: "/pvc-three",
|
||||||
|
Source: "127.0.0.1:/",
|
||||||
|
MountPoint: "/var/lib/kubelet/pods/pod-three/volumes/kubernetes.io~csi/pvc-three/mount",
|
||||||
|
FsType: "nfs",
|
||||||
|
MountOptions: []string{"rw", "relatime"},
|
||||||
|
SuperOptions: []string{"rw", "vers=3", "port=45059"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Root: "/",
|
||||||
|
Source: "server:/other",
|
||||||
|
MountPoint: "/mnt/other",
|
||||||
|
FsType: "nfs",
|
||||||
|
MountOptions: []string{"rw"},
|
||||||
|
SuperOptions: []string{"rw", "port=2049"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
got := collectActiveNFS(mounts)
|
||||||
|
want := map[int][]string{
|
||||||
|
45059: {
|
||||||
|
"/var/lib/kubelet/pods/pod-one/volumes/kubernetes.io~csi/pvc-one/mount",
|
||||||
|
"/var/lib/kubelet/pods/pod-two/volumes/kubernetes.io~csi/pvc-two/mount",
|
||||||
|
"/var/lib/kubelet/pods/pod-three/volumes/kubernetes.io~csi/pvc-three/mount",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if (!reflect.DeepEqual(got, want))
|
||||||
|
{
|
||||||
|
t.Fatalf("unexpected active NFS mounts: got %#v, want %#v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
|
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MountMethod int
|
type MountMethod int
|
||||||
@@ -519,3 +520,22 @@ func GetDeviceNameFromMount(mountPath string) (string, error)
|
|||||||
|
|
||||||
return device, nil
|
return device, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetBlockDeviceSize(devicePath string) (uint64, error)
|
||||||
|
{
|
||||||
|
file, err := os.Open(devicePath)
|
||||||
|
if (err != nil)
|
||||||
|
{
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// BLKGETSIZE64 returns the size in bytes as a uint64/int64
|
||||||
|
size, err := unix.IoctlGetInt(int(file.Fd()), unix.BLKGETSIZE64)
|
||||||
|
if (err != nil)
|
||||||
|
{
|
||||||
|
return 0, fmt.Errorf("ioctl BLKGETSIZE64 failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint64(size), nil
|
||||||
|
}
|
||||||
|
|||||||
2
debian/changelog
vendored
2
debian/changelog
vendored
@@ -1,4 +1,4 @@
|
|||||||
vitastor (3.0.9-1) unstable; urgency=medium
|
vitastor (3.2.1-1) unstable; urgency=medium
|
||||||
|
|
||||||
* Bugfixes
|
* Bugfixes
|
||||||
|
|
||||||
|
|||||||
1
debian/rules
vendored
1
debian/rules
vendored
@@ -11,6 +11,7 @@ override_dh_install:
|
|||||||
cp -v node-binding/package.json node-binding/index.js node-binding/addon.cc node-binding/addon.h node-binding/client.cc node-binding/client.h debian/tmp/usr/lib/x86_64-linux-gnu/nodejs/vitastor
|
cp -v node-binding/package.json node-binding/index.js node-binding/addon.cc node-binding/addon.h node-binding/client.cc node-binding/client.h debian/tmp/usr/lib/x86_64-linux-gnu/nodejs/vitastor
|
||||||
cp -v node-binding/build/Release/addon.node debian/tmp/usr/lib/x86_64-linux-gnu/nodejs/vitastor/build/Release
|
cp -v node-binding/build/Release/addon.node debian/tmp/usr/lib/x86_64-linux-gnu/nodejs/vitastor/build/Release
|
||||||
dh_install
|
dh_install
|
||||||
|
cd debian/vitastor-mon/usr/lib/vitastor/mon && npm install --production
|
||||||
|
|
||||||
override_dh_installdeb:
|
override_dh_installdeb:
|
||||||
cat debian/fio_version >> debian/vitastor-fio.substvars
|
cat debian/fio_version >> debian/vitastor-fio.substvars
|
||||||
|
|||||||
6
debian/vitastor-build.sh
vendored
6
debian/vitastor-build.sh
vendored
@@ -37,12 +37,6 @@ rm -rf a b
|
|||||||
|
|
||||||
echo "dep:fio=$FIO" > debian/fio_version
|
echo "dep:fio=$FIO" > debian/fio_version
|
||||||
|
|
||||||
cd /root/vitastor/packages/vitastor-$REL/vitastor-$VER
|
|
||||||
mkdir mon/node_modules
|
|
||||||
cd mon/node_modules
|
|
||||||
curl -s https://git.yourcmc.ru/vitalif/antietcd/archive/master.tar.gz | tar -zx
|
|
||||||
curl -s https://git.yourcmc.ru/vitalif/tinyraft/archive/master.tar.gz | tar -zx
|
|
||||||
|
|
||||||
cd /root/vitastor/packages/vitastor-$REL
|
cd /root/vitastor/packages/vitastor-$REL
|
||||||
if [[ ( "$REL" = "trixie" || "$REL" = "resolute" ) && -e ../vitastor-bookworm/vitastor_$VER.orig.tar.xz ]]; then
|
if [[ ( "$REL" = "trixie" || "$REL" = "resolute" ) && -e ../vitastor-bookworm/vitastor_$VER.orig.tar.xz ]]; then
|
||||||
# Fucking shit, archives differ between bookworm (xz 5.4.1) and trixie (xz 5.8.1)
|
# Fucking shit, archives differ between bookworm (xz 5.4.1) and trixie (xz 5.8.1)
|
||||||
|
|||||||
12
debian/vitastor-buildenv.Dockerfile
vendored
12
debian/vitastor-buildenv.Dockerfile
vendored
@@ -14,18 +14,18 @@ WORKDIR /root
|
|||||||
RUN set -e -x; \
|
RUN set -e -x; \
|
||||||
if [ "$REL" = "buster" ]; then \
|
if [ "$REL" = "buster" ]; then \
|
||||||
perl -i -pe 's/deb.debian.org/archive.debian.org/' /etc/apt/sources.list; \
|
perl -i -pe 's/deb.debian.org/archive.debian.org/' /etc/apt/sources.list; \
|
||||||
apt-get update; \
|
|
||||||
apt-get -y install wget; \
|
|
||||||
wget https://vitastor.io/debian/pubkey.gpg -O /etc/apt/trusted.gpg.d/vitastor.gpg; \
|
|
||||||
echo "deb https://vitastor.io/debian $REL main" >> /etc/apt/sources.list; \
|
|
||||||
fi; \
|
fi; \
|
||||||
|
apt-get update; \
|
||||||
|
apt-get -y install wget; \
|
||||||
|
wget https://vitastor.io/debian/pubkey.gpg -O /etc/apt/trusted.gpg.d/vitastor.gpg; \
|
||||||
|
echo "deb https://vitastor.io/debian $REL main" >> /etc/apt/sources.list; \
|
||||||
grep '^deb ' /etc/apt/sources.list | perl -pe 's/^deb/deb-src/' >> /etc/apt/sources.list; \
|
grep '^deb ' /etc/apt/sources.list | perl -pe 's/^deb/deb-src/' >> /etc/apt/sources.list; \
|
||||||
perl -i -pe 's/Types: deb$/Types: deb deb-src/' /etc/apt/sources.list.d/*.sources || true; \
|
perl -i -pe 's/Types: deb$/Types: deb deb-src/' /etc/apt/sources.list.d/*.sources || true; \
|
||||||
echo 'APT::Install-Recommends false;' >> /etc/apt/apt.conf; \
|
echo 'APT::Install-Recommends false;' >> /etc/apt/apt.conf; \
|
||||||
echo 'APT::Install-Suggests false;' >> /etc/apt/apt.conf
|
echo 'APT::Install-Suggests false;' >> /etc/apt/apt.conf
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get -y install fio libgoogle-perftools-dev devscripts libjerasure-dev cmake libc-ares-dev \
|
apt-get -y install fio libgoogle-perftools-dev devscripts libjerasure-dev cmake libc-ares-dev libisal-crypto-dev \
|
||||||
libibverbs-dev librdmacm-dev libisal-dev libnl-3-dev libnl-genl-3-dev curl nodejs npm node-nan node-bindings && \
|
libibverbs-dev librdmacm-dev libisal-dev libnl-3-dev libnl-genl-3-dev libssl-dev curl nodejs npm node-nan node-bindings && \
|
||||||
apt-get -y build-dep fio && \
|
apt-get -y build-dep fio && \
|
||||||
apt-get --download-only source fio
|
apt-get --download-only source fio
|
||||||
|
|||||||
2
debian/vitastor-client.install
vendored
2
debian/vitastor-client.install
vendored
@@ -4,6 +4,8 @@ usr/bin/vitastor-rm
|
|||||||
usr/bin/vitastor-nbd
|
usr/bin/vitastor-nbd
|
||||||
usr/bin/vitastor-ublk
|
usr/bin/vitastor-ublk
|
||||||
usr/bin/vitastor-nfs
|
usr/bin/vitastor-nfs
|
||||||
|
usr/sbin/mount.vitastorfs
|
||||||
|
usr/sbin/mount.vitastorblk
|
||||||
usr/bin/vitastor-kv
|
usr/bin/vitastor-kv
|
||||||
usr/bin/vitastor-kv-stress
|
usr/bin/vitastor-kv-stress
|
||||||
usr/lib/*/libvitastor*.so*
|
usr/lib/*/libvitastor*.so*
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
VITASTOR_VERSION ?= v3.0.9
|
VITASTOR_VERSION ?= v3.2.1
|
||||||
|
|
||||||
all: build push
|
all: build push
|
||||||
|
|
||||||
|
|||||||
@@ -12,12 +12,7 @@ EnvironmentFile=/etc/vitastor/etcd.conf
|
|||||||
SyslogIdentifier=etcd
|
SyslogIdentifier=etcd
|
||||||
ExecStart=bash -c 'docker run --rm -i -v /var/lib/vitastor/etcd:/data \
|
ExecStart=bash -c 'docker run --rm -i -v /var/lib/vitastor/etcd:/data \
|
||||||
--log-driver none --network host $CONTAINER_OPTIONS --name vitastor-etcd \
|
--log-driver none --network host $CONTAINER_OPTIONS --name vitastor-etcd \
|
||||||
$ETCD_IMAGE /usr/local/bin/etcd --name "$ETCD_NAME" --data-dir /data \
|
$ETCD_IMAGE /usr/local/bin/etcd --data-dir /data
|
||||||
--snapshot-count 10000 --advertise-client-urls http://$ETCD_IP:2379 --listen-client-urls http://$ETCD_IP:2379 \
|
|
||||||
--initial-advertise-peer-urls http://$ETCD_IP:2380 --listen-peer-urls http://$ETCD_IP:2380 \
|
|
||||||
--initial-cluster-token vitastor-etcd-1 --initial-cluster "$ETCD_INITIAL_CLUSTER" \
|
|
||||||
--initial-cluster-state new --max-txn-ops=100000 --max-request-bytes=104857600 \
|
|
||||||
--auto-compaction-retention=10 --auto-compaction-mode=revision'
|
|
||||||
ExecStop=docker stop vitastor-etcd
|
ExecStop=docker stop vitastor-etcd
|
||||||
Restart=always
|
Restart=always
|
||||||
StartLimitInterval=0
|
StartLimitInterval=0
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
# Desired Vitastor version
|
# Desired Vitastor version
|
||||||
VITASTOR_VERSION=v3.0.9
|
VITASTOR_VERSION=v3.2.1
|
||||||
|
|
||||||
# Additional arguments for all containers
|
# Additional arguments for all containers
|
||||||
# For example, you may want to specify a custom logging driver here
|
# For example, you may want to specify a custom logging driver here
|
||||||
|
|||||||
@@ -1,4 +1 @@
|
|||||||
ETCD_IMAGE=quay.io/coreos/etcd:v3.5.18
|
ETCD_IMAGE=quay.io/coreos/etcd:v3.5.18
|
||||||
ETCD_NAME=""
|
|
||||||
ETCD_IP=""
|
|
||||||
ETCD_INITIAL_CLUSTER=""
|
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ or antietcd_data_dir options). All other antietcd parameters
|
|||||||
cluster, cluster_key, persist_filter, stale_read can also be set in
|
cluster, cluster_key, persist_filter, stale_read can also be set in
|
||||||
Vitastor configuration with `antietcd_` prefix.
|
Vitastor configuration with `antietcd_` prefix.
|
||||||
|
|
||||||
|
See also: [antietcd_cert](security.en.md#antietcd_cert),
|
||||||
|
[antietcd_key](security.en.md#antietcd_key) and [etcd_proxy](security.en.md#etcd_proxyurls).
|
||||||
|
|
||||||
You can dump/load data to or from antietcd using Antietcd `anticli` tool:
|
You can dump/load data to or from antietcd using Antietcd `anticli` tool:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ antietcd_data_file или antietcd_data_dir). Все остальные пара
|
|||||||
node_id, cluster, cluster_key, persist_filter, stale_read также можно задавать
|
node_id, cluster, cluster_key, persist_filter, stale_read также можно задавать
|
||||||
в конфигурации Vitastor с префиксом `antietcd_`.
|
в конфигурации Vitastor с префиксом `antietcd_`.
|
||||||
|
|
||||||
|
Смотрите также настройки [antietcd_cert](security.ru.md#antietcd_cert),
|
||||||
|
[antietcd_key](security.ru.md#antietcd_key) и [etcd_proxy](security.ru.md#etcd_proxyurls).
|
||||||
|
|
||||||
Вы можете выгружать/загружать данные в или из antietcd с помощью его инструмента
|
Вы можете выгружать/загружать данные в или из antietcd с помощью его инструмента
|
||||||
`anticli`:
|
`anticli`:
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ between clients, OSDs and etcd.
|
|||||||
- [etcd_ws_keepalive_interval](#etcd_ws_keepalive_interval)
|
- [etcd_ws_keepalive_interval](#etcd_ws_keepalive_interval)
|
||||||
- [etcd_min_reload_interval](#etcd_min_reload_interval)
|
- [etcd_min_reload_interval](#etcd_min_reload_interval)
|
||||||
- [tcp_header_buffer_size](#tcp_header_buffer_size)
|
- [tcp_header_buffer_size](#tcp_header_buffer_size)
|
||||||
|
- [rxbounce](#rxbounce)
|
||||||
- [min_zerocopy_send_size](#min_zerocopy_send_size)
|
- [min_zerocopy_send_size](#min_zerocopy_send_size)
|
||||||
- [use_sync_send_recv](#use_sync_send_recv)
|
- [use_sync_send_recv](#use_sync_send_recv)
|
||||||
|
|
||||||
@@ -303,6 +304,21 @@ is received without an additional copy. You can try to play with this
|
|||||||
parameter and see how it affects random iops and linear bandwidth if you
|
parameter and see how it affects random iops and linear bandwidth if you
|
||||||
want.
|
want.
|
||||||
|
|
||||||
|
## rxbounce
|
||||||
|
|
||||||
|
- Type: boolean
|
||||||
|
- Default: false
|
||||||
|
- Can be changed online: yes
|
||||||
|
|
||||||
|
Receive all TCP data into an intermediate buffer before copying to the actual
|
||||||
|
destination. Enabling this option is required for Windows guests if you use TCP
|
||||||
|
and [security.en.md#proto_checksums](proto_checksums) because Windows uses a
|
||||||
|
shared system-wide 'dummy page' to skip unneeded blocks during reads and breaks
|
||||||
|
checksum verification with it when data copy is avoided. rxbounce is only needed
|
||||||
|
when you use TCP and don't use transport encryption (AES-GCM); it's not required
|
||||||
|
with RDMA or with GCM encryption because both always copy data anyway (yes, RDMA
|
||||||
|
isn't actually zerocopy in Vitastor).
|
||||||
|
|
||||||
## min_zerocopy_send_size
|
## min_zerocopy_send_size
|
||||||
|
|
||||||
- Type: integer
|
- Type: integer
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
- [etcd_ws_keepalive_interval](#etcd_ws_keepalive_interval)
|
- [etcd_ws_keepalive_interval](#etcd_ws_keepalive_interval)
|
||||||
- [etcd_min_reload_interval](#etcd_min_reload_interval)
|
- [etcd_min_reload_interval](#etcd_min_reload_interval)
|
||||||
- [tcp_header_buffer_size](#tcp_header_buffer_size)
|
- [tcp_header_buffer_size](#tcp_header_buffer_size)
|
||||||
|
- [rxbounce](#rxbounce)
|
||||||
- [min_zerocopy_send_size](#min_zerocopy_send_size)
|
- [min_zerocopy_send_size](#min_zerocopy_send_size)
|
||||||
- [use_sync_send_recv](#use_sync_send_recv)
|
- [use_sync_send_recv](#use_sync_send_recv)
|
||||||
|
|
||||||
@@ -310,6 +311,22 @@ Vitastor содержат 128-байтные заголовки, за котор
|
|||||||
поменять этот параметр и посмотреть, как он влияет на производительность
|
поменять этот параметр и посмотреть, как он влияет на производительность
|
||||||
случайного и линейного доступа.
|
случайного и линейного доступа.
|
||||||
|
|
||||||
|
## rxbounce
|
||||||
|
|
||||||
|
- Тип: булево (да/нет)
|
||||||
|
- Значение по умолчанию: false
|
||||||
|
- Можно менять на лету: да
|
||||||
|
|
||||||
|
Принимать все данные по TCP в промежуточный буфер и потом копировать их в
|
||||||
|
буфер клиента (ВМ). Это нужно для гостевых ОС Windows с TCP и включёнными
|
||||||
|
[security.ru.md#proto_checksums](proto_checksums), потому что Windows использует
|
||||||
|
одну разделяемую общесистемную страницу-заглушку для пропуска ненужных блоков
|
||||||
|
при чтении и ломает проверку контрольных сумм, если данные не копируются лишний
|
||||||
|
раз. Опция rxbounce нужна только если вы используете TCP и не используете
|
||||||
|
транспортное шифрование (AES-GCM); с RDMA или шифрованием опция не нужна, так
|
||||||
|
как в этом случае данные копируются всегда (да, RDMA в Vitastor на самом деле
|
||||||
|
не zero-copy).
|
||||||
|
|
||||||
## min_zerocopy_send_size
|
## min_zerocopy_send_size
|
||||||
|
|
||||||
- Тип: целое число
|
- Тип: целое число
|
||||||
|
|||||||
@@ -10,13 +10,37 @@ These parameters affect your Vitastor installation security and apply to OSDs, m
|
|||||||
|
|
||||||
Most of them can be set in /etc/vitastor/vitastor.conf and in etcd, but don't support online modification.
|
Most of them can be set in /etc/vitastor/vitastor.conf and in etcd, but don't support online modification.
|
||||||
|
|
||||||
|
All certificate and private key parameters mentioned may contain a path to a PEM file or just
|
||||||
|
a PEM string with certificate or a private key. In the latter case, the string must begin with
|
||||||
|
"-----BEGIN CERTIFICATE-----" or "-----BEGIN PRIVATE KEY-----".
|
||||||
|
|
||||||
|
- [use_perms](#use_perms)
|
||||||
|
- [cert](#cert)
|
||||||
|
- [pkey](#pkey)
|
||||||
|
- [etcd_ca](#etcd_ca)
|
||||||
|
- [client_ca](#client_ca)
|
||||||
|
- [admin_ca](#admin_ca)
|
||||||
|
- [osd_ca](#osd_ca)
|
||||||
|
- [mon_ca](#mon_ca)
|
||||||
|
- [antietcd_cert](#antietcd_cert)
|
||||||
|
- [antietcd_key](#antietcd_key)
|
||||||
|
- [etcd_proxy.urls](#etcd_proxyurls)
|
||||||
|
- [etcd_proxy.cert](#etcd_proxycert)
|
||||||
|
- [etcd_proxy.key](#etcd_proxykey)
|
||||||
|
- [etcd_proxy.ca](#etcd_proxyca)
|
||||||
|
- [osd_cert](#osd_cert)
|
||||||
|
- [osd_pkey](#osd_pkey)
|
||||||
|
- [api_cert](#api_cert)
|
||||||
|
- [api_pkey](#api_pkey)
|
||||||
- [etcd_client_cert](#etcd_client_cert)
|
- [etcd_client_cert](#etcd_client_cert)
|
||||||
- [etcd_client_key](#etcd_client_key)
|
- [etcd_client_key](#etcd_client_key)
|
||||||
- [etcd_ca](#etcd_ca)
|
|
||||||
- [osd_etcd_client_cert](#osd_etcd_client_cert)
|
- [osd_etcd_client_cert](#osd_etcd_client_cert)
|
||||||
- [osd_etcd_client_key](#osd_etcd_client_key)
|
- [osd_etcd_client_key](#osd_etcd_client_key)
|
||||||
- [mon_etcd_client_cert](#mon_etcd_client_cert)
|
- [mon_etcd_client_cert](#mon_etcd_client_cert)
|
||||||
- [mon_etcd_client_key](#mon_etcd_client_key)
|
- [mon_etcd_client_key](#mon_etcd_client_key)
|
||||||
|
- [proto_checksums](#proto_checksums)
|
||||||
|
- [force_proto_checksums](#force_proto_checksums)
|
||||||
|
- [max_cipher_pool_size](#max_cipher_pool_size)
|
||||||
- [vault_url](#vault_url)
|
- [vault_url](#vault_url)
|
||||||
- [vault_secret_api_path](#vault_secret_api_path)
|
- [vault_secret_api_path](#vault_secret_api_path)
|
||||||
- [vault_client_cert](#vault_client_cert)
|
- [vault_client_cert](#vault_client_cert)
|
||||||
@@ -25,54 +49,201 @@ Most of them can be set in /etc/vitastor/vitastor.conf and in etcd, but don't su
|
|||||||
- [vault_timeout_ms](#vault_timeout_ms)
|
- [vault_timeout_ms](#vault_timeout_ms)
|
||||||
- [vault_error_timeout_sec](#vault_error_timeout_sec)
|
- [vault_error_timeout_sec](#vault_error_timeout_sec)
|
||||||
- [vault_refresh_leeway_sec](#vault_refresh_leeway_sec)
|
- [vault_refresh_leeway_sec](#vault_refresh_leeway_sec)
|
||||||
- [max_aes_xts_pool_size](#max_aes_xts_pool_size)
|
|
||||||
|
|
||||||
## etcd_client_cert
|
## use_perms
|
||||||
|
|
||||||
|
- Type: boolean
|
||||||
|
- Default: false
|
||||||
|
|
||||||
|
Enable client permissions in a Vitastor cluster, including Antietcd built into the Monitor.
|
||||||
|
Requires configured encryption. Also note that separate Antietcd requires separate configuration
|
||||||
|
to use permissions (see [security documentation](../usage/security.en.md) for details).
|
||||||
|
|
||||||
|
## cert
|
||||||
|
|
||||||
- Type: string
|
- Type: string
|
||||||
|
|
||||||
Client TLS certificate to use for Vitastor client (not OSD and not monitor)
|
Client certificate of the current Vitastor user. Required for Vitastor protocol encryption.
|
||||||
etcd https connections. May be path to a file or just a PEM string with certificate.
|
Must be signed with [client_ca](#client_ca). Also used as the client certificate for etcd/Antietcd
|
||||||
In the latter case, string must begin with "-----BEGIN CERTIFICATE-----".
|
connections by default.
|
||||||
|
|
||||||
## etcd_client_key
|
## pkey
|
||||||
|
|
||||||
- Type: string
|
- Type: string
|
||||||
|
|
||||||
Private key for etcd_client_cert (also a file or a PEM string).
|
Private key of the current Vitastor user.
|
||||||
|
|
||||||
## etcd_ca
|
## etcd_ca
|
||||||
|
|
||||||
- Type: string
|
- Type: string
|
||||||
|
|
||||||
Trusted TLS CA to verify etcd server certificate. May be path to a file,
|
Trusted TLS CA to verify etcd server certificate. Or just the etcd server's
|
||||||
directory or just a PEM string with certificate.
|
certificate itself - it's fine to use it for etcd_ca.
|
||||||
|
|
||||||
|
## client_ca
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Trusted TLS CA to verify Vitastor client certificates.
|
||||||
|
Mandatory for Vitastor protocol encryption.
|
||||||
|
|
||||||
|
## admin_ca
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Trusted TLS CA to verify Vitastor administrator certificates.
|
||||||
|
|
||||||
|
## osd_ca
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Trusted TLS CA to verify Vitastor OSD certificates. Also mandatory for Vitastor protocol
|
||||||
|
encryption. Must be different from client_ca. May be equal to osd_cert - different OSDs
|
||||||
|
don't require separate certificates at the moment because their permissions don't differ.
|
||||||
|
|
||||||
|
## mon_ca
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Trusted TLS CA to verify Vitastor Monitor certificates. Used only for separate Antietcd,
|
||||||
|
not required when a monitor built-in Antietcd is used. May be equal to mon_client_etcd_cert.
|
||||||
|
|
||||||
|
## antietcd_cert
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Server TLS certificate for Antietcd built into the Monitor.
|
||||||
|
|
||||||
|
## antietcd_key
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Private key for antietcd_cert.
|
||||||
|
|
||||||
|
## etcd_proxy.urls
|
||||||
|
|
||||||
|
- Type: string or array of strings
|
||||||
|
|
||||||
|
etcd URLs for Antietcd etcd proxy mode.
|
||||||
|
See [Mon as Etcd proxy](../usage/security.en.md#mon-as-etcd-proxy) for details.
|
||||||
|
|
||||||
|
## etcd_proxy.cert
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Client certificate for Antietcd connections to etcd in proxy mode.
|
||||||
|
|
||||||
|
## etcd_proxy.key
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Private key for etcd_proxy.cert.
|
||||||
|
|
||||||
|
## etcd_proxy.ca
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Trusted TLS CA to verify etcd server certificate when connecting to it from Antietcd.
|
||||||
|
|
||||||
|
## osd_cert
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Vitastor OSD server certificate. Required for Vitastor protocol encryption. May be equal
|
||||||
|
to [osd_ca](#osd_ca) - all OSDs share the same permission set for now. Also used as the client
|
||||||
|
certificate for connections from OSD to etcd/Antietcd by default.
|
||||||
|
|
||||||
|
## osd_pkey
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Private key for osd_cert.
|
||||||
|
|
||||||
|
## api_cert
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Server TLS certificate for [vitastor-cli serve](../usage/cli.en.md#serve) API server.
|
||||||
|
|
||||||
|
## api_pkey
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Private key for api_cert.
|
||||||
|
|
||||||
|
## etcd_client_cert
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Client TLS certificate to use for connections from Vitastor clients to etcd/Antietcd if you don't want
|
||||||
|
to use the common client certificate [cert](#cert).
|
||||||
|
|
||||||
|
## etcd_client_key
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Private key for etcd_client_cert.
|
||||||
|
|
||||||
## osd_etcd_client_cert
|
## osd_etcd_client_cert
|
||||||
|
|
||||||
- Type: string
|
- Type: string
|
||||||
|
|
||||||
Same as [etcd_client_cert](#etcd_client_cert), but only for OSDs.
|
Client TLS certificate to use for connections from Vitastor OSDs to etcd/Antietcd if you don't want
|
||||||
OSDs, clients and monitors should have different permissions, so they should
|
to use the common OSD certificate [osd_cert](#osd_cert).
|
||||||
use different certificates.
|
|
||||||
|
|
||||||
## osd_etcd_client_key
|
## osd_etcd_client_key
|
||||||
|
|
||||||
- Type: string
|
- Type: string
|
||||||
|
|
||||||
Same as [etcd_client_key](#etcd_client_key), but only for OSDs.
|
Private key for osd_etcd_client_cert.
|
||||||
|
|
||||||
## mon_etcd_client_cert
|
## mon_etcd_client_cert
|
||||||
|
|
||||||
- Type: string
|
- Type: string
|
||||||
|
|
||||||
Same as [etcd_client_cert](#etcd_client_cert), but only for Vitastor monitors.
|
Client TLS certificate to use for connections from Vitastor Monitors to etcd/Antietcd - required
|
||||||
|
if you don't use the built-in Antietcd. In case you use it Monitor has direct access to Antietcd data
|
||||||
|
and doesn't require any connection.
|
||||||
|
|
||||||
## mon_etcd_client_key
|
## mon_etcd_client_key
|
||||||
|
|
||||||
- Type: string
|
- Type: string
|
||||||
|
|
||||||
Same as [etcd_client_key](#etcd_client_key), but only for Vitastor monitors.
|
Private key for mon_etcd_client_cert.
|
||||||
|
|
||||||
|
## proto_checksums
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
- Default: payload
|
||||||
|
|
||||||
|
One of "full", "payload", "gcm", "none":
|
||||||
|
- "full" means calculate and verify transport level checksums from the full message data
|
||||||
|
including the header - recommended for unencrypted setups.
|
||||||
|
- "payload" enables checksums only for the actual read/write data, but skips them for message
|
||||||
|
headers - recommended for encrypted setups because headers are already protected by AES-GCM.
|
||||||
|
- "gcm" disables checksums and enables AES-GCM encryption of the whole messages including headers
|
||||||
|
and data - AES-GCM already includes MAC which is actually a stronger checksum. This option is
|
||||||
|
slower and is only recommended for untrusted networks.
|
||||||
|
- "none" disables transport level checksums at all.
|
||||||
|
|
||||||
|
## force_proto_checksums
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
To allow older clients to connect to a Vitastor cluster with enabled checksums, Vitastor OSDs
|
||||||
|
allow clients to downgrade their proto_checksums by default. force_proto_checksums sets the
|
||||||
|
minimum security level allowed for connecting clients. When encryption is disabled, default
|
||||||
|
force_proto_checksums is none and clients without checksums are allowed. With enabled
|
||||||
|
encryption, force_proto_checksums becomes "payload" by default to block unauthenticated data
|
||||||
|
on the transport level.
|
||||||
|
|
||||||
|
## max_cipher_pool_size
|
||||||
|
|
||||||
|
- Type: integer
|
||||||
|
- Default: 256
|
||||||
|
|
||||||
|
Maximum number of OpenSSL cipher contexts cached in OSD memory, counted separately
|
||||||
|
for each cipher and for encryption/decryption. Probably doesn't require modification.
|
||||||
|
|
||||||
## vault_url
|
## vault_url
|
||||||
|
|
||||||
@@ -103,14 +274,14 @@ Vault v1 secret API mount path to use.
|
|||||||
|
|
||||||
- Type: string
|
- Type: string
|
||||||
|
|
||||||
Client TLS certificate to use for Vault connections. Just like [etcd_client_cert](#etcd_client_cert),
|
Client TLS certificate to use for Vault connections if you don't want to use the common Vitastor
|
||||||
may be path to a file or just a certificate in PEM string.
|
client certificate [cert](#cert) which is also used for Vault connections by default.
|
||||||
|
|
||||||
## vault_client_key
|
## vault_client_key
|
||||||
|
|
||||||
- Type: string
|
- Type: string
|
||||||
|
|
||||||
Private key for vault_client_cert (also a file or a PEM string).
|
Private key for the vault_client_cert certificate.
|
||||||
|
|
||||||
## vault_ca
|
## vault_ca
|
||||||
|
|
||||||
@@ -140,11 +311,3 @@ Time (in seconds) to wait before retrying after receiving an error from Vault.
|
|||||||
|
|
||||||
Extra time (in seconds) before real Vault token lease_timeout to refresh it, just
|
Extra time (in seconds) before real Vault token lease_timeout to refresh it, just
|
||||||
in case of system clock drift.
|
in case of system clock drift.
|
||||||
|
|
||||||
## max_aes_xts_pool_size
|
|
||||||
|
|
||||||
- Type: integer
|
|
||||||
- Default: 256
|
|
||||||
|
|
||||||
Maximum number of OpenSSL encryption contexts cached in OSD memory. Probably
|
|
||||||
doesn't require modification.
|
|
||||||
|
|||||||
@@ -12,13 +12,37 @@ OSD, мониторами и клиентами.
|
|||||||
Большая их часть может задаваться в /etc/vitastor/vitastor.conf и в etcd, но не
|
Большая их часть может задаваться в /etc/vitastor/vitastor.conf и в etcd, но не
|
||||||
поддерживает онлайн-изменение.
|
поддерживает онлайн-изменение.
|
||||||
|
|
||||||
|
Все параметры сертификатов и закрытых ключей могут быть путём к файлу или просто
|
||||||
|
строкой с сертификатом в формате PEM. В последнем случае строка должна начинаться с
|
||||||
|
"-----BEGIN CERTIFICATE-----" или "-----BEGIN PRIVATE KEY-----".
|
||||||
|
|
||||||
|
- [use_perms](#use_perms)
|
||||||
|
- [cert](#cert)
|
||||||
|
- [pkey](#pkey)
|
||||||
|
- [etcd_ca](#etcd_ca)
|
||||||
|
- [client_ca](#client_ca)
|
||||||
|
- [admin_ca](#admin_ca)
|
||||||
|
- [osd_ca](#osd_ca)
|
||||||
|
- [mon_ca](#mon_ca)
|
||||||
|
- [antietcd_cert](#antietcd_cert)
|
||||||
|
- [antietcd_key](#antietcd_key)
|
||||||
|
- [etcd_proxy.urls](#etcd_proxyurls)
|
||||||
|
- [etcd_proxy.cert](#etcd_proxycert)
|
||||||
|
- [etcd_proxy.key](#etcd_proxykey)
|
||||||
|
- [etcd_proxy.ca](#etcd_proxyca)
|
||||||
|
- [osd_cert](#osd_cert)
|
||||||
|
- [osd_pkey](#osd_pkey)
|
||||||
|
- [api_cert](#api_cert)
|
||||||
|
- [api_pkey](#api_pkey)
|
||||||
- [etcd_client_cert](#etcd_client_cert)
|
- [etcd_client_cert](#etcd_client_cert)
|
||||||
- [etcd_client_key](#etcd_client_key)
|
- [etcd_client_key](#etcd_client_key)
|
||||||
- [etcd_ca](#etcd_ca)
|
|
||||||
- [osd_etcd_client_cert](#osd_etcd_client_cert)
|
- [osd_etcd_client_cert](#osd_etcd_client_cert)
|
||||||
- [osd_etcd_client_key](#osd_etcd_client_key)
|
- [osd_etcd_client_key](#osd_etcd_client_key)
|
||||||
- [mon_etcd_client_cert](#mon_etcd_client_cert)
|
- [mon_etcd_client_cert](#mon_etcd_client_cert)
|
||||||
- [mon_etcd_client_key](#mon_etcd_client_key)
|
- [mon_etcd_client_key](#mon_etcd_client_key)
|
||||||
|
- [proto_checksums](#proto_checksums)
|
||||||
|
- [force_proto_checksums](#force_proto_checksums)
|
||||||
|
- [max_cipher_pool_size](#max_cipher_pool_size)
|
||||||
- [vault_url](#vault_url)
|
- [vault_url](#vault_url)
|
||||||
- [vault_secret_api_path](#vault_secret_api_path)
|
- [vault_secret_api_path](#vault_secret_api_path)
|
||||||
- [vault_client_cert](#vault_client_cert)
|
- [vault_client_cert](#vault_client_cert)
|
||||||
@@ -27,56 +51,205 @@ OSD, мониторами и клиентами.
|
|||||||
- [vault_timeout_ms](#vault_timeout_ms)
|
- [vault_timeout_ms](#vault_timeout_ms)
|
||||||
- [vault_error_timeout_sec](#vault_error_timeout_sec)
|
- [vault_error_timeout_sec](#vault_error_timeout_sec)
|
||||||
- [vault_refresh_leeway_sec](#vault_refresh_leeway_sec)
|
- [vault_refresh_leeway_sec](#vault_refresh_leeway_sec)
|
||||||
- [max_aes_xts_pool_size](#max_aes_xts_pool_size)
|
|
||||||
|
|
||||||
## etcd_client_cert
|
## use_perms
|
||||||
|
|
||||||
|
- Тип: булево (да/нет)
|
||||||
|
- Значение по умолчанию: false
|
||||||
|
|
||||||
|
Включает клиентские привилегии в кластере Vitastor, в том числе во встроенном в мониторе Antietcd.
|
||||||
|
Требует настроенного шифрования протокола. Также обратите внимание, что отдельно установленный Antietcd
|
||||||
|
требует отдельной настройки привилегий (подробности смотрите в [документации безопасности](../usage/security.ru.md)).
|
||||||
|
|
||||||
|
## cert
|
||||||
|
|
||||||
- Тип: строка
|
- Тип: строка
|
||||||
|
|
||||||
Клиентский TLS сертификат для https-подключений к etcd для клиентов Vitastor
|
Клиентский сертификат текущего пользователя Vitastor. Требуется для шифрования протокола Vitastor.
|
||||||
(не OSD и не мониторов). Может быть путём к файлу или просто строкой с
|
Должен быть подписан [client_ca](#client_ca). Также по умолчанию используется как клиентский
|
||||||
сертификатом в формате PEM. В последнем случае строка должна начинаться с
|
сертификат для подключения к etcd/Antietcd и Vault.
|
||||||
"-----BEGIN CERTIFICATE-----".
|
|
||||||
|
|
||||||
## etcd_client_key
|
## pkey
|
||||||
|
|
||||||
- Тип: строка
|
- Тип: строка
|
||||||
|
|
||||||
Закрытый ключ для сертификата etcd_client_cert (также путь к файлу или PEM строка).
|
Закрытый ключ текущего пользователя Vitastor.
|
||||||
|
|
||||||
## etcd_ca
|
## etcd_ca
|
||||||
|
|
||||||
- Тип: строка
|
- Тип: строка
|
||||||
|
|
||||||
Доверенный корневой TLS-сертификат для проверки сертификата сервера etcd.
|
Доверенный корневой TLS-сертификат для проверки сертификата сервера etcd.
|
||||||
Может быть путём к файлу, директории или просто строкой с сертификатом в
|
Либо же просто сам сертификат сервера etcd - его можно использовать как etcd_ca.
|
||||||
формате PEM.
|
|
||||||
|
## client_ca
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Доверенный TLS-сертификат для проверки сертификатов клиентов Vitastor.
|
||||||
|
Требуется для шифрования протокола Vitastor.
|
||||||
|
|
||||||
|
## admin_ca
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Доверенный TLS-сертификат для проверки сертификатов администраторов Vitastor.
|
||||||
|
|
||||||
|
## osd_ca
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Доверенный TLS-сертификат для проверки сертификатов OSD Vitastor. Также обязателен
|
||||||
|
для шифрования протокола Vitastor. Должен отличаться от client_ca. Может быть равен
|
||||||
|
osd_cert - разные OSD не требуют разных сертификатов, потому что на данный момент
|
||||||
|
привилегии разных OSD никак не отличаются.
|
||||||
|
|
||||||
|
## mon_ca
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Доверенный TLS-сертификат для проверки сертификатов мониторов Vitastor. Используется
|
||||||
|
только отдельно установленным Antietcd, не требуется при использовании встроенного в монитор
|
||||||
|
Antietcd. Может быть равен mon_client_etcd_cert.
|
||||||
|
|
||||||
|
## antietcd_cert
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Серверный TLS-сертификат для Antietcd, встроенного в монитор.
|
||||||
|
|
||||||
|
## antietcd_key
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Закрытый ключ для сертификата antietcd_cert.
|
||||||
|
|
||||||
|
## etcd_proxy.urls
|
||||||
|
|
||||||
|
- Тип: строка или массив строк
|
||||||
|
|
||||||
|
Адреса etcd для режима Antietcd etcd-прокси.
|
||||||
|
Смотрите подробности в разделе [Mon в роли Etcd proxy](../usage/security.ru.md#mon-в-роли-etcd-proxy).
|
||||||
|
|
||||||
|
## etcd_proxy.cert
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Клиентский сертификат для подключений от Antietcd к etcd в режиме прокси.
|
||||||
|
|
||||||
|
## etcd_proxy.key
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Закрытый ключ для сертификата etcd_proxy.cert.
|
||||||
|
|
||||||
|
## etcd_proxy.ca
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Доверенный TLS-сертификат для проверки сертификата сервера etcd при подключениях от Antietcd.
|
||||||
|
|
||||||
|
## osd_cert
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Сертификат сервера Vitastor OSD. Требуется для шифрования протокола Vitastor. Может быть равен
|
||||||
|
[osd_ca](#osd_ca) - все OSD на данный момент имеют одинаковые привилегии. Также по умолчанию
|
||||||
|
используется как клиентский сертификат для подключения от OSD к etcd/Antietcd.
|
||||||
|
|
||||||
|
## osd_pkey
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Закрытый ключ для сертификата osd_cert.
|
||||||
|
|
||||||
|
## api_cert
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Серверный TLS-сертификат для API-сервера [vitastor-cli serve](../usage/cli.ru.md#serve).
|
||||||
|
|
||||||
|
## api_pkey
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Закрытый ключ для сертификата api_cert.
|
||||||
|
|
||||||
|
## etcd_client_cert
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Клиентский TLS сертификат для подключений от клиентов Vitastor к etcd/Antietcd, если вы не хотите
|
||||||
|
использовать общий клиентский сертификат [cert](#cert).
|
||||||
|
|
||||||
|
## etcd_client_key
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Закрытый ключ для сертификата etcd_client_cert.
|
||||||
|
|
||||||
## osd_etcd_client_cert
|
## osd_etcd_client_cert
|
||||||
|
|
||||||
- Тип: строка
|
- Тип: строка
|
||||||
|
|
||||||
Аналогично [etcd_client_cert](#etcd_client_cert), но только для OSD.
|
Клиентский TLS сертификат для подключений от Vitastor OSD к etcd/Antietcd, если вы не хотите
|
||||||
OSD, клиенты и мониторы должны иметь разные привилегии, поэтому они должны
|
использовать общий сертификат OSD [osd_cert](#osd_cert).
|
||||||
использовать разные сертификаты.
|
|
||||||
|
|
||||||
## osd_etcd_client_key
|
## osd_etcd_client_key
|
||||||
|
|
||||||
- Тип: строка
|
- Тип: строка
|
||||||
|
|
||||||
Аналогично [etcd_client_key](#etcd_client_key), но только для OSD.
|
Закрытый ключ для сертификата osd_etcd_client_cert.
|
||||||
|
|
||||||
## mon_etcd_client_cert
|
## mon_etcd_client_cert
|
||||||
|
|
||||||
- Тип: строка
|
- Тип: строка
|
||||||
|
|
||||||
Аналогично [etcd_client_cert](#etcd_client_cert), но только для мониторов Vitastor.
|
Клиентский TLS сертификат для подключений от мониторов Vitastor к etcd/Antietcd - требуется, если
|
||||||
|
вы не используете встроенный в монитор Antietcd. Если вы используете его, то монитор и так имеет
|
||||||
|
прямой доступ к данным Antietcd и не требует никаких соединений.
|
||||||
|
|
||||||
## mon_etcd_client_key
|
## mon_etcd_client_key
|
||||||
|
|
||||||
- Тип: строка
|
- Тип: строка
|
||||||
|
|
||||||
Аналогично [etcd_client_key](#etcd_client_key), но только для мониторов Vitastor.
|
Закрытый ключ для сертификата mon_etcd_client_cert.
|
||||||
|
|
||||||
|
## proto_checksums
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
- Значение по умолчанию: payload
|
||||||
|
|
||||||
|
Одно из значений "full", "payload", "gcm" и "none":
|
||||||
|
- "full" означает расчёт и проверку контрольных сумм на транспортном уровне от полных сообщений,
|
||||||
|
включая их заголовки и данные - рекомендуется для кластеров без шифрования.
|
||||||
|
- "payload" включает контрольные суммы только для данных сообщений, но пропускает заголовки -
|
||||||
|
такая настройка рекомендуется для кластеров с включённым шифрованием, потому что в них заголовки
|
||||||
|
и так защищены шифрованием AES-GCM.
|
||||||
|
- "gcm" отключает контрольные суммы и включает шифрование полных сообщений включая заголовки и
|
||||||
|
данные - AES-GCM уже включает в себя MAC, который по сути является криптостойкой контрольной
|
||||||
|
суммой. Такая настройка медленнее и рекомендуется только для недоверенных сетей.
|
||||||
|
- "none" полностью отключает контрольные суммы на транспортном уровне.
|
||||||
|
|
||||||
|
## force_proto_checksums
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Чтобы старые клиенты Vitastor могли подключаться к кластеру с включёнными контрольными
|
||||||
|
суммами, Vitastor OSD по умолчанию разрешают клиентам отключать контрольные суммы
|
||||||
|
данных (proto_checksums). Настройка force_proto_checksums задаёт минимальный уровень
|
||||||
|
безопасности, разрешённый для подключающихся клиентов. Когда шифрование отключено,
|
||||||
|
force_proto_checksums по умолчанию равно none и подключения клиентов без контрольных
|
||||||
|
сумм разрешаются. При включённом шифровании значение по умолчанию force_proto_checksums
|
||||||
|
становится "payload", чтобы блокировать подключения с неаутентифицированными данными.
|
||||||
|
|
||||||
|
## max_cipher_pool_size
|
||||||
|
|
||||||
|
- Тип: целое число
|
||||||
|
- Значение по умолчанию: 256
|
||||||
|
|
||||||
|
Максимальное количество кэшируемых в памяти OSD контекстов шифра OpenSSL, учитываемое
|
||||||
|
отдельно для каждого шифра и для шифрования и расшифровки. Вряд ли требует изменения.
|
||||||
|
|
||||||
## vault_url
|
## vault_url
|
||||||
|
|
||||||
@@ -106,14 +279,14 @@ OSD, клиенты и мониторы должны иметь разные п
|
|||||||
|
|
||||||
- Тип: строка
|
- Тип: строка
|
||||||
|
|
||||||
Клиентский TLS сертификат для подключений к Vault. Как и [etcd_client_cert](#etcd_client_cert),
|
Клиентский TLS сертификат для подключений к Vault на тот случай, если вы не хотите использовать
|
||||||
может быть путём к файлу или просто PEM-строкой с сертификатом.
|
общий сертификат клиента Vitastor [cert](#cert), используемый для подключений к Vault по умолчанию.
|
||||||
|
|
||||||
## vault_client_key
|
## vault_client_key
|
||||||
|
|
||||||
- Тип: строка
|
- Тип: строка
|
||||||
|
|
||||||
Закрытый ключ для сертификата vault_client_cert (также путь к файлу или PEM строка).
|
Закрытый ключ для сертификата vault_client_cert.
|
||||||
|
|
||||||
## vault_ca
|
## vault_ca
|
||||||
|
|
||||||
@@ -144,11 +317,3 @@ OSD, клиенты и мониторы должны иметь разные п
|
|||||||
|
|
||||||
Зазор времени (в секундах), чтобы обновлять токены Vault чуть раньше их реального
|
Зазор времени (в секундах), чтобы обновлять токены Vault чуть раньше их реального
|
||||||
lease_timeout, на случай "ухода" системных часов.
|
lease_timeout, на случай "ухода" системных часов.
|
||||||
|
|
||||||
## max_aes_xts_pool_size
|
|
||||||
|
|
||||||
- Тип: целое число
|
|
||||||
- Значение по умолчанию: 256
|
|
||||||
|
|
||||||
Максимальное количество кэшируемых в памяти OSD контекстов шифрования OpenSSL.
|
|
||||||
Вряд ли требует изменения.
|
|
||||||
|
|||||||
@@ -66,6 +66,8 @@
|
|||||||
|
|
||||||
{{../../usage/nfs.en.md}}
|
{{../../usage/nfs.en.md}}
|
||||||
|
|
||||||
|
{{../../usage/security.en.md}}
|
||||||
|
|
||||||
{{../../usage/admin.en.md}}
|
{{../../usage/admin.en.md}}
|
||||||
|
|
||||||
## Performance
|
## Performance
|
||||||
|
|||||||
@@ -66,6 +66,8 @@
|
|||||||
|
|
||||||
{{../../usage/nfs.ru.md}}
|
{{../../usage/nfs.ru.md}}
|
||||||
|
|
||||||
|
{{../../usage/security.ru.md}}
|
||||||
|
|
||||||
{{../../usage/admin.ru.md}}
|
{{../../usage/admin.ru.md}}
|
||||||
|
|
||||||
## Производительность
|
## Производительность
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ for (const file of params_files)
|
|||||||
let out = '\n';
|
let out = '\n';
|
||||||
for (const c of cfg)
|
for (const c of cfg)
|
||||||
{
|
{
|
||||||
out += `\n- [${c.name}](#${c.name})`;
|
out += `\n- [${c.name}](#${c.name.replace(/\./g, '')})`;
|
||||||
}
|
}
|
||||||
for (const c of cfg)
|
for (const c of cfg)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,6 +21,9 @@
|
|||||||
cluster, cluster_key, persist_filter, stale_read can also be set in
|
cluster, cluster_key, persist_filter, stale_read can also be set in
|
||||||
Vitastor configuration with `antietcd_` prefix.
|
Vitastor configuration with `antietcd_` prefix.
|
||||||
|
|
||||||
|
See also: [antietcd_cert](security.en.md#antietcd_cert),
|
||||||
|
[antietcd_key](security.en.md#antietcd_key) and [etcd_proxy](security.en.md#etcd_proxyurls).
|
||||||
|
|
||||||
You can dump/load data to or from antietcd using Antietcd `anticli` tool:
|
You can dump/load data to or from antietcd using Antietcd `anticli` tool:
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -47,6 +50,9 @@
|
|||||||
node_id, cluster, cluster_key, persist_filter, stale_read также можно задавать
|
node_id, cluster, cluster_key, persist_filter, stale_read также можно задавать
|
||||||
в конфигурации Vitastor с префиксом `antietcd_`.
|
в конфигурации Vitastor с префиксом `antietcd_`.
|
||||||
|
|
||||||
|
Смотрите также настройки [antietcd_cert](security.ru.md#antietcd_cert),
|
||||||
|
[antietcd_key](security.ru.md#antietcd_key) и [etcd_proxy](security.ru.md#etcd_proxyurls).
|
||||||
|
|
||||||
Вы можете выгружать/загружать данные в или из antietcd с помощью его инструмента
|
Вы можете выгружать/загружать данные в или из antietcd с помощью его инструмента
|
||||||
`anticli`:
|
`anticli`:
|
||||||
|
|
||||||
|
|||||||
@@ -358,6 +358,29 @@
|
|||||||
параметра читается без дополнительного копирования. Вы можете попробовать
|
параметра читается без дополнительного копирования. Вы можете попробовать
|
||||||
поменять этот параметр и посмотреть, как он влияет на производительность
|
поменять этот параметр и посмотреть, как он влияет на производительность
|
||||||
случайного и линейного доступа.
|
случайного и линейного доступа.
|
||||||
|
- name: rxbounce
|
||||||
|
type: bool
|
||||||
|
default: false
|
||||||
|
online: true
|
||||||
|
info: |
|
||||||
|
Receive all TCP data into an intermediate buffer before copying to the actual
|
||||||
|
destination. Enabling this option is required for Windows guests if you use TCP
|
||||||
|
and [security.en.md#proto_checksums](proto_checksums) because Windows uses a
|
||||||
|
shared system-wide 'dummy page' to skip unneeded blocks during reads and breaks
|
||||||
|
checksum verification with it when data copy is avoided. rxbounce is only needed
|
||||||
|
when you use TCP and don't use transport encryption (AES-GCM); it's not required
|
||||||
|
with RDMA or with GCM encryption because both always copy data anyway (yes, RDMA
|
||||||
|
isn't actually zerocopy in Vitastor).
|
||||||
|
info_ru: |
|
||||||
|
Принимать все данные по TCP в промежуточный буфер и потом копировать их в
|
||||||
|
буфер клиента (ВМ). Это нужно для гостевых ОС Windows с TCP и включёнными
|
||||||
|
[security.ru.md#proto_checksums](proto_checksums), потому что Windows использует
|
||||||
|
одну разделяемую общесистемную страницу-заглушку для пропуска ненужных блоков
|
||||||
|
при чтении и ломает проверку контрольных сумм, если данные не копируются лишний
|
||||||
|
раз. Опция rxbounce нужна только если вы используете TCP и не используете
|
||||||
|
транспортное шифрование (AES-GCM); с RDMA или шифрованием опция не нужна, так
|
||||||
|
как в этом случае данные копируются всегда (да, RDMA в Vitastor на самом деле
|
||||||
|
не zero-copy).
|
||||||
- name: min_zerocopy_send_size
|
- name: min_zerocopy_send_size
|
||||||
type: int
|
type: int
|
||||||
default: 32768
|
default: 32768
|
||||||
|
|||||||
@@ -3,3 +3,7 @@
|
|||||||
These parameters affect your Vitastor installation security and apply to OSDs, monitors and clients.
|
These parameters affect your Vitastor installation security and apply to OSDs, monitors and clients.
|
||||||
|
|
||||||
Most of them can be set in /etc/vitastor/vitastor.conf and in etcd, but don't support online modification.
|
Most of them can be set in /etc/vitastor/vitastor.conf and in etcd, but don't support online modification.
|
||||||
|
|
||||||
|
All certificate and private key parameters mentioned may contain a path to a PEM file or just
|
||||||
|
a PEM string with certificate or a private key. In the latter case, the string must begin with
|
||||||
|
"-----BEGIN CERTIFICATE-----" or "-----BEGIN PRIVATE KEY-----".
|
||||||
|
|||||||
@@ -5,3 +5,7 @@ OSD, мониторами и клиентами.
|
|||||||
|
|
||||||
Большая их часть может задаваться в /etc/vitastor/vitastor.conf и в etcd, но не
|
Большая их часть может задаваться в /etc/vitastor/vitastor.conf и в etcd, но не
|
||||||
поддерживает онлайн-изменение.
|
поддерживает онлайн-изменение.
|
||||||
|
|
||||||
|
Все параметры сертификатов и закрытых ключей могут быть путём к файлу или просто
|
||||||
|
строкой с сертификатом в формате PEM. В последнем случае строка должна начинаться с
|
||||||
|
"-----BEGIN CERTIFICATE-----" или "-----BEGIN PRIVATE KEY-----".
|
||||||
|
|||||||
@@ -1,49 +1,209 @@
|
|||||||
- name: etcd_client_cert
|
- name: use_perms
|
||||||
|
type: bool
|
||||||
|
default: false
|
||||||
|
info: |
|
||||||
|
Enable client permissions in a Vitastor cluster, including Antietcd built into the Monitor.
|
||||||
|
Requires configured encryption. Also note that separate Antietcd requires separate configuration
|
||||||
|
to use permissions (see [security documentation](../usage/security.en.md) for details).
|
||||||
|
info_ru: |
|
||||||
|
Включает клиентские привилегии в кластере Vitastor, в том числе во встроенном в мониторе Antietcd.
|
||||||
|
Требует настроенного шифрования протокола. Также обратите внимание, что отдельно установленный Antietcd
|
||||||
|
требует отдельной настройки привилегий (подробности смотрите в [документации безопасности](../usage/security.ru.md)).
|
||||||
|
- name: cert
|
||||||
type: string
|
type: string
|
||||||
info: |
|
info: |
|
||||||
Client TLS certificate to use for Vitastor client (not OSD and not monitor)
|
Client certificate of the current Vitastor user. Required for Vitastor protocol encryption.
|
||||||
etcd https connections. May be path to a file or just a PEM string with certificate.
|
Must be signed with [client_ca](#client_ca). Also used as the client certificate for etcd/Antietcd
|
||||||
In the latter case, string must begin with "-----BEGIN CERTIFICATE-----".
|
connections by default.
|
||||||
info_ru: |
|
info_ru: |
|
||||||
Клиентский TLS сертификат для https-подключений к etcd для клиентов Vitastor
|
Клиентский сертификат текущего пользователя Vitastor. Требуется для шифрования протокола Vitastor.
|
||||||
(не OSD и не мониторов). Может быть путём к файлу или просто строкой с
|
Должен быть подписан [client_ca](#client_ca). Также по умолчанию используется как клиентский
|
||||||
сертификатом в формате PEM. В последнем случае строка должна начинаться с
|
сертификат для подключения к etcd/Antietcd и Vault.
|
||||||
"-----BEGIN CERTIFICATE-----".
|
- name: pkey
|
||||||
- name: etcd_client_key
|
|
||||||
type: string
|
type: string
|
||||||
info: Private key for etcd_client_cert (also a file or a PEM string).
|
info: Private key of the current Vitastor user.
|
||||||
info_ru: Закрытый ключ для сертификата etcd_client_cert (также путь к файлу или PEM строка).
|
info_ru: Закрытый ключ текущего пользователя Vitastor.
|
||||||
- name: etcd_ca
|
- name: etcd_ca
|
||||||
type: string
|
type: string
|
||||||
info: |
|
info: |
|
||||||
Trusted TLS CA to verify etcd server certificate. May be path to a file,
|
Trusted TLS CA to verify etcd server certificate. Or just the etcd server's
|
||||||
directory or just a PEM string with certificate.
|
certificate itself - it's fine to use it for etcd_ca.
|
||||||
info_ru: |
|
info_ru: |
|
||||||
Доверенный корневой TLS-сертификат для проверки сертификата сервера etcd.
|
Доверенный корневой TLS-сертификат для проверки сертификата сервера etcd.
|
||||||
Может быть путём к файлу, директории или просто строкой с сертификатом в
|
Либо же просто сам сертификат сервера etcd - его можно использовать как etcd_ca.
|
||||||
формате PEM.
|
- name: client_ca
|
||||||
|
type: string
|
||||||
|
info: |
|
||||||
|
Trusted TLS CA to verify Vitastor client certificates.
|
||||||
|
Mandatory for Vitastor protocol encryption.
|
||||||
|
info_ru: |
|
||||||
|
Доверенный TLS-сертификат для проверки сертификатов клиентов Vitastor.
|
||||||
|
Требуется для шифрования протокола Vitastor.
|
||||||
|
- name: admin_ca
|
||||||
|
type: string
|
||||||
|
info: |
|
||||||
|
Trusted TLS CA to verify Vitastor administrator certificates.
|
||||||
|
info_ru: |
|
||||||
|
Доверенный TLS-сертификат для проверки сертификатов администраторов Vitastor.
|
||||||
|
- name: osd_ca
|
||||||
|
type: string
|
||||||
|
info: |
|
||||||
|
Trusted TLS CA to verify Vitastor OSD certificates. Also mandatory for Vitastor protocol
|
||||||
|
encryption. Must be different from client_ca. May be equal to osd_cert - different OSDs
|
||||||
|
don't require separate certificates at the moment because their permissions don't differ.
|
||||||
|
info_ru: |
|
||||||
|
Доверенный TLS-сертификат для проверки сертификатов OSD Vitastor. Также обязателен
|
||||||
|
для шифрования протокола Vitastor. Должен отличаться от client_ca. Может быть равен
|
||||||
|
osd_cert - разные OSD не требуют разных сертификатов, потому что на данный момент
|
||||||
|
привилегии разных OSD никак не отличаются.
|
||||||
|
- name: mon_ca
|
||||||
|
type: string
|
||||||
|
info: |
|
||||||
|
Trusted TLS CA to verify Vitastor Monitor certificates. Used only for separate Antietcd,
|
||||||
|
not required when a monitor built-in Antietcd is used. May be equal to mon_client_etcd_cert.
|
||||||
|
info_ru: |
|
||||||
|
Доверенный TLS-сертификат для проверки сертификатов мониторов Vitastor. Используется
|
||||||
|
только отдельно установленным Antietcd, не требуется при использовании встроенного в монитор
|
||||||
|
Antietcd. Может быть равен mon_client_etcd_cert.
|
||||||
|
- name: antietcd_cert
|
||||||
|
type: string
|
||||||
|
info: Server TLS certificate for Antietcd built into the Monitor.
|
||||||
|
info_ru: Серверный TLS-сертификат для Antietcd, встроенного в монитор.
|
||||||
|
- name: antietcd_key
|
||||||
|
type: string
|
||||||
|
info: Private key for antietcd_cert.
|
||||||
|
info_ru: Закрытый ключ для сертификата antietcd_cert.
|
||||||
|
- name: etcd_proxy.urls
|
||||||
|
type: string or array of strings
|
||||||
|
type_ru: строка или массив строк
|
||||||
|
info: |
|
||||||
|
etcd URLs for Antietcd etcd proxy mode.
|
||||||
|
See [Mon as Etcd proxy](../usage/security.en.md#mon-as-etcd-proxy) for details.
|
||||||
|
info_ru: |
|
||||||
|
Адреса etcd для режима Antietcd etcd-прокси.
|
||||||
|
Смотрите подробности в разделе [Mon в роли Etcd proxy](../usage/security.ru.md#mon-в-роли-etcd-proxy).
|
||||||
|
- name: etcd_proxy.cert
|
||||||
|
type: string
|
||||||
|
info: Client certificate for Antietcd connections to etcd in proxy mode.
|
||||||
|
info_ru: Клиентский сертификат для подключений от Antietcd к etcd в режиме прокси.
|
||||||
|
- name: etcd_proxy.key
|
||||||
|
type: string
|
||||||
|
info: Private key for etcd_proxy.cert.
|
||||||
|
info_ru: Закрытый ключ для сертификата etcd_proxy.cert.
|
||||||
|
- name: etcd_proxy.ca
|
||||||
|
type: string
|
||||||
|
info: Trusted TLS CA to verify etcd server certificate when connecting to it from Antietcd.
|
||||||
|
info_ru: Доверенный TLS-сертификат для проверки сертификата сервера etcd при подключениях от Antietcd.
|
||||||
|
- name: osd_cert
|
||||||
|
type: string
|
||||||
|
info: |
|
||||||
|
Vitastor OSD server certificate. Required for Vitastor protocol encryption. May be equal
|
||||||
|
to [osd_ca](#osd_ca) - all OSDs share the same permission set for now. Also used as the client
|
||||||
|
certificate for connections from OSD to etcd/Antietcd by default.
|
||||||
|
info_ru: |
|
||||||
|
Сертификат сервера Vitastor OSD. Требуется для шифрования протокола Vitastor. Может быть равен
|
||||||
|
[osd_ca](#osd_ca) - все OSD на данный момент имеют одинаковые привилегии. Также по умолчанию
|
||||||
|
используется как клиентский сертификат для подключения от OSD к etcd/Antietcd.
|
||||||
|
- name: osd_pkey
|
||||||
|
type: string
|
||||||
|
info: Private key for osd_cert.
|
||||||
|
info_ru: Закрытый ключ для сертификата osd_cert.
|
||||||
|
- name: api_cert
|
||||||
|
type: string
|
||||||
|
info: Server TLS certificate for [vitastor-cli serve](../usage/cli.en.md#serve) API server.
|
||||||
|
info_ru: Серверный TLS-сертификат для API-сервера [vitastor-cli serve](../usage/cli.ru.md#serve).
|
||||||
|
- name: api_pkey
|
||||||
|
type: string
|
||||||
|
info: Private key for api_cert.
|
||||||
|
info_ru: Закрытый ключ для сертификата api_cert.
|
||||||
|
- name: etcd_client_cert
|
||||||
|
type: string
|
||||||
|
info: |
|
||||||
|
Client TLS certificate to use for connections from Vitastor clients to etcd/Antietcd if you don't want
|
||||||
|
to use the common client certificate [cert](#cert).
|
||||||
|
info_ru: |
|
||||||
|
Клиентский TLS сертификат для подключений от клиентов Vitastor к etcd/Antietcd, если вы не хотите
|
||||||
|
использовать общий клиентский сертификат [cert](#cert).
|
||||||
|
- name: etcd_client_key
|
||||||
|
type: string
|
||||||
|
info: Private key for etcd_client_cert.
|
||||||
|
info_ru: Закрытый ключ для сертификата etcd_client_cert.
|
||||||
- name: osd_etcd_client_cert
|
- name: osd_etcd_client_cert
|
||||||
type: string
|
type: string
|
||||||
info: |
|
info: |
|
||||||
Same as [etcd_client_cert](#etcd_client_cert), but only for OSDs.
|
Client TLS certificate to use for connections from Vitastor OSDs to etcd/Antietcd if you don't want
|
||||||
OSDs, clients and monitors should have different permissions, so they should
|
to use the common OSD certificate [osd_cert](#osd_cert).
|
||||||
use different certificates.
|
|
||||||
info_ru: |
|
info_ru: |
|
||||||
Аналогично [etcd_client_cert](#etcd_client_cert), но только для OSD.
|
Клиентский TLS сертификат для подключений от Vitastor OSD к etcd/Antietcd, если вы не хотите
|
||||||
OSD, клиенты и мониторы должны иметь разные привилегии, поэтому они должны
|
использовать общий сертификат OSD [osd_cert](#osd_cert).
|
||||||
использовать разные сертификаты.
|
|
||||||
- name: osd_etcd_client_key
|
- name: osd_etcd_client_key
|
||||||
type: string
|
type: string
|
||||||
info: Same as [etcd_client_key](#etcd_client_key), but only for OSDs.
|
info: Private key for osd_etcd_client_cert.
|
||||||
info_ru: Аналогично [etcd_client_key](#etcd_client_key), но только для OSD.
|
info_ru: Закрытый ключ для сертификата osd_etcd_client_cert.
|
||||||
- name: mon_etcd_client_cert
|
- name: mon_etcd_client_cert
|
||||||
type: string
|
type: string
|
||||||
info: Same as [etcd_client_cert](#etcd_client_cert), but only for Vitastor monitors.
|
info: |
|
||||||
info_ru: Аналогично [etcd_client_cert](#etcd_client_cert), но только для мониторов Vitastor.
|
Client TLS certificate to use for connections from Vitastor Monitors to etcd/Antietcd - required
|
||||||
|
if you don't use the built-in Antietcd. In case you use it Monitor has direct access to Antietcd data
|
||||||
|
and doesn't require any connection.
|
||||||
|
info_ru: |
|
||||||
|
Клиентский TLS сертификат для подключений от мониторов Vitastor к etcd/Antietcd - требуется, если
|
||||||
|
вы не используете встроенный в монитор Antietcd. Если вы используете его, то монитор и так имеет
|
||||||
|
прямой доступ к данным Antietcd и не требует никаких соединений.
|
||||||
- name: mon_etcd_client_key
|
- name: mon_etcd_client_key
|
||||||
type: string
|
type: string
|
||||||
info: Same as [etcd_client_key](#etcd_client_key), but only for Vitastor monitors.
|
info: Private key for mon_etcd_client_cert.
|
||||||
info_ru: Аналогично [etcd_client_key](#etcd_client_key), но только для мониторов Vitastor.
|
info_ru: Закрытый ключ для сертификата mon_etcd_client_cert.
|
||||||
|
- name: proto_checksums
|
||||||
|
type: string
|
||||||
|
default: payload
|
||||||
|
info: |
|
||||||
|
One of "full", "payload", "gcm", "none":
|
||||||
|
- "full" means calculate and verify transport level checksums from the full message data
|
||||||
|
including the header - recommended for unencrypted setups.
|
||||||
|
- "payload" enables checksums only for the actual read/write data, but skips them for message
|
||||||
|
headers - recommended for encrypted setups because headers are already protected by AES-GCM.
|
||||||
|
- "gcm" disables checksums and enables AES-GCM encryption of the whole messages including headers
|
||||||
|
and data - AES-GCM already includes MAC which is actually a stronger checksum. This option is
|
||||||
|
slower and is only recommended for untrusted networks.
|
||||||
|
- "none" disables transport level checksums at all.
|
||||||
|
info_ru: |
|
||||||
|
Одно из значений "full", "payload", "gcm" и "none":
|
||||||
|
- "full" означает расчёт и проверку контрольных сумм на транспортном уровне от полных сообщений,
|
||||||
|
включая их заголовки и данные - рекомендуется для кластеров без шифрования.
|
||||||
|
- "payload" включает контрольные суммы только для данных сообщений, но пропускает заголовки -
|
||||||
|
такая настройка рекомендуется для кластеров с включённым шифрованием, потому что в них заголовки
|
||||||
|
и так защищены шифрованием AES-GCM.
|
||||||
|
- "gcm" отключает контрольные суммы и включает шифрование полных сообщений включая заголовки и
|
||||||
|
данные - AES-GCM уже включает в себя MAC, который по сути является криптостойкой контрольной
|
||||||
|
суммой. Такая настройка медленнее и рекомендуется только для недоверенных сетей.
|
||||||
|
- "none" полностью отключает контрольные суммы на транспортном уровне.
|
||||||
|
- name: force_proto_checksums
|
||||||
|
type: string
|
||||||
|
info: |
|
||||||
|
To allow older clients to connect to a Vitastor cluster with enabled checksums, Vitastor OSDs
|
||||||
|
allow clients to downgrade their proto_checksums by default. force_proto_checksums sets the
|
||||||
|
minimum security level allowed for connecting clients. When encryption is disabled, default
|
||||||
|
force_proto_checksums is none and clients without checksums are allowed. With enabled
|
||||||
|
encryption, force_proto_checksums becomes "payload" by default to block unauthenticated data
|
||||||
|
on the transport level.
|
||||||
|
info_ru: |
|
||||||
|
Чтобы старые клиенты Vitastor могли подключаться к кластеру с включёнными контрольными
|
||||||
|
суммами, Vitastor OSD по умолчанию разрешают клиентам отключать контрольные суммы
|
||||||
|
данных (proto_checksums). Настройка force_proto_checksums задаёт минимальный уровень
|
||||||
|
безопасности, разрешённый для подключающихся клиентов. Когда шифрование отключено,
|
||||||
|
force_proto_checksums по умолчанию равно none и подключения клиентов без контрольных
|
||||||
|
сумм разрешаются. При включённом шифровании значение по умолчанию force_proto_checksums
|
||||||
|
становится "payload", чтобы блокировать подключения с неаутентифицированными данными.
|
||||||
|
- name: max_cipher_pool_size
|
||||||
|
type: int
|
||||||
|
default: 256
|
||||||
|
info: |
|
||||||
|
Maximum number of OpenSSL cipher contexts cached in OSD memory, counted separately
|
||||||
|
for each cipher and for encryption/decryption. Probably doesn't require modification.
|
||||||
|
info_ru: |
|
||||||
|
Максимальное количество кэшируемых в памяти OSD контекстов шифра OpenSSL, учитываемое
|
||||||
|
отдельно для каждого шифра и для шифрования и расшифровки. Вряд ли требует изменения.
|
||||||
- name: vault_url
|
- name: vault_url
|
||||||
type: string
|
type: string
|
||||||
info: |
|
info: |
|
||||||
@@ -81,15 +241,15 @@
|
|||||||
- name: vault_client_cert
|
- name: vault_client_cert
|
||||||
type: string
|
type: string
|
||||||
info: |
|
info: |
|
||||||
Client TLS certificate to use for Vault connections. Just like [etcd_client_cert](#etcd_client_cert),
|
Client TLS certificate to use for Vault connections if you don't want to use the common Vitastor
|
||||||
may be path to a file or just a certificate in PEM string.
|
client certificate [cert](#cert) which is also used for Vault connections by default.
|
||||||
info_ru: |
|
info_ru: |
|
||||||
Клиентский TLS сертификат для подключений к Vault. Как и [etcd_client_cert](#etcd_client_cert),
|
Клиентский TLS сертификат для подключений к Vault на тот случай, если вы не хотите использовать
|
||||||
может быть путём к файлу или просто PEM-строкой с сертификатом.
|
общий сертификат клиента Vitastor [cert](#cert), используемый для подключений к Vault по умолчанию.
|
||||||
- name: vault_client_key
|
- name: vault_client_key
|
||||||
type: string
|
type: string
|
||||||
info: Private key for vault_client_cert (also a file or a PEM string).
|
info: Private key for the vault_client_cert certificate.
|
||||||
info_ru: Закрытый ключ для сертификата vault_client_cert (также путь к файлу или PEM строка).
|
info_ru: Закрытый ключ для сертификата vault_client_cert.
|
||||||
- name: vault_ca
|
- name: vault_ca
|
||||||
type: string
|
type: string
|
||||||
info: |
|
info: |
|
||||||
@@ -120,12 +280,3 @@
|
|||||||
info_ru: |
|
info_ru: |
|
||||||
Зазор времени (в секундах), чтобы обновлять токены Vault чуть раньше их реального
|
Зазор времени (в секундах), чтобы обновлять токены Vault чуть раньше их реального
|
||||||
lease_timeout, на случай "ухода" системных часов.
|
lease_timeout, на случай "ухода" системных часов.
|
||||||
- name: max_aes_xts_pool_size
|
|
||||||
type: int
|
|
||||||
default: 256
|
|
||||||
info: |
|
|
||||||
Maximum number of OpenSSL encryption contexts cached in OSD memory. Probably
|
|
||||||
doesn't require modification.
|
|
||||||
info_ru: |
|
|
||||||
Максимальное количество кэшируемых в памяти OSD контекстов шифрования OpenSSL.
|
|
||||||
Вряд ли требует изменения.
|
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ at Vitastor Kubernetes operator: https://github.com/Antilles7227/vitastor-operat
|
|||||||
The instruction is very simple.
|
The instruction is very simple.
|
||||||
|
|
||||||
1. Download a Docker image of the desired version: \
|
1. Download a Docker image of the desired version: \
|
||||||
`docker pull vitalif/vitastor:v3.0.9`
|
`docker pull vitalif/vitastor:v3.2.1`
|
||||||
2. Install scripts to the host system: \
|
2. Install scripts to the host system: \
|
||||||
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.9 install.sh`
|
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.2.1 install.sh`
|
||||||
3. Reload udev rules: \
|
3. Reload udev rules: \
|
||||||
`udevadm control --reload-rules`
|
`udevadm control --reload-rules`
|
||||||
4. Enable the vitastor-host service: \
|
4. Enable the vitastor-host service: \
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ Vitastor можно установить в Docker/Podman. При этом etcd,
|
|||||||
Инструкция по установке максимально простая.
|
Инструкция по установке максимально простая.
|
||||||
|
|
||||||
1. Скачайте Docker-образ желаемой версии: \
|
1. Скачайте Docker-образ желаемой версии: \
|
||||||
`docker pull vitalif/vitastor:v3.0.9`
|
`docker pull vitalif/vitastor:v3.2.1`
|
||||||
2. Установите скрипты в хост-систему командой: \
|
2. Установите скрипты в хост-систему командой: \
|
||||||
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.9 install.sh`
|
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.2.1 install.sh`
|
||||||
3. Перезагрузите правила udev: \
|
3. Перезагрузите правила udev: \
|
||||||
`udevadm control --reload-rules`
|
`udevadm control --reload-rules`
|
||||||
4. Включите сервис vitastor-host: \
|
4. Включите сервис vitastor-host: \
|
||||||
|
|||||||
@@ -14,12 +14,9 @@
|
|||||||
- Debian 13 (Trixie/Sid): `deb https://vitastor.io/debian trixie main`
|
- Debian 13 (Trixie/Sid): `deb https://vitastor.io/debian trixie main`
|
||||||
- Debian 12 (Bookworm): `deb https://vitastor.io/debian bookworm main`
|
- Debian 12 (Bookworm): `deb https://vitastor.io/debian bookworm main`
|
||||||
- Debian 11 (Bullseye): `deb https://vitastor.io/debian bullseye main`
|
- Debian 11 (Bullseye): `deb https://vitastor.io/debian bullseye main`
|
||||||
- Debian 10 (Buster): `deb https://vitastor.io/debian buster main`
|
|
||||||
- Ubuntu 22.04 (Jammy): `deb https://vitastor.io/debian jammy main`
|
|
||||||
- Ubuntu 24.04 (Noble): `deb https://vitastor.io/debian noble main`
|
|
||||||
- Ubuntu 26.04 (Resolute): `deb https://vitastor.io/debian resolute main`
|
- Ubuntu 26.04 (Resolute): `deb https://vitastor.io/debian resolute main`
|
||||||
- Add `-oldstable` to bookworm/bullseye/buster in this line to install the last
|
- Ubuntu 24.04 (Noble): `deb https://vitastor.io/debian noble main`
|
||||||
stable version from 0.9.x branch instead of 1.x
|
- Ubuntu 22.04 (Jammy): `deb https://vitastor.io/debian jammy main`
|
||||||
- To always prefer vitastor-patched QEMU and Libvirt versions, add the following to `/etc/apt/preferences`:
|
- To always prefer vitastor-patched QEMU and Libvirt versions, add the following to `/etc/apt/preferences`:
|
||||||
```
|
```
|
||||||
Package: *
|
Package: *
|
||||||
@@ -31,17 +28,14 @@
|
|||||||
## CentOS
|
## CentOS
|
||||||
|
|
||||||
- Add Vitastor package repository:
|
- Add Vitastor package repository:
|
||||||
- CentOS 7: `yum install https://vitastor.io/rpms/centos/7/vitastor-release.rpm`
|
|
||||||
- CentOS 8: `dnf install https://vitastor.io/rpms/centos/8/vitastor-release.rpm`
|
- CentOS 8: `dnf install https://vitastor.io/rpms/centos/8/vitastor-release.rpm`
|
||||||
- AlmaLinux 9 and other RHEL 9 clones (Rocky, Oracle...): `dnf install https://vitastor.io/rpms/centos/9/vitastor-release.rpm`
|
- AlmaLinux 9 and other RHEL 9 clones (Rocky, Oracle...): `dnf install https://vitastor.io/rpms/centos/9/vitastor-release.rpm`
|
||||||
- AlmaLinux 10 and other RHEL 10 clones: `dnf install https://vitastor.io/rpms/centos/10/vitastor-release.rpm`
|
- AlmaLinux 10 and other RHEL 10 clones: `dnf install https://vitastor.io/rpms/centos/10/vitastor-release.rpm`
|
||||||
- Enable EPEL: `yum/dnf install epel-release`
|
- Enable EPEL: `yum/dnf install epel-release`
|
||||||
- Enable additional CentOS repositories:
|
- Enable additional CentOS repositories:
|
||||||
- CentOS 7: `yum install centos-release-scl`
|
|
||||||
- CentOS 8: `dnf install centos-release-advanced-virtualization`
|
- CentOS 8: `dnf install centos-release-advanced-virtualization`
|
||||||
- RHEL 9/10 clones: not required
|
- RHEL 9/10 clones: not required
|
||||||
- Enable elrepo-kernel:
|
- Enable elrepo-kernel:
|
||||||
- CentOS 7: `yum install https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm`
|
|
||||||
- CentOS 8: `dnf install https://www.elrepo.org/elrepo-release-8.el8.elrepo.noarch.rpm`
|
- CentOS 8: `dnf install https://www.elrepo.org/elrepo-release-8.el8.elrepo.noarch.rpm`
|
||||||
- RHEL 9 clones: `dnf install https://www.elrepo.org/elrepo-release-9.el9.elrepo.noarch.rpm`
|
- RHEL 9 clones: `dnf install https://www.elrepo.org/elrepo-release-9.el9.elrepo.noarch.rpm`
|
||||||
- RHEL 10 clones: not required
|
- RHEL 10 clones: not required
|
||||||
|
|||||||
@@ -14,12 +14,9 @@
|
|||||||
- Debian 13 (Trixie/Sid): `deb https://vitastor.io/debian trixie main`
|
- Debian 13 (Trixie/Sid): `deb https://vitastor.io/debian trixie main`
|
||||||
- Debian 12 (Bookworm): `deb https://vitastor.io/debian bookworm main`
|
- Debian 12 (Bookworm): `deb https://vitastor.io/debian bookworm main`
|
||||||
- Debian 11 (Bullseye): `deb https://vitastor.io/debian bullseye main`
|
- Debian 11 (Bullseye): `deb https://vitastor.io/debian bullseye main`
|
||||||
- Debian 10 (Buster): `deb https://vitastor.io/debian buster main`
|
|
||||||
- Ubuntu 22.04 (Jammy): `deb https://vitastor.io/debian jammy main`
|
|
||||||
- Ubuntu 24.04 (Noble): `deb https://vitastor.io/debian noble main`
|
|
||||||
- Ubuntu 26.04 (Resolute): `deb https://vitastor.io/debian resolute main`
|
- Ubuntu 26.04 (Resolute): `deb https://vitastor.io/debian resolute main`
|
||||||
- Добавьте `-oldstable` к слову bookworm/bullseye/buster в этой строке, чтобы
|
- Ubuntu 24.04 (Noble): `deb https://vitastor.io/debian noble main`
|
||||||
установить последнюю стабильную версию из ветки 0.9.x вместо 1.x
|
- Ubuntu 22.04 (Jammy): `deb https://vitastor.io/debian jammy main`
|
||||||
- Чтобы всегда предпочитались версии пакетов QEMU и Libvirt с патчами Vitastor, добавьте в `/etc/apt/preferences`:
|
- Чтобы всегда предпочитались версии пакетов QEMU и Libvirt с патчами Vitastor, добавьте в `/etc/apt/preferences`:
|
||||||
```
|
```
|
||||||
Package: *
|
Package: *
|
||||||
@@ -31,17 +28,14 @@
|
|||||||
## CentOS
|
## CentOS
|
||||||
|
|
||||||
- Добавьте в систему репозиторий Vitastor:
|
- Добавьте в систему репозиторий Vitastor:
|
||||||
- CentOS 7: `yum install https://vitastor.io/rpms/centos/7/vitastor-release.rpm`
|
|
||||||
- CentOS 8: `dnf install https://vitastor.io/rpms/centos/8/vitastor-release.rpm`
|
- CentOS 8: `dnf install https://vitastor.io/rpms/centos/8/vitastor-release.rpm`
|
||||||
- AlmaLinux 9 и другие клоны RHEL 9 (Rocky, Oracle...): `dnf install https://vitastor.io/rpms/centos/9/vitastor-release.rpm`
|
- AlmaLinux 9 и другие клоны RHEL 9 (Rocky, Oracle...): `dnf install https://vitastor.io/rpms/centos/9/vitastor-release.rpm`
|
||||||
- AlmaLinux 10 и другие клоны RHEL 10: `dnf install https://vitastor.io/rpms/centos/10/vitastor-release.rpm`
|
- AlmaLinux 10 и другие клоны RHEL 10: `dnf install https://vitastor.io/rpms/centos/10/vitastor-release.rpm`
|
||||||
- Включите EPEL: `yum/dnf install epel-release`
|
- Включите EPEL: `yum/dnf install epel-release`
|
||||||
- Включите дополнительные репозитории CentOS:
|
- Включите дополнительные репозитории CentOS:
|
||||||
- CentOS 7: `yum install centos-release-scl`
|
|
||||||
- CentOS 8: `dnf install centos-release-advanced-virtualization`
|
- CentOS 8: `dnf install centos-release-advanced-virtualization`
|
||||||
- Клоны RHEL 9/10: не нужно
|
- Клоны RHEL 9/10: не нужно
|
||||||
- Включите elrepo-kernel:
|
- Включите elrepo-kernel:
|
||||||
- CentOS 7: `yum install https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm`
|
|
||||||
- CentOS 8: `dnf install https://www.elrepo.org/elrepo-release-8.el8.elrepo.noarch.rpm`
|
- CentOS 8: `dnf install https://www.elrepo.org/elrepo-release-8.el8.elrepo.noarch.rpm`
|
||||||
- Клоны RHEL 9: `dnf install https://www.elrepo.org/elrepo-release-9.el9.elrepo.noarch.rpm`
|
- Клоны RHEL 9: `dnf install https://www.elrepo.org/elrepo-release-9.el9.elrepo.noarch.rpm`
|
||||||
- Клоны RHEL 10: не нужно
|
- Клоны RHEL 10: не нужно
|
||||||
|
|||||||
@@ -6,10 +6,10 @@
|
|||||||
|
|
||||||
# Proxmox VE
|
# Proxmox VE
|
||||||
|
|
||||||
To enable Vitastor support in Proxmox Virtual Environment (6.4-9.x are supported):
|
To enable Vitastor support in Proxmox Virtual Environment (7.x-9.x are supported):
|
||||||
|
|
||||||
- Add the corresponding Vitastor Debian repository into sources.list on Proxmox hosts:
|
- Add the corresponding Vitastor Debian repository into sources.list on Proxmox hosts:
|
||||||
trixie for 9.0+, bookworm for 8.1+, pve8.0 for 8.0, bullseye for 7.4, pve7.3 for 7.3, pve7.2 for 7.2, pve7.1 for 7.1, buster for 6.4
|
trixie for 9.0+, bookworm for 8.1+, pve8.0 for 8.0, bullseye for 7.4, pve7.3 for 7.3, pve7.2 for 7.2, pve7.1 for 7.1
|
||||||
- Install vitastor-client, pve-qemu-kvm, pve-storage-vitastor (* or see note) packages from Vitastor repository
|
- Install vitastor-client, pve-qemu-kvm, pve-storage-vitastor (* or see note) packages from Vitastor repository
|
||||||
- Define storage in `/etc/pve/storage.cfg` (see below)
|
- Define storage in `/etc/pve/storage.cfg` (see below)
|
||||||
- Block network access from VMs to Vitastor network (to OSDs and etcd),
|
- Block network access from VMs to Vitastor network (to OSDs and etcd),
|
||||||
|
|||||||
@@ -6,10 +6,10 @@
|
|||||||
|
|
||||||
# Proxmox VE
|
# Proxmox VE
|
||||||
|
|
||||||
Чтобы подключить Vitastor к Proxmox Virtual Environment (поддерживаются версии 6.4-9.x):
|
Чтобы подключить Vitastor к Proxmox Virtual Environment (поддерживаются версии 7.x-9.x):
|
||||||
|
|
||||||
- Добавьте соответствующий Debian-репозиторий Vitastor в sources.list на хостах Proxmox:
|
- Добавьте соответствующий Debian-репозиторий Vitastor в sources.list на хостах Proxmox:
|
||||||
trixie для 9.0+, bookworm для 8.1+, pve8.0 для 8.0, bullseye для 7.4, pve7.3 для 7.3, pve7.2 для 7.2, pve7.1 для 7.1, buster для 6.4
|
trixie для 9.0+, bookworm для 8.1+, pve8.0 для 8.0, bullseye для 7.4, pve7.3 для 7.3, pve7.2 для 7.2, pve7.1 для 7.1
|
||||||
- Установите пакеты vitastor-client, pve-qemu-kvm, pve-storage-vitastor (* или см. сноску) из репозитория Vitastor
|
- Установите пакеты vitastor-client, pve-qemu-kvm, pve-storage-vitastor (* или см. сноску) из репозитория Vitastor
|
||||||
- Определите тип хранилища в `/etc/pve/storage.cfg` (см. ниже)
|
- Определите тип хранилища в `/etc/pve/storage.cfg` (см. ниже)
|
||||||
- Обязательно заблокируйте доступ от виртуальных машин к сети Vitastor (OSD и etcd), т.к. Vitastor (пока) не поддерживает аутентификацию
|
- Обязательно заблокируйте доступ от виртуальных машин к сети Vitastor (OSD и etcd), т.к. Vitastor (пока) не поддерживает аутентификацию
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
- CMake
|
- CMake
|
||||||
- jerasure, c-ares headers and libraries
|
- jerasure, c-ares headers and libraries
|
||||||
- ISA-L, libibverbs, librdmacm, libnl3 headers and libraries (optional)
|
- ISA-L, libibverbs, librdmacm, libnl3 headers and libraries (optional)
|
||||||
- tcmalloc (google-perftools-dev)
|
|
||||||
|
|
||||||
## Basic instructions
|
## Basic instructions
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
- CMake
|
- CMake
|
||||||
- Заголовки и библиотеки jerasure, c-ares
|
- Заголовки и библиотеки jerasure, c-ares
|
||||||
- Опционально - заголовки и библиотеки ISA-L, libibverbs, librdmacm, libnl3
|
- Опционально - заголовки и библиотеки ISA-L, libibverbs, librdmacm, libnl3
|
||||||
- tcmalloc (google-perftools-dev)
|
|
||||||
|
|
||||||
## Базовая инструкция
|
## Базовая инструкция
|
||||||
|
|
||||||
|
|||||||
@@ -41,8 +41,8 @@
|
|||||||
- [Built-in Prometheus metric exporter](../config/monitor.en.md#enable_prometheus)
|
- [Built-in Prometheus metric exporter](../config/monitor.en.md#enable_prometheus)
|
||||||
- [NFS RDMA support](../usage/nfs.en.md#rdma) (probably also usable for GPUDirect)
|
- [NFS RDMA support](../usage/nfs.en.md#rdma) (probably also usable for GPUDirect)
|
||||||
- [S3](../installation/s3.en.md)
|
- [S3](../installation/s3.en.md)
|
||||||
- [TLS support for etcd connections](../config/security.en.md)
|
- [Full transport encryption and access control](../usage/security.en.md)
|
||||||
- [AES-256-XTS image encryption](../usage/cli.en.md#create) and [Vault support](../config/security.en.md#vault_url) for key storage
|
- [AES-256-XTS end-to-end image encryption](../usage/security.en.md#end-to-end-image-data-encryption-aes-xts) with [Vault support](../usage/security.en.md#vaultopenbao-setup) for key storage
|
||||||
|
|
||||||
## Plugins and tools
|
## Plugins and tools
|
||||||
|
|
||||||
|
|||||||
@@ -43,8 +43,8 @@
|
|||||||
- [Встроенный Prometheus-экспортер метрик](../config/monitor.ru.md#enable_prometheus)
|
- [Встроенный Prometheus-экспортер метрик](../config/monitor.ru.md#enable_prometheus)
|
||||||
- [Поддержка NFS RDMA](../usage/nfs.ru.md#rdma) (вероятно, также подходящая для GPUDirect)
|
- [Поддержка NFS RDMA](../usage/nfs.ru.md#rdma) (вероятно, также подходящая для GPUDirect)
|
||||||
- [S3](../installation/s3.ru.md)
|
- [S3](../installation/s3.ru.md)
|
||||||
- [Поддержка TLS-соединений с etcd](../config/security.ru.md)
|
- [Полное транспортное шифрование и контроль доступа](../usage/security.ru.md)
|
||||||
- [AES-256-XTS шифрование данных](../usage/cli.ru.md#create) и [поддержка Vault](../config/security.ru.md#vault_url) для хранения ключей
|
- [AES-256-XTS сквозное шифрование данных](../usage/security.ru.md#сквозное-шифрование-данных-образов-aes-xts) с [поддержкой Vault](../usage/security.ru.md#настройка-vaultopenbao) для хранения ключей
|
||||||
|
|
||||||
## Драйверы и инструменты
|
## Драйверы и инструменты
|
||||||
|
|
||||||
|
|||||||
@@ -41,12 +41,16 @@
|
|||||||
## Configure monitors
|
## Configure monitors
|
||||||
|
|
||||||
On the monitor hosts:
|
On the monitor hosts:
|
||||||
- Put identical etcd_address into `/etc/vitastor/vitastor.conf`. Example:
|
- Create minimal configuration in `/etc/vitastor/vitastor.conf`:
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
"etcd_address": ["10.200.1.10:2379","10.200.1.11:2379","10.200.1.12:2379"]
|
"etcd_address": ["http://10.200.1.10:2379","http://10.200.1.11:2379","http://10.200.1.12:2379"],
|
||||||
|
"osd_network": "10.200.1.0/24",
|
||||||
|
"use_perms": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
- Note that you can enable encryption by using `https://` and `use_perms` option.
|
||||||
|
[Details](security.en.md#quick-setup) about encryption setup with make-etcd.
|
||||||
- Create systemd units for etcd by running: `/usr/lib/vitastor/mon/make-etcd`
|
- Create systemd units for etcd by running: `/usr/lib/vitastor/mon/make-etcd`
|
||||||
Or, if you installed Vitastor in Docker, run `systemctl start vitastor-host; docker exec vitastor make-etcd`.
|
Or, if you installed Vitastor in Docker, run `systemctl start vitastor-host; docker exec vitastor make-etcd`.
|
||||||
- Start etcd and monitors: `systemctl enable --now vitastor-etcd vitastor-mon`
|
- Start etcd and monitors: `systemctl enable --now vitastor-etcd vitastor-mon`
|
||||||
|
|||||||
@@ -41,25 +41,23 @@
|
|||||||
## Настройте мониторы
|
## Настройте мониторы
|
||||||
|
|
||||||
На хостах, выделенных под мониторы:
|
На хостах, выделенных под мониторы:
|
||||||
- Пропишите одинаковые etcd_address в `/etc/vitastor/vitastor.conf`. Например:
|
- Создайте минимальную конфигурацию в `/etc/vitastor/vitastor.conf`:
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
"etcd_address": ["10.200.1.10:2379","10.200.1.11:2379","10.200.1.12:2379"]
|
"etcd_address": ["http://10.200.1.10:2379","http://10.200.1.11:2379","http://10.200.1.12:2379"],
|
||||||
|
"osd_network": "10.200.1.0/24",
|
||||||
|
"use_perms": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
- Обратите внимание, что с помощью схемы `https://` и опции `use_perms` можно включить шифрование.
|
||||||
|
[Подробно](security.ru.md#быстрая-настройка) о настройке шифрования через make-etcd.
|
||||||
- Инициализируйте сервисы etcd, запустив `/usr/lib/vitastor/mon/make-etcd`.\
|
- Инициализируйте сервисы etcd, запустив `/usr/lib/vitastor/mon/make-etcd`.\
|
||||||
Либо, если вы установили Vitastor в Docker, запустите `systemctl start vitastor-host; docker exec vitastor make-etcd`.
|
Либо, если вы установили Vitastor в Docker, запустите `systemctl start vitastor-host; docker exec vitastor make-etcd`.
|
||||||
- Запустите etcd и мониторы: `systemctl enable --now vitastor-etcd vitastor-mon`
|
- Запустите etcd и мониторы: `systemctl enable --now vitastor-etcd vitastor-mon`
|
||||||
|
|
||||||
## Настройте OSD
|
## Настройте OSD
|
||||||
|
|
||||||
- Пропишите etcd_address и [osd_network](../config/network.ru.md#osd_network) в `/etc/vitastor/vitastor.conf`. Например:
|
- Создайте/скопируйте с узлов с мониторами файл конфигурации `/etc/vitastor/vitastor.conf`.
|
||||||
```
|
|
||||||
{
|
|
||||||
"etcd_address": ["10.200.1.10:2379","10.200.1.11:2379","10.200.1.12:2379"],
|
|
||||||
"osd_network": "10.200.1.0/24"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
- Инициализуйте OSD:
|
- Инициализуйте OSD:
|
||||||
- Только SSD или только HDD: `vitastor-disk prepare /dev/sdXXX [/dev/sdYYY ...]`.
|
- Только SSD или только HDD: `vitastor-disk prepare /dev/sdXXX [/dev/sdYYY ...]`.
|
||||||
Если вы используете десктопные SSD без конденсаторов, добавьте опцию `--disable_data_fsync off`,
|
Если вы используете десктопные SSD без конденсаторов, добавьте опцию `--disable_data_fsync off`,
|
||||||
|
|||||||
@@ -33,6 +33,11 @@ It supports the following commands:
|
|||||||
- [modify-pool](#modify-pool)
|
- [modify-pool](#modify-pool)
|
||||||
- [ls-pools](#ls-pools)
|
- [ls-pools](#ls-pools)
|
||||||
- [rm-pool](#rm-pool)
|
- [rm-pool](#rm-pool)
|
||||||
|
- [ls-user](#ls-user)
|
||||||
|
- [modify-user](#modify-user)
|
||||||
|
- [rm-user](#rm-user)
|
||||||
|
- [serve](#serve)
|
||||||
|
- [cpubench](#cpubench)
|
||||||
|
|
||||||
Global options:
|
Global options:
|
||||||
|
|
||||||
@@ -107,6 +112,7 @@ Options:
|
|||||||
--sort FIELD Sort by specified field (name, size, used_size, <read|write|delete>_<iops|bps|lat|queue>)
|
--sort FIELD Sort by specified field (name, size, used_size, <read|write|delete>_<iops|bps|lat|queue>)
|
||||||
-r|--reverse Sort in descending order
|
-r|--reverse Sort in descending order
|
||||||
-n|--count N Only list first N items
|
-n|--count N Only list first N items
|
||||||
|
--ids ID1,ID2 Only list images with specified full IDs
|
||||||
--tree Show image snapshot/clone tree
|
--tree Show image snapshot/clone tree
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -136,6 +142,10 @@ Create an image. Options:
|
|||||||
* `--enc-key random` - Generate a new random AES-256-XTS encryption key for the new image.
|
* `--enc-key random` - Generate a new random AES-256-XTS encryption key for the new image.
|
||||||
* `--enc-key HEX` - Set a specified AES-256-XTS key (64 bytes in hex) for the new image.
|
* `--enc-key HEX` - Set a specified AES-256-XTS key (64 bytes in hex) for the new image.
|
||||||
* `--enc-key vault:ID` - Use an encryption key from an external Vault secret with specified ID.
|
* `--enc-key vault:ID` - Use an encryption key from an external Vault secret with specified ID.
|
||||||
|
* `--owner USERNAME` - Set image owner. The default owner is the current user taken from the
|
||||||
|
CN of the TLS certificate used to connect to the cluster.
|
||||||
|
* `--owner-group NAME` - Set image owner group. Users in this group get full access to the image.
|
||||||
|
* `--reader-group NAME` - Set image reader group. Users in this group get read-only access to the image.
|
||||||
|
|
||||||
```
|
```
|
||||||
vitastor-cli create --snapshot <snapshot> [OPTIONS] <image>
|
vitastor-cli create --snapshot <snapshot> [OPTIONS] <image>
|
||||||
@@ -165,6 +175,9 @@ You should resize file system in the image, if present, before shrinking it.
|
|||||||
* `-f|--force` - Proceed with shrinking or setting readwrite flag even if the image has children.
|
* `-f|--force` - Proceed with shrinking or setting readwrite flag even if the image has children.
|
||||||
* `--down-ok` - Proceed with shrinking even if some data will be left on unavailable OSDs.
|
* `--down-ok` - Proceed with shrinking even if some data will be left on unavailable OSDs.
|
||||||
* `--enc-key HEX` - Change image encryption key (allowed only with `--force`).
|
* `--enc-key HEX` - Change image encryption key (allowed only with `--force`).
|
||||||
|
* `--owner USERNAME` - Change image owner.
|
||||||
|
* `--owner-group NAME` - Change image owner group name.
|
||||||
|
* `--reader-group NAME` - Change image reader group name.
|
||||||
|
|
||||||
## dd
|
## dd
|
||||||
|
|
||||||
@@ -183,6 +196,7 @@ Options can be specified in classic dd style (`key=value`) or like usual (`--key
|
|||||||
| `iimg=<image>` | Copy from Vitastor image `<image>` |
|
| `iimg=<image>` | Copy from Vitastor image `<image>` |
|
||||||
| `if=<file>` | Copy from file `<file>` |
|
| `if=<file>` | Copy from file `<file>` |
|
||||||
| `oimg=<image>` | Copy to Vitastor image `<image>` |
|
| `oimg=<image>` | Copy to Vitastor image `<image>` |
|
||||||
|
| `opool=<pool>` | Pool to create output image if it doesn't exist |
|
||||||
| `of=<file>` | Copy to file `<file>` |
|
| `of=<file>` | Copy to file `<file>` |
|
||||||
| `bs=1M` | Set copy block size |
|
| `bs=1M` | Set copy block size |
|
||||||
| `count=N` | Copy only N input blocks. If N ends in B it counts bytes, not blocks |
|
| `count=N` | Copy only N input blocks. If N ends in B it counts bytes, not blocks |
|
||||||
@@ -420,6 +434,7 @@ Optional parameters:
|
|||||||
| `--used_for_app s3:<name>` | Mark pool as used for S3 location with name `<name>` |
|
| `--used_for_app s3:<name>` | Mark pool as used for S3 location with name `<name>` |
|
||||||
| `--pg_stripe_size <number>` | Increase object grouping stripe |
|
| `--pg_stripe_size <number>` | Increase object grouping stripe |
|
||||||
| `--max_osd_combinations 10000` | Maximum number of random combinations for LP solver input |
|
| `--max_osd_combinations 10000` | Maximum number of random combinations for LP solver input |
|
||||||
|
| `--creator_group <group>` | User group allowed to create images in this pool |
|
||||||
| `--wait` | Wait for the new pool to come online |
|
| `--wait` | Wait for the new pool to come online |
|
||||||
| `-f` or `--force` | Do not check that cluster has enough OSDs to create the pool |
|
| `-f` or `--force` | Do not check that cluster has enough OSDs to create the pool |
|
||||||
|
|
||||||
@@ -439,8 +454,9 @@ Modify an existing pool. Modifiable parameters:
|
|||||||
|
|
||||||
```
|
```
|
||||||
[-s|--pg_size <number>] [--pg_minsize <number>] [-n|--pg_count <count>]
|
[-s|--pg_size <number>] [--pg_minsize <number>] [-n|--pg_count <count>]
|
||||||
[--failure_domain <level>] [--root_node <node>] [--osd_tags <tags>] [--no_inode_stats 0|1]
|
[--failure_domain <level>] [--root_node <node>] [--osd_tags <tags>] [--used_for_app <type>:<name>]
|
||||||
[--max_osd_combinations <number>] [--primary_affinity_tags <tags>] [--scrub_interval <time>]
|
[--max_osd_combinations <number>] [--primary_affinity_tags <tags>] [--scrub_interval <time>]
|
||||||
|
[--level_placement <rules>] [--raw_placement <rules>] [--creator_group <group>]
|
||||||
```
|
```
|
||||||
|
|
||||||
Non-modifiable parameters (changing them WILL lead to data loss):
|
Non-modifiable parameters (changing them WILL lead to data loss):
|
||||||
@@ -468,7 +484,7 @@ Remove a pool. Refuses to remove pools with images without `--force`.
|
|||||||
|
|
||||||
## ls-pools
|
## ls-pools
|
||||||
|
|
||||||
`vitastor-cli ls-pools|pool-ls|ls-pool|pools [-l] [--detail] [--sort FIELD] [-r] [-n N] [--stats] [<glob> ...]`
|
`vitastor-cli ls-pools|pool-ls|ls-pool|pools [-l] [--detail] [--sort FIELD] [-r] [-n N] [<glob> ...]`
|
||||||
|
|
||||||
List pools (only matching <glob> patterns if passed).
|
List pools (only matching <glob> patterns if passed).
|
||||||
|
|
||||||
@@ -479,3 +495,77 @@ List pools (only matching <glob> patterns if passed).
|
|||||||
| `--sort FIELD` | Sort by specified field (see fields in --json output) |
|
| `--sort FIELD` | Sort by specified field (see fields in --json output) |
|
||||||
| `-r` or `--reverse` | Sort in descending order |
|
| `-r` or `--reverse` | Sort in descending order |
|
||||||
| `-n` or `--count N` | Only list first N items |
|
| `-n` or `--count N` | Only list first N items |
|
||||||
|
|
||||||
|
## ls-user
|
||||||
|
|
||||||
|
`vitastor-cli ls-users|user-ls|ls-user|list-users [<name> ...]`
|
||||||
|
|
||||||
|
List users. If names are passed, only list users with those names. User names must
|
||||||
|
match CN (Common Name) of TLS client certificates used to connect to the cluster.
|
||||||
|
|
||||||
|
See [Users and access rights](security.en.md#users-and-access-rights) for details on
|
||||||
|
the Vitastor authorization model.
|
||||||
|
|
||||||
|
## modify-user
|
||||||
|
|
||||||
|
`vitastor-cli modify-user --groups <group1,group2,...> <username>`
|
||||||
|
|
||||||
|
Create or update a user with the given name (matching TLS certificate CN).
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
* `--groups GROUPS` - Set user's groups (comma-separated). Groups are used together with
|
||||||
|
`--owner-group` / `--reader-group` fields of images and `--creator_group` of the pool to
|
||||||
|
authorize operations. Pass an empty string to clear the groups.
|
||||||
|
|
||||||
|
## rm-user
|
||||||
|
|
||||||
|
`vitastor-cli rm-user|remove-user|delete-user <username>`
|
||||||
|
|
||||||
|
Remove a user.
|
||||||
|
|
||||||
|
## serve
|
||||||
|
|
||||||
|
`vitastor-cli serve`
|
||||||
|
|
||||||
|
Start an HTTP server that handles vitastor-cli commands over a REST API in JSON format.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
| <!-- --> | <!-- --> |
|
||||||
|
|-----------------------|-------------------------------------------------------------------------------|
|
||||||
|
| `--bind_address ADDR` | Server IP address(es), separated by space. Default is `127.0.0.1`. |
|
||||||
|
| `--port 8080` | Server port. Default is `8080`. |
|
||||||
|
| `--api_cert FILE` | Server TLS certificate. |
|
||||||
|
| `--api_pkey FILE` | Private key for `--api_cert`. |
|
||||||
|
| `--cert FILE` | Administrator or client certificate used by the server to access the cluster. |
|
||||||
|
| `--pkey FILE` | Private key for `--cert`. |
|
||||||
|
|
||||||
|
When `api_cert`/`api_pkey` are set the server operates in HTTPS mode. If `client_ca`
|
||||||
|
is also set (in configuration or on the command line), connecting clients are authenticated by their
|
||||||
|
TLS certificates. When [use_perms](../config/security.en.md#use_perms) is enabled, HTTPS with client
|
||||||
|
authentication is mandatory and per-user permissions are enforced. For regular clients the server only
|
||||||
|
allows operations on images that the user owns or has group access to; all other operations require
|
||||||
|
administrator privileges.
|
||||||
|
|
||||||
|
The vitastor-cli process itself must be authenticated as an admin user (its own client
|
||||||
|
[cert](../config/security.en.md#cert) must be signed with [admin_ca](../config/security.en.md#admin_ca))
|
||||||
|
to be able to serve all requests.
|
||||||
|
|
||||||
|
`vitastor-cli serve` exposes a full OpenAPI specification at `/openapi` endpoint.
|
||||||
|
Start the server and check it out for the information about available API methods.
|
||||||
|
|
||||||
|
See also [Security in Vitastor](security.en.md) for the full picture of how authentication
|
||||||
|
and authorization work.
|
||||||
|
|
||||||
|
## cpubench
|
||||||
|
|
||||||
|
`vitastor-cli cpubench [--json]`
|
||||||
|
|
||||||
|
Run CPU crypto performance tests: AES-256-GCM, AES-256-XTS and xxhash3. This benchmark
|
||||||
|
does not connect to the cluster — it only measures encryption/hashing speed locally.
|
||||||
|
|
||||||
|
Specify `--json` to get the output as machine-readable JSON.
|
||||||
|
|
||||||
|
See [Encryption performance](security.en.md#encryption-performance) for example results
|
||||||
|
on modern and older CPUs.
|
||||||
|
|||||||
@@ -34,6 +34,11 @@ vitastor-cli - интерфейс командной строки для адм
|
|||||||
- [modify-pool](#modify-pool)
|
- [modify-pool](#modify-pool)
|
||||||
- [ls-pools](#ls-pools)
|
- [ls-pools](#ls-pools)
|
||||||
- [rm-pool](#rm-pool)
|
- [rm-pool](#rm-pool)
|
||||||
|
- [ls-user](#ls-user)
|
||||||
|
- [modify-user](#modify-user)
|
||||||
|
- [rm-user](#rm-user)
|
||||||
|
- [serve](#serve)
|
||||||
|
- [cpubench](#cpubench)
|
||||||
|
|
||||||
Глобальные опции:
|
Глобальные опции:
|
||||||
|
|
||||||
@@ -109,6 +114,7 @@ kaveri 2/1 32 0 B 10 G 0 B 100% 0%
|
|||||||
--sort FIELD Сортировать по заданному полю (name, size, used_size, <read|write|delete>_<iops|bps|lat|queue>)
|
--sort FIELD Сортировать по заданному полю (name, size, used_size, <read|write|delete>_<iops|bps|lat|queue>)
|
||||||
-r|--reverse Сортировать в обратном порядке
|
-r|--reverse Сортировать в обратном порядке
|
||||||
-n|--count N Показывать только первые N записей
|
-n|--count N Показывать только первые N записей
|
||||||
|
--ids ID1,ID2 Показывать только образы с заданными полными ID
|
||||||
--tree Вывести снапшоты и клоны в виде дерева
|
--tree Вывести снапшоты и клоны в виде дерева
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -138,6 +144,12 @@ bench-kaveri kaveri 10 G 10 G 0 B/s 0 0 0 us 0 B/s 0
|
|||||||
* `--enc-key random` - Сгенерировать случайный ключ шифрования AES-256-XTS для нового образа.
|
* `--enc-key random` - Сгенерировать случайный ключ шифрования AES-256-XTS для нового образа.
|
||||||
* `--enc-key HEX` - Установить заданный ключ AES-256-XTS (64 байта в hex) для нового образа.
|
* `--enc-key HEX` - Установить заданный ключ AES-256-XTS (64 байта в hex) для нового образа.
|
||||||
* `--enc-key vault:ID` - Использовать ключ из внешнего секрета с заданным ID из Vault.
|
* `--enc-key vault:ID` - Использовать ключ из внешнего секрета с заданным ID из Vault.
|
||||||
|
* `--owner USERNAME` - Задать владельца образа. По умолчанию владельцем становится текущий
|
||||||
|
пользователь, определяемый по CN TLS-сертификата, используемого для подключения к кластеру.
|
||||||
|
* `--owner-group NAME` - Задать группу владельцев образа. Пользователи из этой группы получают
|
||||||
|
полный доступ к образу.
|
||||||
|
* `--reader-group NAME` - Задать группу читателей образа. Пользователи из этой группы получают
|
||||||
|
доступ к образу только на чтение.
|
||||||
|
|
||||||
```
|
```
|
||||||
vitastor-cli create --snapshot <snapshot> [ОПЦИИ] <image>
|
vitastor-cli create --snapshot <snapshot> [ОПЦИИ] <image>
|
||||||
@@ -170,6 +182,9 @@ vitastor-cli snap-create [ОПЦИИ] <image>@<snapshot>
|
|||||||
* `-f|--force` - Разрешить уменьшение или перевод в чтение-запись образа, у которого есть клоны.
|
* `-f|--force` - Разрешить уменьшение или перевод в чтение-запись образа, у которого есть клоны.
|
||||||
* `--down-ok` - Разрешить уменьшение, даже если часть данных останется неудалённой на недоступных OSD.
|
* `--down-ok` - Разрешить уменьшение, даже если часть данных останется неудалённой на недоступных OSD.
|
||||||
* `--enc-key HEX` - Изменить ключ шифрования образа (разрешено только с `--force`).
|
* `--enc-key HEX` - Изменить ключ шифрования образа (разрешено только с `--force`).
|
||||||
|
* `--owner USERNAME` - Изменить владельца образа.
|
||||||
|
* `--owner-group NAME` - Изменить имя группы владельцев образа.
|
||||||
|
* `--reader-group NAME` - Изменить имя группы читателей образа.
|
||||||
|
|
||||||
## dd
|
## dd
|
||||||
|
|
||||||
@@ -188,6 +203,7 @@ vitastor-cli dd [iimg=<image> | if=<file>] [oimg=<image> | of=<file>] [bs=1M] \
|
|||||||
| `iimg=<image>` | Копировать из образа Vitastor `<image>` |
|
| `iimg=<image>` | Копировать из образа Vitastor `<image>` |
|
||||||
| `if=<file>` | Копировать из файла `<file>` |
|
| `if=<file>` | Копировать из файла `<file>` |
|
||||||
| `oimg=<image>` | Копировать в образ Vitastor `<image>` |
|
| `oimg=<image>` | Копировать в образ Vitastor `<image>` |
|
||||||
|
| `opool=<pool>` | Пул для создания выходного образа, если он не существует |
|
||||||
| `of=<file>` | Копировать в файл `<file>` |
|
| `of=<file>` | Копировать в файл `<file>` |
|
||||||
| `bs=1M` | Задать размер блока копирования |
|
| `bs=1M` | Задать размер блока копирования |
|
||||||
| `count=N` | Копировать не более N блоков. Если N заканчивается на B - то N байт. |
|
| `count=N` | Копировать не более N блоков. Если N заканчивается на B - то N байт. |
|
||||||
@@ -435,6 +451,7 @@ OSD PARENT UP SIZE USED% TAGS WEIGHT BLOCK BITMAP
|
|||||||
| `--scrub_interval <time>` | Включить скрабы с заданным интервалом времени (число + единица s/m/h/d/M/y) |
|
| `--scrub_interval <time>` | Включить скрабы с заданным интервалом времени (число + единица s/m/h/d/M/y) |
|
||||||
| `--pg_stripe_size <number>` | Увеличить блок группировки объектов по PG |
|
| `--pg_stripe_size <number>` | Увеличить блок группировки объектов по PG |
|
||||||
| `--max_osd_combinations 10000` | Максимальное число случайных комбинаций OSD для ЛП-солвера |
|
| `--max_osd_combinations 10000` | Максимальное число случайных комбинаций OSD для ЛП-солвера |
|
||||||
|
| `--creator_group <group>` | Группа пользователей, которым разрешено создавать образы в данном пуле |
|
||||||
| `--wait` | Подождать, пока новый пул будет активирован |
|
| `--wait` | Подождать, пока новый пул будет активирован |
|
||||||
| `-f` или `--force` | Не проверять, что в кластере достаточно доменов отказа для создания пула |
|
| `-f` или `--force` | Не проверять, что в кластере достаточно доменов отказа для создания пула |
|
||||||
|
|
||||||
@@ -454,8 +471,9 @@ OSD PARENT UP SIZE USED% TAGS WEIGHT BLOCK BITMAP
|
|||||||
|
|
||||||
```
|
```
|
||||||
[-s|--pg_size <number>] [--pg_minsize <number>] [-n|--pg_count <count>]
|
[-s|--pg_size <number>] [--pg_minsize <number>] [-n|--pg_count <count>]
|
||||||
[--failure_domain <level>] [--root_node <node>] [--osd_tags <tags>]
|
[--failure_domain <level>] [--root_node <node>] [--osd_tags <tags>] [--used_for_app <type>:<name>]
|
||||||
[--max_osd_combinations <number>] [--primary_affinity_tags <tags>] [--scrub_interval <time>]
|
[--max_osd_combinations <number>] [--primary_affinity_tags <tags>] [--scrub_interval <time>]
|
||||||
|
[--level_placement <rules>] [--raw_placement <rules>] [--creator_group <group>]
|
||||||
```
|
```
|
||||||
|
|
||||||
Неизменяемые параметры (их изменение ПРИВЕДЁТ к потере данных):
|
Неизменяемые параметры (их изменение ПРИВЕДЁТ к потере данных):
|
||||||
@@ -483,7 +501,7 @@ OSD PARENT UP SIZE USED% TAGS WEIGHT BLOCK BITMAP
|
|||||||
|
|
||||||
## ls-pools
|
## ls-pools
|
||||||
|
|
||||||
`vitastor-cli ls-pools|pool-ls|ls-pool|pools [-l] [--detail] [--sort FIELD] [-r] [-n N] [--stats] [<glob> ...]`
|
`vitastor-cli ls-pools|pool-ls|ls-pool|pools [-l] [--detail] [--sort FIELD] [-r] [-n N] [<glob> ...]`
|
||||||
|
|
||||||
Показать список пулов. Если передан(ы) шаблон(ы) `<glob>`, то только с именами,
|
Показать список пулов. Если передан(ы) шаблон(ы) `<glob>`, то только с именами,
|
||||||
соответствующими одному из шаблонов (стандартные ФС-шаблоны с * и ?).
|
соответствующими одному из шаблонов (стандартные ФС-шаблоны с * и ?).
|
||||||
@@ -495,3 +513,79 @@ OSD PARENT UP SIZE USED% TAGS WEIGHT BLOCK BITMAP
|
|||||||
| `--sort FIELD` | Сортировать по заданному полю (поля см. в выводе с --json) |
|
| `--sort FIELD` | Сортировать по заданному полю (поля см. в выводе с --json) |
|
||||||
| `-r` или `--reverse` | Сортировать в обратном порядке |
|
| `-r` или `--reverse` | Сортировать в обратном порядке |
|
||||||
| `-n` или `--count N` | Выводить только первые N записей |
|
| `-n` или `--count N` | Выводить только первые N записей |
|
||||||
|
|
||||||
|
## ls-user
|
||||||
|
|
||||||
|
`vitastor-cli ls-users|user-ls|ls-user|list-users [<name> ...]`
|
||||||
|
|
||||||
|
Показать список пользователей. Если переданы имена, показать только пользователей с указанными
|
||||||
|
именами. Имена пользователей должны совпадать с CN (Common Name) TLS-сертификатов
|
||||||
|
клиентов, используемых для подключения к кластеру.
|
||||||
|
|
||||||
|
Подробнее о модели авторизации в Vitastor см. в разделе
|
||||||
|
[Пользователи и права доступа](security.ru.md#пользователи-и-права-доступа).
|
||||||
|
|
||||||
|
## modify-user
|
||||||
|
|
||||||
|
`vitastor-cli modify-user --groups <group1,group2,...> <username>`
|
||||||
|
|
||||||
|
Создать или изменить пользователя с указанным именем (совпадающим с CN TLS-сертификата).
|
||||||
|
|
||||||
|
Опции:
|
||||||
|
|
||||||
|
* `--groups GROUPS` - Задать группы пользователя (через запятую). Группы используются
|
||||||
|
вместе с полями `owner-group` / `reader-group` образов и полем `--creator_group` пулов
|
||||||
|
для авторизации операций. Пустая строка очищает список групп.
|
||||||
|
|
||||||
|
## rm-user
|
||||||
|
|
||||||
|
`vitastor-cli rm-user|remove-user|delete-user <username>`
|
||||||
|
|
||||||
|
Удалить пользователя.
|
||||||
|
|
||||||
|
## serve
|
||||||
|
|
||||||
|
`vitastor-cli serve`
|
||||||
|
|
||||||
|
Запустить HTTP-сервер, обрабатывающий команды vitastor-cli через REST API в формате JSON.
|
||||||
|
|
||||||
|
Опции:
|
||||||
|
|
||||||
|
| <!-- --> | <!-- --> |
|
||||||
|
|-----------------------|----------------------------------------------------------------------------------|
|
||||||
|
| `--bind_address ADDR` | IP-адрес(а) сервера через пробел. По умолчанию `127.0.0.1`. |
|
||||||
|
| `--port 8080` | Порт сервера. По умолчанию `8080`. |
|
||||||
|
| `--api_cert FILE` | TLS-сертификат сервера. |
|
||||||
|
| `--api_pkey FILE` | Закрытый ключ для сертификата `--api_cert`. |
|
||||||
|
| `--cert FILE` | Сертификат администратора/клиента, используемый сервером для доступа к кластеру. |
|
||||||
|
| `--pkey FILE` | Закрытый ключ для сертификата `--cert`. |
|
||||||
|
|
||||||
|
Если заданы `api_cert`/`api_pkey`, сервер работает в режиме HTTPS. Если дополнительно
|
||||||
|
задан `client_ca` (в файле конфигурации или в командной строке), подключающиеся клиенты аутентифицируются
|
||||||
|
по своим TLS-сертификатам. Если включена опция [use_perms](../config/security.ru.md#use_perms), HTTPS с
|
||||||
|
аутентификацией клиентов обязателен, и права доступа проверяются для каждого пользователя. Обычным клиентам
|
||||||
|
сервер разрешает только операции с образами, которыми они владеют или к которым имеют доступ через группы;
|
||||||
|
остальные операции разрешены только администраторам.
|
||||||
|
|
||||||
|
Сам процесс vitastor-cli при этом должен использовать сертификат администратора ([cert](../config/security.en.md#cert),
|
||||||
|
подписанный [admin_ca](../config/security.en.md#admin_ca)), чтобы иметь возможность обслуживать
|
||||||
|
все виды запросов.
|
||||||
|
|
||||||
|
`vitastor-cli serve` выставляет полную OpenAPI-спецификацию по адресу `/openapi`.
|
||||||
|
Запустите сервер и посмотрите её для подробной информации о доступных методах API.
|
||||||
|
|
||||||
|
Смотрите также подробную информацию о том, как устроены аутентификация и авторизация:
|
||||||
|
[Безопасность в Vitastor](security.ru.md).
|
||||||
|
|
||||||
|
## cpubench
|
||||||
|
|
||||||
|
`vitastor-cli cpubench [--json]`
|
||||||
|
|
||||||
|
Запустить тесты производительности CPU для криптографических операций: AES-256-GCM,
|
||||||
|
AES-256-XTS и xxhash3. Эта команда не обращается к кластеру — она только замеряет
|
||||||
|
скорость шифрования/хеширования локально.
|
||||||
|
|
||||||
|
Укажите опцию `--json`, чтобы получить вывод в машиночитаемом JSON-формате.
|
||||||
|
|
||||||
|
Примеры вывода на современных и старых процессорах см. в разделе
|
||||||
|
[Производительность шифрования](security.ru.md#производительность-шифрования).
|
||||||
|
|||||||
@@ -8,15 +8,27 @@
|
|||||||
|
|
||||||
Vitastor has two file system implementations. Both can be used via `vitastor-nfs`.
|
Vitastor has two file system implementations. Both can be used via `vitastor-nfs`.
|
||||||
|
|
||||||
Commands:
|
|
||||||
- [mount](#mount)
|
|
||||||
- [start](#start)
|
|
||||||
- [upgrade](#upgrade)
|
|
||||||
- [defrag](#defrag)
|
|
||||||
|
|
||||||
⚠️ Important: follow the instructions from [Linux NFS write size](#linux-nfs-write-size)
|
⚠️ Important: follow the instructions from [Linux NFS write size](#linux-nfs-write-size)
|
||||||
for optimal Vitastor NFS performance if you use EC and HDD and mount your NFS from Linux.
|
for optimal Vitastor NFS performance if you use EC and HDD and mount your NFS from Linux.
|
||||||
|
|
||||||
|
- [Pseudo-FS](#pseudo-fs)
|
||||||
|
- [VitastorFS](#vitastorfs)
|
||||||
|
- [Supported POSIX features](#supported-posix-features)
|
||||||
|
- [Limitations](#limitations)
|
||||||
|
- [Linux NFS](#linux-nfs)
|
||||||
|
- [Sync vs async safety](#sync-vs-async-safety)
|
||||||
|
- [Linux NFS readahead](#linux-nfs-readahead)
|
||||||
|
- [Linux NFS write size](#linux-nfs-write-size)
|
||||||
|
- [Horizontal scaling](#horizontal-scaling)
|
||||||
|
- [RDMA](#rdma)
|
||||||
|
- [Commands](#commands)
|
||||||
|
- [mount](#mount)
|
||||||
|
- [start](#start)
|
||||||
|
- [upgrade](#upgrade)
|
||||||
|
- [defrag](#defrag)
|
||||||
|
- [Mounting from /etc/fstab](#mounting-from-etc-fstab)
|
||||||
|
- [Common options](#common-options)
|
||||||
|
|
||||||
## Pseudo-FS
|
## Pseudo-FS
|
||||||
|
|
||||||
Simplified pseudo-FS proxy is used for file-based image access emulation. It's not
|
Simplified pseudo-FS proxy is used for file-based image access emulation. It's not
|
||||||
@@ -42,7 +54,7 @@ vitastor-nfs start --block --etcd_address 192.168.5.10:2379 --portmap 0 --port 2
|
|||||||
To mount the FS exported by this server, run:
|
To mount the FS exported by this server, run:
|
||||||
|
|
||||||
```
|
```
|
||||||
mount server:/ /mnt/ -o port=2050,mountport=2050,nfsvers=3,soft,nolock,tcp
|
mount server:/ /mnt/ -o port=2050,mountport=2050,nfsvers=3,soft,sync,nolock,tcp
|
||||||
```
|
```
|
||||||
|
|
||||||
## VitastorFS
|
## VitastorFS
|
||||||
@@ -105,7 +117,57 @@ Other notable missing features which should be addressed in the future:
|
|||||||
in the DB. The FS is implemented is such way that this garbage doesn't affect its
|
in the DB. The FS is implemented is such way that this garbage doesn't affect its
|
||||||
function, but having a tool to clean it up still seems a right thing to do.
|
function, but having a tool to clean it up still seems a right thing to do.
|
||||||
|
|
||||||
## Linux NFS write size
|
## Linux NFS
|
||||||
|
|
||||||
|
Linux NFS client has some specific features of behaviour important for Vitastor:
|
||||||
|
|
||||||
|
### Sync vs async safety
|
||||||
|
|
||||||
|
Linux NFS has 2 options which affect the mount mode: **soft/hard** and **sync/async**.
|
||||||
|
|
||||||
|
**⚠️ Warning: soft,async mode is dangerous and may lead to data loss!**
|
||||||
|
|
||||||
|
How these options work:
|
||||||
|
|
||||||
|
- In the **hard** mode all requests to the server are retried infinitely. If the NFS server becomes
|
||||||
|
unavailable, it becomes impossible to unmount the FS until you reboot the client host.
|
||||||
|
- In the **soft** each request is only tried 2 times (default **retrans** option value for TCP connections).
|
||||||
|
In this case, unmounting a dead FS is possible.
|
||||||
|
- In the **sync** mode all modification requests are sent to the server immediately. This is slower,
|
||||||
|
but allows the client to see all errors if they happen.
|
||||||
|
- In the **async** mode, modification requests are accumulated and sent to the server in the background
|
||||||
|
mode in parallel. It is faster, but it can hide write errors from the client. Instead of receiving an
|
||||||
|
error from a write operation, the client will only get an error when he closes the file (unsuccessful
|
||||||
|
close() call). Another problem is that there's no convenient way to set write parallelism for NFS, so
|
||||||
|
the client can overload the server.
|
||||||
|
|
||||||
|
That's exactly why **soft,async** is dangerous. When a large file is copied, the server is easily overwhelmed
|
||||||
|
with write requests and starts to respond slowly. Write requests time out, soft mode retries them only 1 time
|
||||||
|
and discards them, which results in parts of the file being lost.
|
||||||
|
|
||||||
|
So to recap: always use either **soft,sync** mode, or **hard,async**. By default, `vitastor-nfs mount` uses
|
||||||
|
**soft,sync** if mode options are not specified.
|
||||||
|
|
||||||
|
### Linux NFS readahead
|
||||||
|
|
||||||
|
By default, Linux uses rather small readahead value of 128 KB for NFS mounts.
|
||||||
|
|
||||||
|
For simple reading/copuing of files it's way too low because it stops VitastorFS from
|
||||||
|
using parallel reads.
|
||||||
|
|
||||||
|
However, you can increase readahead. You have 3 options:
|
||||||
|
|
||||||
|
1. Use the built-in `vitastor-nfs mount --readahead <SIZE>` option (for example, `--readahead 2M`).
|
||||||
|
2. Edit `[nfsrahead]` section in `/etc/nfs.conf` - add `nfs=2048` in it (global setting for all NFS 3.0 mounts).
|
||||||
|
3. Manually with the following command: \
|
||||||
|
`echo 2048 > /sys/class/bdi/$(findmnt -no MAJ:MIN /mnt/nfs 2>/dev/null | tr -d ' ')/read_ahead_kb`
|
||||||
|
|
||||||
|
Here, `2048` is the desired readahead in KB, and `/mnt/nfs` is your FS mountpoint.
|
||||||
|
|
||||||
|
You can easily see 2x-3x linear read improvement after you increase the parameter, so we
|
||||||
|
really urge you to try increasing it!
|
||||||
|
|
||||||
|
### Linux NFS write size
|
||||||
|
|
||||||
Linux NFS client (nfs/nfsv3/nfsv4 kernel modules) has a hard-coded maximum I/O size,
|
Linux NFS client (nfs/nfsv3/nfsv4 kernel modules) has a hard-coded maximum I/O size,
|
||||||
currently set to 1 MB - see `rsize` and `wsize` in [man 5 nfs](https://linux.die.net/man/5/nfs).
|
currently set to 1 MB - see `rsize` and `wsize` in [man 5 nfs](https://linux.die.net/man/5/nfs).
|
||||||
@@ -161,7 +223,7 @@ modprobe nfsv3
|
|||||||
After these (not much complicated 🙂) manipulations NFS begins to be mounted
|
After these (not much complicated 🙂) manipulations NFS begins to be mounted
|
||||||
with new wsize and rsize by default and it fixes Vitastor-NFS linear write performance.
|
with new wsize and rsize by default and it fixes Vitastor-NFS linear write performance.
|
||||||
|
|
||||||
## Horizontal scaling
|
### Horizontal scaling
|
||||||
|
|
||||||
Linux NFS 3.0 client doesn't support built-in scaling or failover, i.e. you can't
|
Linux NFS 3.0 client doesn't support built-in scaling or failover, i.e. you can't
|
||||||
specify multiple server addresses when mounting the FS.
|
specify multiple server addresses when mounting the FS.
|
||||||
@@ -200,6 +262,7 @@ Use regular `umount <MOUNTPOINT>` to unmount the FS.
|
|||||||
The server will be automatically stopped when the FS is unmounted.
|
The server will be automatically stopped when the FS is unmounted.
|
||||||
|
|
||||||
- `-o|--options <OPT>` - Pass additional NFS mount options (ex.: -o async).
|
- `-o|--options <OPT>` - Pass additional NFS mount options (ex.: -o async).
|
||||||
|
- `--readahead <SIZE>` - Set Linux FS readahead to `<SIZE>`. Default is 128 KB, larger values increase sequential read speed (for example, try `--readahead 2M`).
|
||||||
|
|
||||||
### start
|
### start
|
||||||
|
|
||||||
@@ -249,6 +312,31 @@ Options:
|
|||||||
| `--include-empty` | Include old and empty volumes; make sure to restart NFS servers before using it |
|
| `--include-empty` | Include old and empty volumes; make sure to restart NFS servers before using it |
|
||||||
| `--no-rm` | Move, but do not delete data |
|
| `--no-rm` | Move, but do not delete data |
|
||||||
|
|
||||||
|
## Mounting from /etc/fstab
|
||||||
|
|
||||||
|
Vitastor packages include mount helpers for `/etc/fstab`: **/usr/sbin/mount.vitastorfs** and **/usr/sbin/mount.vitastorblk**.
|
||||||
|
|
||||||
|
Both are simple symbolic links to `vitastor-nfs` and allow to handle `mount` calls correctly.
|
||||||
|
|
||||||
|
Example `/etc/fstab` entries:
|
||||||
|
|
||||||
|
```
|
||||||
|
myfs /S vitastorfs pool=fspool,readahead=2M,--etcd_address=10.0.2.15:2379,soft,sync 0 0
|
||||||
|
none /mnt/vitastor-block vitastorblk pool=ecpool 0 0
|
||||||
|
```
|
||||||
|
|
||||||
|
When you run `mount /S`, the helper is called internally and starts VitastorFS.
|
||||||
|
Helper call syntax is:
|
||||||
|
|
||||||
|
```
|
||||||
|
mount.vitastorfs <FSNAME> <MOUNTPOINT> -o <OPTS>
|
||||||
|
```
|
||||||
|
|
||||||
|
`<OPTS>` is comma-separated and includes all mount options from fstab. You can include
|
||||||
|
both Vitastor and NFS mount options in it. An option is treated as an NFS option if it
|
||||||
|
does not start with `--`, `pool=`, `readahead=` or if it doesn't contain `=` or `_`.
|
||||||
|
Other options are treated as Vitastor options.
|
||||||
|
|
||||||
## Common options
|
## Common options
|
||||||
|
|
||||||
| <!-- --> | <!-- --> |
|
| <!-- --> | <!-- --> |
|
||||||
@@ -262,3 +350,4 @@ Options:
|
|||||||
| `--logfile <FILE>` | log to the specified file |
|
| `--logfile <FILE>` | log to the specified file |
|
||||||
| `--enforce 1` | enforce permissions at the server side (no by default) |
|
| `--enforce 1` | enforce permissions at the server side (no by default) |
|
||||||
| `--foreground 1` | stay in foreground, do not daemonize |
|
| `--foreground 1` | stay in foreground, do not daemonize |
|
||||||
|
| `--trace` | trace all NFS requests |
|
||||||
|
|||||||
@@ -8,15 +8,27 @@
|
|||||||
|
|
||||||
В Vitastor есть две реализации файловой системы. Обе используются через `vitastor-nfs`.
|
В Vitastor есть две реализации файловой системы. Обе используются через `vitastor-nfs`.
|
||||||
|
|
||||||
Команды:
|
|
||||||
- [mount](#mount)
|
|
||||||
- [start](#start)
|
|
||||||
- [upgrade](#upgrade)
|
|
||||||
- [defrag](#defrag)
|
|
||||||
|
|
||||||
⚠️ Важно: для оптимальной производительности Vitastor NFS в Linux при использовании
|
⚠️ Важно: для оптимальной производительности Vitastor NFS в Linux при использовании
|
||||||
HDD и EC (erasure кодов) выполните инструкции из раздела [Размер записи Linux NFS](#размер-записи-linux-nfs).
|
HDD и EC (erasure кодов) выполните инструкции из раздела [Размер записи Linux NFS](#размер-записи-linux-nfs).
|
||||||
|
|
||||||
|
- [Псевдо-ФС](#псевдо-фс)
|
||||||
|
- [VitastorFS](#vitastorfs)
|
||||||
|
- [Поддерживаемые функции POSIX](#поддерживаемые-функции-posix)
|
||||||
|
- [Ограничения](#ограничения)
|
||||||
|
- [NFS в Linux](#nfs-в-linux)
|
||||||
|
- [Безопасность режимов sync и async](#безопасность-режимов-sync-и-async)
|
||||||
|
- [Упреждающее чтение Linux NFS](#упреждающее-чтение-linux-nfs)
|
||||||
|
- [Размер записи Linux NFS](#размер-записи-linux-nfs)
|
||||||
|
- [Горизонтальное масштабирование](#горизонтальное-масштабирование)
|
||||||
|
- [RDMA](#rdma)
|
||||||
|
- [Команды](#команды)
|
||||||
|
- [mount](#mount)
|
||||||
|
- [start](#start)
|
||||||
|
- [upgrade](#upgrade)
|
||||||
|
- [defrag](#defrag)
|
||||||
|
- [Монтирование из /etc/fstab](#монтирование-из-etc-fstab)
|
||||||
|
- [Общие опции](#общие-опции)
|
||||||
|
|
||||||
## Псевдо-ФС
|
## Псевдо-ФС
|
||||||
|
|
||||||
Упрощённая реализация псевдо-ФС используется для эмуляции файлового доступа к блочным
|
Упрощённая реализация псевдо-ФС используется для эмуляции файлового доступа к блочным
|
||||||
@@ -42,7 +54,7 @@ vitastor-nfs start --block --etcd_address 192.168.5.10:2379 --portmap 0 --port 2
|
|||||||
Примонтировать ФС, запущенную с такими опциями, можно следующей командой:
|
Примонтировать ФС, запущенную с такими опциями, можно следующей командой:
|
||||||
|
|
||||||
```
|
```
|
||||||
mount server:/ /mnt/ -o port=2050,mountport=2050,nfsvers=3,soft,nolock,tcp
|
mount server:/ /mnt/ -o port=2050,mountport=2050,nfsvers=3,soft,sync,nolock,tcp
|
||||||
```
|
```
|
||||||
|
|
||||||
## VitastorFS
|
## VitastorFS
|
||||||
@@ -109,7 +121,58 @@ JSON-формате :-). Для инспекции содержимого БД
|
|||||||
записи. ФС устроена так, что на работу они не влияют, но для порядка и их стоит
|
записи. ФС устроена так, что на работу они не влияют, но для порядка и их стоит
|
||||||
уметь подчищать.
|
уметь подчищать.
|
||||||
|
|
||||||
## Размер записи Linux NFS
|
## NFS в Linux
|
||||||
|
|
||||||
|
NFS в Linux имеет некоторые важные для Vitastor особенности поведения:
|
||||||
|
|
||||||
|
### Безопасность режимов sync и async
|
||||||
|
|
||||||
|
В Linux NFS есть 2 опции, влияющие на режим монтирования: **soft/hard** и **sync/async**.
|
||||||
|
|
||||||
|
**⚠️ Внимание: сочетание soft,async опасно и может приводить к потере данных!**
|
||||||
|
|
||||||
|
Логика работы опций:
|
||||||
|
|
||||||
|
- В режиме **hard** все запросы к серверу повторяются бесконечно. Если NFS-сервер становится недоступен,
|
||||||
|
отмонтировать ФС становится невозможно до перезагрузки клиентской машины.
|
||||||
|
- В режиме **soft** каждый запрос повторяется только 2 раза (значение опции **retrans** по умолчанию для
|
||||||
|
TCP соединений). В этом случае отмонтировать умершую ФС можно.
|
||||||
|
- В режиме **sync** все запросы изменения отправляются на сервер сразу. Это медленнее, но зато любую
|
||||||
|
ошибку записи клиент видит сразу.
|
||||||
|
- В режиме **async** запросы изменения накапливаются и отправляются на сервер в фоновом режиме, параллельно.
|
||||||
|
Это быстрее, но ошибки записи могут быть незаметны для клиента и вместо ошибки записи клиент может
|
||||||
|
получить ошибку в момент закрытия файла (вызова close()). Помимо этого, в NFS нет нормального способа
|
||||||
|
регулировать параллелизм записи, поэтому клиент может легко перегружать сервер большим количеством команд.
|
||||||
|
|
||||||
|
Именно поэтому режим **soft,async** опасен. При копировании любого большого файла сервер перегружается и
|
||||||
|
начинает медленно отвечать. Команды записи заканчиваются таймаутом, soft режим делает 1 повторную попытку,
|
||||||
|
после чего забывает команды совсем. Часть блоков файла оказывается просто потерянной.
|
||||||
|
|
||||||
|
Резюме: всегда используйте либо режим **soft,sync**, либо **hard,async**. Если опции `vitastor-nfs mount`
|
||||||
|
не указаны, режим по умолчанию - **soft,sync**.
|
||||||
|
|
||||||
|
### Упреждающее чтение Linux NFS
|
||||||
|
|
||||||
|
По умолчанию Linux использует довольно небольшой размер упреждающего чтения для
|
||||||
|
файловых систем NFS - 128 КБ.
|
||||||
|
|
||||||
|
Для простого чтения/копирования файлов его не достаточно, потому что оно мешает VitastorFS
|
||||||
|
нормально утилизировать параллелизм чтения.
|
||||||
|
|
||||||
|
Однако, вы можете увеличить эту настройку целыми 3 способами:
|
||||||
|
|
||||||
|
1. Встроенной опцией `vitastor-nfs mount --readahead <SIZE>` (например, `--readahead 2M`).
|
||||||
|
2. Добавив в секцию `[nfsrahead]` файла конфигурации `/etc/nfs.conf` строку `nfs=2048` (глобальная
|
||||||
|
настройка для всех NFS 3.0 точек монтирования).
|
||||||
|
3. Вручную следующей командой: \
|
||||||
|
`echo 2048 > /sys/class/bdi/$(findmnt -no MAJ:MIN /mnt/nfs 2>/dev/null | tr -d ' ')/read_ahead_kb`
|
||||||
|
|
||||||
|
Здесь `2048` - размер readahead, а `/mnt/nfs` - путь к вашей ФС.
|
||||||
|
|
||||||
|
После увеличения параметра скорость линейного чтения легко может увеличиться в 2-3 раза, так что
|
||||||
|
советуем обязательно попробовать!
|
||||||
|
|
||||||
|
### Размер записи Linux NFS
|
||||||
|
|
||||||
Клиент Linux NFS (модули ядра nfs/nfsv3/nfsv4) имеет фиксированный в коде максимальный
|
Клиент Linux NFS (модули ядра nfs/nfsv3/nfsv4) имеет фиксированный в коде максимальный
|
||||||
размер запроса ввода-вывода, равный 1 МБ - см. `rsize` и `wsize` в [man 5 nfs](https://linux.die.net/man/5/nfs).
|
размер запроса ввода-вывода, равный 1 МБ - см. `rsize` и `wsize` в [man 5 nfs](https://linux.die.net/man/5/nfs).
|
||||||
@@ -169,7 +232,7 @@ modprobe nfsv3
|
|||||||
монтироваться с новыми wsize и rsize, и производительность линейной записи в Vitastor-NFS
|
монтироваться с новыми wsize и rsize, и производительность линейной записи в Vitastor-NFS
|
||||||
исправляется.
|
исправляется.
|
||||||
|
|
||||||
## Горизонтальное масштабирование
|
### Горизонтальное масштабирование
|
||||||
|
|
||||||
Клиент Linux NFS 3.0 не поддерживает встроенное масштабирование или отказоустойчивость.
|
Клиент Linux NFS 3.0 не поддерживает встроенное масштабирование или отказоустойчивость.
|
||||||
То есть, вы не можете задать несколько адресов серверов при монтировании ФС.
|
То есть, вы не можете задать несколько адресов серверов при монтировании ФС.
|
||||||
@@ -209,6 +272,9 @@ VitastorFS из GPUDirect.
|
|||||||
Сервер автоматически останавливается при отмонтировании ФС.
|
Сервер автоматически останавливается при отмонтировании ФС.
|
||||||
|
|
||||||
- `-o|--options <OPT>` - Передать дополнительные опции монтирования NFS (пример: -o async).
|
- `-o|--options <OPT>` - Передать дополнительные опции монтирования NFS (пример: -o async).
|
||||||
|
- `--readahead <SIZE>` - Устанавливает размер упреждающей записи ФС Linux в `<SIZE>`.
|
||||||
|
Значение по умолчанию 128 KB, бОльшие значения значительно улучшают скорость линейного чтения.
|
||||||
|
Попробуйте, например, `--readahead 2M`.
|
||||||
|
|
||||||
### start
|
### start
|
||||||
|
|
||||||
@@ -261,6 +327,32 @@ VitastorFS из GPUDirect.
|
|||||||
| `--include-empty` | Дефрагментировать старые и пустые тома; обязательно перезапустите NFS-сервера после использования этой опции |
|
| `--include-empty` | Дефрагментировать старые и пустые тома; обязательно перезапустите NFS-сервера после использования этой опции |
|
||||||
| `--no-rm` | Перемещать, но не удалять данные |
|
| `--no-rm` | Перемещать, но не удалять данные |
|
||||||
|
|
||||||
|
## Монтирование из /etc/fstab
|
||||||
|
|
||||||
|
Пакеты Vitastor включают хелперы для монтирования из `/etc/fstab`: **/usr/sbin/mount.vitastorfs** и **/usr/sbin/mount.vitastorblk**.
|
||||||
|
|
||||||
|
Оба являются простыми символическими ссылками на `vitastor-nfs` и дают возможность
|
||||||
|
корректно обрабатывать вызовы монтирования утилитой `mount`.
|
||||||
|
|
||||||
|
Примеры записей `/etc/fstab`:
|
||||||
|
|
||||||
|
```
|
||||||
|
myfs /S vitastorfs pool=fspool,readahead=2M,--etcd_address=10.0.2.15:2379,soft,sync 0 0
|
||||||
|
none /mnt/vitastor-block vitastorblk pool=ecpool 0 0
|
||||||
|
```
|
||||||
|
|
||||||
|
Когда вы запускаете `mount /S`, внутри вызывается хелпер и запускает VitastorFS.
|
||||||
|
Синтаксис вызова самого хелпера:
|
||||||
|
|
||||||
|
```
|
||||||
|
mount.vitastorfs <FSNAME> <MOUNTPOINT> -o <OPTS>
|
||||||
|
```
|
||||||
|
|
||||||
|
`<OPTS>` - это набор опций, разделённых запятой, и в нём передаются все опции
|
||||||
|
монтирования из fstab. В него можно включать и опции Vitastor, и опции NFS. Опция
|
||||||
|
считается опцией NFS, если она не начинается с `--`, `pool=` и `readahead=`, а также
|
||||||
|
если она не содержит `=` или `_`. Все прочие опции считаются опциями Vitastor.
|
||||||
|
|
||||||
## Общие опции
|
## Общие опции
|
||||||
|
|
||||||
| <!-- --> | <!-- --> |
|
| <!-- --> | <!-- --> |
|
||||||
@@ -274,3 +366,4 @@ VitastorFS из GPUDirect.
|
|||||||
| `--logfile <FILE>` | записывать логи в заданный файл |
|
| `--logfile <FILE>` | записывать логи в заданный файл |
|
||||||
| `--enforce 1` | проверять права доступа на стороне сервера (по умолчанию нет) |
|
| `--enforce 1` | проверять права доступа на стороне сервера (по умолчанию нет) |
|
||||||
| `--foreground 1` | не уходить в фон после запуска |
|
| `--foreground 1` | не уходить в фон после запуска |
|
||||||
|
| `--trace` | логгировать все запросы NFS |
|
||||||
|
|||||||
665
docs/usage/security.en.md
Normal file
665
docs/usage/security.en.md
Normal file
@@ -0,0 +1,665 @@
|
|||||||
|
[Documentation](../../README.md#documentation) → Usage → Security in Vitastor
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
[Читать на русском](security.ru.md)
|
||||||
|
|
||||||
|
# Security in Vitastor
|
||||||
|
|
||||||
|
- [Overview](#overview)
|
||||||
|
- [Quick setup](#quick-setup)
|
||||||
|
- Principles of operation
|
||||||
|
- [etcd transport encryption (TLS)](#etcd-transport-encryption-tls)
|
||||||
|
- [OSD transport encryption (AES-GCM)](#osd-transport-encryption-aes-gcm)
|
||||||
|
- [End-to-end image data encryption (AES-XTS)](#end-to-end-image-data-encryption-aes-xts)
|
||||||
|
- [Certificate-based authentication](#certificate-based-authentication)
|
||||||
|
- [Users and access rights](#users-and-access-rights)
|
||||||
|
- [etcd privileges](#etcd-privileges)
|
||||||
|
- Manual setup
|
||||||
|
- [Configuring OSD transport encryption](#configuring-osd-transport-encryption)
|
||||||
|
- etcd/Antietcd setup options
|
||||||
|
- [Mon with embedded Antietcd](#mon-with-embedded-antietcd)
|
||||||
|
- [Mon as an Etcd proxy](#mon-as-an-etcd-proxy)
|
||||||
|
- [Mon with a separate Antietcd Proxy](#mon-with-a-separate-antietcd-proxy)
|
||||||
|
- [Standalone Antietcd without etcd](#standalone-antietcd-without-etcd)
|
||||||
|
- [Vault/OpenBao setup](#vaultopenbao-setup)
|
||||||
|
- [Vault setup example](#vault-setup-example)
|
||||||
|
- Lists of allowed operations
|
||||||
|
- [etcd data access rights](#etcd-data-access-rights)
|
||||||
|
- [OSD data access rights](#osd-data-access-rights)
|
||||||
|
- [API access rights](#api-access-rights)
|
||||||
|
- [Encryption performance](#encryption-performance)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Starting from version 3.1.0, Vitastor provides full data protection:
|
||||||
|
control plane protection (etcd), data plane protection (OSDs), and end-to-end data encryption.
|
||||||
|
|
||||||
|
- Control plane protection:
|
||||||
|
- etcd transport encryption (TLS)
|
||||||
|
- Authentication via client TLS (X.509) certificates
|
||||||
|
- Access control of clients to etcd data
|
||||||
|
- Data plane protection:
|
||||||
|
- Full AES-GCM encryption of OSD transport (similar to TLS, but faster)
|
||||||
|
- Alternatively, AES-GCM encryption of just operation headers with data checksums using a secret "salt"
|
||||||
|
- Authentication via client TLS (X.509) certificates
|
||||||
|
- Access control of clients on the OSD side
|
||||||
|
- End-to-end encryption:
|
||||||
|
- Data is encrypted using AES-XTS on the client side, the Vitastor cluster has no access to plaintext data
|
||||||
|
- AES-XTS keys can be stored in etcd or in an external Vault/OpenBao
|
||||||
|
|
||||||
|
All features are optional and disabled in the simplest configuration. By default, only
|
||||||
|
transport-level data checksums ([proto_checksums](../config/security.en.md#proto_checksums)=payload)
|
||||||
|
are enabled for clients that support them (>= 3.1.0). For older clients, connections
|
||||||
|
without data checksums are allowed by default ([force_proto_checksums](../config/security.en.md#force_proto_checksums) is empty).
|
||||||
|
|
||||||
|
For a quick setup, jump to the [Quick setup](#quick-setup) section.
|
||||||
|
|
||||||
|
Descriptions of all security-related parameters can be found [here](../config/security.en.md).
|
||||||
|
|
||||||
|
## Quick setup
|
||||||
|
|
||||||
|
For a quick setup, use the `/usr/lib/vitastor/mon/make-etcd` script:
|
||||||
|
|
||||||
|
1. Log in to the node where the first monitor and etcd will be located.
|
||||||
|
2. Create `/etc/vitastor/vitastor.conf` with minimal parameters: etcd_address,
|
||||||
|
osd_network and, if you want to enable privileges, use_perms (note `https://`
|
||||||
|
in etcd addresses):
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"etcd_address": ["https://10.0.0.10:2379","https://10.0.0.11:2379","https://10.0.0.12:2379"],
|
||||||
|
"osd_network": "10.0.0.0/24",
|
||||||
|
"use_perms": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
3. Run `/usr/lib/vitastor/mon/make-etcd` without parameters or with the `--antietcd-only`
|
||||||
|
parameter if you want to initialize the cluster with Antietcd only, without etcd.
|
||||||
|
4. The script will generate all necessary certificates and offer to copy them to the other
|
||||||
|
monitor nodes. Root ssh access to these nodes is required to do it, so agree if it's
|
||||||
|
available.
|
||||||
|
5. If you don't have root ssh access from the first node to other nodes, copy configuration
|
||||||
|
manually (vitastor.conf and all certificates/keys, except CA `*_ca.key`) and call
|
||||||
|
`/usr/lib/vitastor/mon/make-etcd` on every node.
|
||||||
|
|
||||||
|
After that, you can proceed with OSD initialization.
|
||||||
|
|
||||||
|
If you want to understand the setup in more detail, read the [Principles of operation](#principles-of-operation)
|
||||||
|
and [Manual setup](#manual-setup) sections below.
|
||||||
|
|
||||||
|
## Principles of operation
|
||||||
|
|
||||||
|
### etcd transport encryption (TLS)
|
||||||
|
|
||||||
|
Possible setups:
|
||||||
|
- Without encryption (http)
|
||||||
|
- With encryption (https)
|
||||||
|
- With encryption and client certificate authentication. Either the same certificate
|
||||||
|
used for authentication on the OSD side (`cert`+`pkey` / `osd_cert`+`osd_pkey`)
|
||||||
|
is used, or a separately specified certificate (`etcd_client_cert`+`etcd_client_key`).
|
||||||
|
|
||||||
|
### OSD transport encryption (AES-GCM)
|
||||||
|
|
||||||
|
Possible setups:
|
||||||
|
- Unencrypted transport without checksums: `proto_checksums=none`.
|
||||||
|
- Unencrypted transport with data checksums: `proto_checksums=payload` (may be omitted,
|
||||||
|
this is the default value). It's allowed to disable checksums on the client side, or
|
||||||
|
use an older client that does not support checksums. If you want to block connections
|
||||||
|
from clients without checksums, use the option `force_proto_checksums=payload`.
|
||||||
|
- Header-only encryption with data checksums: activated when the options
|
||||||
|
`cert`, `pkey`, `osd_ca` are set on the client side and `osd_cert`, `osd_pkey`, `osd_ca`, `client_ca`
|
||||||
|
on the OSD side, with `proto_checksums=payload`. In this mode, disabling checksums on the client
|
||||||
|
side is forbidden by default, i.e. `force_proto_checksums=payload` is used.
|
||||||
|
- Full transport encryption of all traffic: same as the previous option, but with `proto_checksums=gcm`.
|
||||||
|
In this case, clients are by default allowed to downgrade to checksums only, but this
|
||||||
|
can also be forbidden via `force_proto_checksums=gcm`. This is the slowest setup and
|
||||||
|
it's only recommended for insecure (public) networks. In particular, full traffic
|
||||||
|
encryption together with end-to-end AES-XTS image encryption encrypts data twice.
|
||||||
|
|
||||||
|
Encryption uses the AES-256-GCM algorithm and a custom simplified key exchange protocol,
|
||||||
|
fully analogous to TLS 1.3 ECDHE.
|
||||||
|
|
||||||
|
### End-to-end image data encryption (AES-XTS)
|
||||||
|
|
||||||
|
The Vitastor client supports encrypting each image's data with its own key. In this case,
|
||||||
|
data is encrypted by the client before sending it to OSDs and OSDs can't see it in plain.
|
||||||
|
The encryption key can be changed when cloning/creating image snapshots. For example,
|
||||||
|
you can make a base VM image (say, Debian Linux) unencrypted, but have encrypted client VM
|
||||||
|
images inheriting from it.
|
||||||
|
|
||||||
|
Image encryption keys can be stored in etcd or in an external Vault. In the latter case,
|
||||||
|
etcd only stores key IDs and Vitastor cluster can't decrypt the data at all. To use
|
||||||
|
Vault, create an image with the `--enc_key vault:ID` option, specify vault_url and vault_ca
|
||||||
|
options in the configuration, create accounts for all clients in Vault, and grant them access
|
||||||
|
to the required v1 secrets.
|
||||||
|
|
||||||
|
Once again, if AES-XTS is used together with full traffic encryption (`proto_checksums=gcm`),
|
||||||
|
image data is encrypted twice — first with AES-XTS, and then with AES-GCM. Use it only if
|
||||||
|
you are completely paranoid :-).
|
||||||
|
|
||||||
|
### Certificate-based authentication
|
||||||
|
|
||||||
|
When encryption is enabled, Vitastor clients, OSDs, and monitors authenticate via certificates
|
||||||
|
for both etcd (Antietcd) and OSD connections.
|
||||||
|
|
||||||
|
Separate certificates must be used for OSDs and monitors — either self-signed, or signed
|
||||||
|
by separate CAs (`osd_ca` and `mon_ca`). All OSDs can use the same certificate, and all
|
||||||
|
monitors can also use the same certificate, since the privileges of different OSDs or
|
||||||
|
different monitors do not differ (theoretically, one could differentiate OSD certificates
|
||||||
|
by pool, but there has been no need for this so far).
|
||||||
|
|
||||||
|
Also, a monitor certificate may not be needed at all if Antietcd is embedded into the monitor
|
||||||
|
itself. In this case, the monitor already has access to all etcd data directly in memory.
|
||||||
|
|
||||||
|
### Users and access rights
|
||||||
|
|
||||||
|
When transport encryption is disabled, Vitastor operates without access control, i.e.,
|
||||||
|
any cluster client has full access to both the management layer and the data layer. This
|
||||||
|
option is suitable for dedicated trusted storage networks.
|
||||||
|
|
||||||
|
When OSD transport encryption is enabled (at least for headers), you can enable access
|
||||||
|
control by turning on the `use_perms=true` option. When this option is enabled, each user
|
||||||
|
(and even an OSD or a monitor) can perform only operations allowed for him.
|
||||||
|
|
||||||
|
Each user, administrator, OSD or monitor must have their own certificate signed by a specific
|
||||||
|
root certificate:
|
||||||
|
- `client_ca` for regular users. Common Name of the certificate must be equal to the user's
|
||||||
|
name. Regular users can only read and modify explicitly permitted images and can't modify
|
||||||
|
cluster state.
|
||||||
|
- `admin_ca` for administrators. Administrators can read and modify all images, and also
|
||||||
|
adminster the cluster: view overall statistics and status, create and delete OSDs, etc.
|
||||||
|
Common Name is not checked for admin users.
|
||||||
|
- `osd_ca` for OSDs and `mon_ca` for monitors (if Antietcd runs separately). OSDs and monitors
|
||||||
|
also have distinct permission sets.
|
||||||
|
|
||||||
|
User privileges for regular users are stored in etcd data and currently consist of only
|
||||||
|
one property in `/vitastor/config/user/<name>`:
|
||||||
|
- `groups` - List of group names the user is a member of.
|
||||||
|
|
||||||
|
Images have the following properties:
|
||||||
|
- Owner (`owner`) — the user name that is allowed to both read and modify the image
|
||||||
|
- Owner group (`owner_group`) — the owner group name
|
||||||
|
- Reader group (`reader_group`) — the name of the group of users allowed to read the image
|
||||||
|
|
||||||
|
And there is also a property on the pool:
|
||||||
|
- Creator group (`creator_group`) — the name of the group of users allowed to create images in the pool
|
||||||
|
|
||||||
|
For the list of allowed operations on image data on the OSD side, see the
|
||||||
|
[OSD data access rights](#osd-data-access-rights) section.
|
||||||
|
|
||||||
|
### etcd privileges
|
||||||
|
|
||||||
|
etcd privileges are implemented through Antietcd in all modes of operation.
|
||||||
|
|
||||||
|
Built-in etcd privileges are not supported due to numerous inconveniences:
|
||||||
|
- Certificate-based authentication does not work at all in etcd's REST interface,
|
||||||
|
- Privileges are stored separately from k/v data and cannot participate in transactions,
|
||||||
|
- Only the administrator (root) can change privileges,
|
||||||
|
- There is no support for filtering range read responses by privileges.
|
||||||
|
|
||||||
|
If etcd is used, Antietcd acts as a filtering proxy and can be embedded in the Vitastor
|
||||||
|
monitor or run separately. In this case, etcd must allow incoming connections only from
|
||||||
|
Antietcd, and all other components must connect to Antietcd.
|
||||||
|
|
||||||
|
If Antietcd runs as a part of the Vitastor monitor, it is sufficient to enable the option
|
||||||
|
`use_perms=true` and set the required certificates. If Antietcd is run separately, privileges
|
||||||
|
have to be enabled separately using Antietcd options. For more details on the setup, see
|
||||||
|
the [etcd/Antietcd setup options](#etcdantietcd-setup-options) section.
|
||||||
|
|
||||||
|
For the list of allowed operations with etcd data, see the
|
||||||
|
[etcd data access rights](#etcd-data-access-rights) section.
|
||||||
|
|
||||||
|
## Manual setup
|
||||||
|
|
||||||
|
### Configuring OSD transport encryption
|
||||||
|
|
||||||
|
You need 3 certificates: one for OSDs, one for signing client certificates and one for
|
||||||
|
signing administrator certificates. For OSDs, you can use a self-signed certificate (osd_ca.crt)
|
||||||
|
or a separate certificate (osd.crt) signed by a trusted osd_ca.crt certificate. For clients,
|
||||||
|
you must use separate certificates signed by a common CA certificate (client_ca.crt). One more
|
||||||
|
CA certificate (admin_ca.crt) is required for signing administrator certificates.
|
||||||
|
|
||||||
|
Add to the Vitastor configuration on OSD servers:
|
||||||
|
- use_perms: true
|
||||||
|
- osd_ca: osd_ca.crt
|
||||||
|
- client_ca: client_ca.crt
|
||||||
|
- admin_ca: admin_ca.crt
|
||||||
|
- osd_cert: osd_ca.crt
|
||||||
|
- osd_pkey: osd_ca.key
|
||||||
|
|
||||||
|
On the client side:
|
||||||
|
- use_perms: true
|
||||||
|
- cert: client.crt
|
||||||
|
- pkey: client.key
|
||||||
|
|
||||||
|
### etcd/Antietcd setup options
|
||||||
|
|
||||||
|
The following configuration options are available:
|
||||||
|
|
||||||
|
#### Mon with embedded Antietcd
|
||||||
|
|
||||||
|
The simplest option. Apart from OSD certificates, you only need 1 certificate for Antietcd (antietcd.crt).
|
||||||
|
|
||||||
|
Vitastor settings (`/etc/vitastor/vitastor.conf`):
|
||||||
|
- etcd_address: [ "http://mon1:2379", ... ] (addresses of your monitors with port 2379)
|
||||||
|
- use_perms: true
|
||||||
|
- use_antietcd: true
|
||||||
|
- antietcd_cert: antietcd.crt
|
||||||
|
- antietcd_key: antietcd.key
|
||||||
|
- etcd_ca: antietcd.crt
|
||||||
|
- osd_ca: osd_ca.crt
|
||||||
|
- client_ca: client_ca.crt
|
||||||
|
- admin_ca: admin_ca.crt
|
||||||
|
|
||||||
|
#### Mon as an Etcd proxy
|
||||||
|
|
||||||
|
If you want to enable privileges, but stay on etcd, you can use etcd proxy mode.
|
||||||
|
|
||||||
|
You will need 2 separate certificates: one for etcd (etcd.crt) and one for antietcd (antietcd.crt).
|
||||||
|
The etcd client port must be different from the standard 2379 — for example, you can pick 2381.
|
||||||
|
|
||||||
|
Vitastor settings:
|
||||||
|
- etcd_address: [ "http://mon1:2379", ... ] (addresses of your monitors with port 2379)
|
||||||
|
- use_perms: true
|
||||||
|
- use_antietcd: true
|
||||||
|
- etcd_proxy:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"urls": [ "http://mon1:2381", ... ], // addresses of your etcd with port 2381
|
||||||
|
"cert": "antietcd.crt",
|
||||||
|
"key": "antietcd.key",
|
||||||
|
"ca": "etcd.crt"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- antietcd_cert: antietcd.crt
|
||||||
|
- antietcd_key: antietcd.key
|
||||||
|
- etcd_ca: antietcd.crt
|
||||||
|
- osd_ca: osd_ca.crt
|
||||||
|
- client_ca: client_ca.crt
|
||||||
|
- admin_ca: admin_ca.crt
|
||||||
|
|
||||||
|
etcd command-line options:
|
||||||
|
```
|
||||||
|
--advertise-client-urls=https://<ADDRESS>:2381 --listen-client-urls=https://<ADDRESS>:2381 \
|
||||||
|
--client-cert-auth --cert-file=etcd.crt --key-file=etcd.key --trusted-ca-file=antietcd.crt \
|
||||||
|
--peer-client-cert-auth --peer-cert-file=etcd.crt --peer-key-file=etcd.key --peer-trusted-ca-file=etcd.crt
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Mon with a separate Antietcd Proxy
|
||||||
|
|
||||||
|
If in addition to the previous option you want to offload Vitastor monitor's tasks from Antietcd,
|
||||||
|
you can run it separately.
|
||||||
|
|
||||||
|
Similar to the previous option, 2 certificates are needed: one for etcd and one for antietcd,
|
||||||
|
plus a separate certificate for the monitor.
|
||||||
|
|
||||||
|
Vitastor settings:
|
||||||
|
- etcd_address: [ "http://mon1:2379", ... ] (addresses of your monitors with port 2379)
|
||||||
|
- use_perms: true
|
||||||
|
- use_antietcd: false
|
||||||
|
- etcd_ca: antietcd.crt
|
||||||
|
- osd_ca: osd_ca.crt
|
||||||
|
- client_ca: client_ca.crt
|
||||||
|
- admin_ca: admin_ca.crt
|
||||||
|
- mon_etcd_client_cert: mon_ca.crt
|
||||||
|
- mon_etcd_client_key: mon_ca.key
|
||||||
|
|
||||||
|
Antietcd command-line options:
|
||||||
|
```
|
||||||
|
--port 2379 --cert /etc/vitastor/antietcd.crt --key /etc/vitastor/antietcd.key \
|
||||||
|
--client_cert_auth 1 --auth_filter /usr/lib/vitastor/mon/vitastor_auth_filter.js \
|
||||||
|
--ca /etc/vitastor/client_ca.crt --admin_ca /etc/vitastor/admin_ca.crt \
|
||||||
|
--osd_ca /etc/vitastor/osd_ca.crt --mon_ca /etc/vitastor/mon_ca.crt \
|
||||||
|
--etcd_proxy url1,url2,... --etcd_ca /etc/vitastor/etcd.crt \
|
||||||
|
--etcd_cert /etc/vitastor/antietcd.crt --etcd_key /etc/vitastor/antietcd.key
|
||||||
|
```
|
||||||
|
|
||||||
|
etcd command-line options (same as in the previous option):
|
||||||
|
```
|
||||||
|
--advertise-client-urls=https://<ADDRESS>:2381 --listen-client-urls=https://<ADDRESS>:2381 \
|
||||||
|
--client-cert-auth --cert-file=etcd.crt --key-file=etcd.key --trusted-ca-file=antietcd.crt \
|
||||||
|
--peer-client-cert-auth --peer-cert-file=etcd.crt --peer-key-file=etcd.key --peer-trusted-ca-file=etcd.crt
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Standalone Antietcd without etcd
|
||||||
|
|
||||||
|
Same as the previous option, but etcd and its certificate are not needed:
|
||||||
|
|
||||||
|
Vitastor settings (same as in the previous option):
|
||||||
|
- etcd_address: [ "http://mon1:2379", ... ] (addresses of your monitors with port 2379)
|
||||||
|
- use_perms: true
|
||||||
|
- use_antietcd: false
|
||||||
|
- etcd_ca: antietcd.crt
|
||||||
|
- osd_ca: osd_ca.crt
|
||||||
|
- client_ca: client_ca.crt
|
||||||
|
- admin_ca: admin_ca.crt
|
||||||
|
- mon_etcd_client_cert: mon_ca.crt
|
||||||
|
- mon_etcd_client_key: mon_ca.key
|
||||||
|
|
||||||
|
Antietcd command-line options:
|
||||||
|
```
|
||||||
|
--port 2379 --cert /etc/vitastor/antietcd.crt --key /etc/vitastor/antietcd.key \
|
||||||
|
--client_cert_auth 1 --auth_filter /usr/lib/vitastor/mon/vitastor_auth_filter.js \
|
||||||
|
--ca /etc/vitastor/client_ca.crt --admin_ca /etc/vitastor/admin_ca.crt \
|
||||||
|
--osd_ca /etc/vitastor/osd_ca.crt --mon_ca /etc/vitastor/mon_ca.crt \
|
||||||
|
--persist_filter /usr/lib/vitastor/mon/vitastor_persist_filter.js
|
||||||
|
```
|
||||||
|
|
||||||
|
### Vault/OpenBao setup
|
||||||
|
|
||||||
|
To use Vault, each client that needs to get image keys from Vault needs a Vault account.
|
||||||
|
Vitastor only supports client certificate-based authentication, so all client certificates
|
||||||
|
(`cert`+`pkey`) must be registered in Vault, and they must be granted access to the
|
||||||
|
corresponding secrets (v1 secrets API is supported).
|
||||||
|
|
||||||
|
The required format of a Vault secret is a single `key` field as a hexadecimal string.
|
||||||
|
The AES-256-XTS algorithm is used, so the key length is 64 bytes, i.e., the string must
|
||||||
|
consist of 128 hexadecimal digits.
|
||||||
|
|
||||||
|
To connect to Vault, set the following settings in Vitastor.conf:
|
||||||
|
- `vault_url` — Vault address (e.g., `https://vault:8200`)
|
||||||
|
- `vault_ca` — Vault's own certificate
|
||||||
|
|
||||||
|
After that, if you create an image (`vitastor-cli create`) with the option `--enc_key vault:<ID>`,
|
||||||
|
Vitastor clients will first contact Vault to obtain a token at `/v1/auth/cert/login`,
|
||||||
|
and then request the actual secret from Vault at `/v1/secret/<ID>`.
|
||||||
|
|
||||||
|
#### Vault setup example
|
||||||
|
|
||||||
|
Step-by-step instructions for setting up a test Vault using OpenBao as an example:
|
||||||
|
|
||||||
|
1. If TLS is not yet configured, generate a self-signed TLS certificate for Vault:
|
||||||
|
```
|
||||||
|
openssl req -days 3650 -x509 -addext basicConstraints=critical,CA:TRUE,pathlen:1 --addext subjectAltName=DNS:vault \
|
||||||
|
-new -newkey rsa:4096 -nodes -keyout /etc/openbao/vault.key -out /etc/openbao/vault.crt
|
||||||
|
```
|
||||||
|
Configure it in `/etc/openbao/openbao.hcl`:
|
||||||
|
```
|
||||||
|
listener "tcp" {
|
||||||
|
address = "0.0.0.0:8200"
|
||||||
|
tls_cert_file = "/etc/openbao/vault.crt"
|
||||||
|
tls_key_file = "/etc/openbao/vault.key"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
And restart OpenBao (`systemctl restart openbao`).
|
||||||
|
2. Copy Vault's TLS certificate for Vitastor:
|
||||||
|
```
|
||||||
|
cp /etc/openbao/vault.crt /etc/vitastor/vault.crt
|
||||||
|
```
|
||||||
|
Transfer it to all client nodes and specify it in `/etc/vitastor/vitastor.conf`:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
...
|
||||||
|
"vault_url": "http://vault:8200",
|
||||||
|
"vault_ca": "/etc/vitastor/vault.crt"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
3. Check Vault status:
|
||||||
|
```
|
||||||
|
bao status -ca-cert /etc/openbao/vault.crt -address=https://vault:8200
|
||||||
|
```
|
||||||
|
4. Initialize Vault in test mode from 1 node (with 1 key share):
|
||||||
|
```
|
||||||
|
bao operator init -n 1 -t 1 -ca-cert /etc/openbao/vault.crt -address=https://vault:8200
|
||||||
|
```
|
||||||
|
5. Unseal Vault:
|
||||||
|
```
|
||||||
|
bao operator unseal -ca-cert /etc/openbao/vault.crt -address=https://vault:8200
|
||||||
|
```
|
||||||
|
6. Enable certificate-based authentication:
|
||||||
|
```
|
||||||
|
bao auth enable -ca-cert /etc/openbao/vault.crt -address=https://vault:8200 cert
|
||||||
|
```
|
||||||
|
7. Enable v1 secrets:
|
||||||
|
```
|
||||||
|
bao secrets enable -ca-cert /etc/openbao/vault.crt -address=https://vault:8200 -path=secret kv-v1
|
||||||
|
```
|
||||||
|
8. Create a test secret:
|
||||||
|
```
|
||||||
|
bao kv put -ca-cert /etc/openbao/vault.crt -address=https://vault:8200 secret/vitastor/testimg3 key=$(openssl rand -hex 64)
|
||||||
|
```
|
||||||
|
9. Generate a signed certificate for a Vitastor user (on a machine where you have `client_ca.crt` and `client_ca.key`):
|
||||||
|
```
|
||||||
|
openssl req -subj '/CN=testimg3' -nodes -new -keyout testimg3.key -out testimg3.csr
|
||||||
|
openssl x509 -req -days 3650 -CA client_ca.crt -CAkey client_ca.key -CAcreateserial -in testimg3.csr -out testimg3.crt
|
||||||
|
rm testimg3.csr
|
||||||
|
```
|
||||||
|
10. Create a user in Vault and grant it access to the secret:
|
||||||
|
```
|
||||||
|
cat >testimg3.policy <<EOF
|
||||||
|
path "/secret/vitastor/testimg3" {
|
||||||
|
capabilities = ["read"]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
bao policy write -ca-cert /etc/openbao/vault.crt -address=https://vault:8200 testimg3 testimg3.policy
|
||||||
|
|
||||||
|
bao write -ca-cert /etc/openbao/vault.crt -address=https://vault:8200 auth/cert/certs/testimg3 \
|
||||||
|
certificate=@testimg3.crt display_name=testimg3 token_ttl=24h token_policies=testimg3
|
||||||
|
```
|
||||||
|
11. Test access to the secret:
|
||||||
|
```
|
||||||
|
curl --cacert /etc/vitastor/vault.crt --cert testimg3.crt --key testimg3.key \
|
||||||
|
--json '{}' https://vault:8200/v1/auth/cert/login
|
||||||
|
```
|
||||||
|
A token will be printed, substitute it into the following request:
|
||||||
|
```
|
||||||
|
curl --cacert /etc/vitastor/vault.crt --cert testimg3.crt --key testimg3.key \
|
||||||
|
-H 'X-Vault-Token: <RECEIVED TOKEN>' https://vault:8200/v1/secret/vitastor/testimg3
|
||||||
|
```
|
||||||
|
12. Create an image in Vitastor with the given secret (as an administrator or someone who
|
||||||
|
has the right to create images in your pool):
|
||||||
|
```
|
||||||
|
vitastor-cli create -s 100G --enc_key vault:vitastor/testimg3 --owner testimg3 testimg3
|
||||||
|
```
|
||||||
|
13. Test access to the image as user testimg3:
|
||||||
|
```
|
||||||
|
vitastor-cli --cert testimg3.crt --pkey testimg3.key dd if=/dev/urandom oimg=testimg3 bs=1M count=100
|
||||||
|
```
|
||||||
|
|
||||||
|
## Lists of allowed operations
|
||||||
|
|
||||||
|
### etcd data access rights
|
||||||
|
|
||||||
|
Below, all key names are given without the common prefix `/vitastor`.
|
||||||
|
|
||||||
|
Allowed operations with keys in Antietcd for regular users:
|
||||||
|
- Read-only:
|
||||||
|
- Always allowed:
|
||||||
|
- `/config/global`
|
||||||
|
- `/config/node_placement`
|
||||||
|
- `/config/pools`
|
||||||
|
- `/pg/config`
|
||||||
|
- `/osd/state/*`
|
||||||
|
- `/pg/state/*`
|
||||||
|
- `/index/maxid/*`
|
||||||
|
- For images [readable by the user](#users-and-access-rights):
|
||||||
|
- `/config/inode/*`
|
||||||
|
- `/index/image/*`
|
||||||
|
- `/inode/stats/*`
|
||||||
|
- Read and write:
|
||||||
|
- For pools in which the user can create images:
|
||||||
|
- `/index/maxid/*`
|
||||||
|
- For images owned by the user:
|
||||||
|
- `/config/inode/*`
|
||||||
|
- `/index/image/*`
|
||||||
|
|
||||||
|
Allowed operations with keys in Antietcd for administrators:
|
||||||
|
- Read:
|
||||||
|
- `/stats`
|
||||||
|
- `/mon/*`
|
||||||
|
- `/pg/*`
|
||||||
|
- `/pgstats/*`
|
||||||
|
- `/inode/stats/*`
|
||||||
|
- `/pool/stats/*`
|
||||||
|
- Read and write:
|
||||||
|
- `/config/*`
|
||||||
|
- `/osd/*`
|
||||||
|
- `/index/*`
|
||||||
|
- `/pg/config`
|
||||||
|
- `/pg/history/*`
|
||||||
|
- `/stats`
|
||||||
|
- `/history/last_clean_pgs`
|
||||||
|
|
||||||
|
Allowed operations with keys in etcd for OSDs:
|
||||||
|
- Read:
|
||||||
|
- `/pg/config`
|
||||||
|
- `/config/*`
|
||||||
|
- Read and write:
|
||||||
|
- `/osd/*`
|
||||||
|
- `/pg/state/*`
|
||||||
|
- `/pg/history/*`
|
||||||
|
- `/pgstats/*`
|
||||||
|
|
||||||
|
Allowed operations with keys in etcd for monitors:
|
||||||
|
- Read:
|
||||||
|
- `/config/*`
|
||||||
|
- `/osd/*`
|
||||||
|
- `/pgstats/*`
|
||||||
|
- Read and write:
|
||||||
|
- `/pg/config`
|
||||||
|
- `/stats`
|
||||||
|
- `/history/last_clean_pgs`
|
||||||
|
- `/mon/*`
|
||||||
|
- `/pg/history/*`
|
||||||
|
- `/pg/stats/*`
|
||||||
|
- `/pgstats/*`
|
||||||
|
- `/inode/stats/*`
|
||||||
|
- `/pool/stats/*`
|
||||||
|
|
||||||
|
### OSD data access rights
|
||||||
|
|
||||||
|
When the `use_perms` option and encryption are enabled, OSDs authenticate clients via
|
||||||
|
certificates and allow each client only what is allowed by the access control model.
|
||||||
|
|
||||||
|
Client operations:
|
||||||
|
- READ — allowed for images the user has read access to.
|
||||||
|
- WRITE, DELETE, SCRUB — allowed for images the user has write access to.
|
||||||
|
- SYNC — the operation is not tied to an image and is always allowed.
|
||||||
|
- DESCRIBE — the operation is allowed only for administrators (used by the commands
|
||||||
|
`vitastor-cli describe` and `fix`).
|
||||||
|
- PING — the operation is always allowed.
|
||||||
|
- SHOW_CONFIG — the operation is always allowed, however, if the client presents
|
||||||
|
itself as an OSD in it, then it is verified that it uses a certificate signed by `osd_ca`.
|
||||||
|
- SEC_LIST (listing) — allowed for other OSDs and administrators with any parameters,
|
||||||
|
and for regular clients only allowed for requests limited to an image the user has
|
||||||
|
read access to.
|
||||||
|
|
||||||
|
Cluster operations — allowed only for other OSDs:
|
||||||
|
- SEC_READ
|
||||||
|
- SEC_WRITE
|
||||||
|
- SEC_WRITE_STABLE
|
||||||
|
- SEC_SYNC
|
||||||
|
- SEC_STABILIZE
|
||||||
|
- SEC_ROLLBACK
|
||||||
|
- SEC_DELETE
|
||||||
|
- SEC_READ_BMP
|
||||||
|
- SEC_LOCK
|
||||||
|
|
||||||
|
### API access rights
|
||||||
|
|
||||||
|
[vitastor-cli serve](../usage/cli.en.md#serve) also supports client authentication
|
||||||
|
via certificates. Only certificates signed by `client_ca` are accepted. A separate
|
||||||
|
certificate `api_cert` with the key `api_pkey` is used as the server certificate.
|
||||||
|
|
||||||
|
For `vitastor-cli serve` to work correctly, it must use a certificate of an administrator
|
||||||
|
(`cert`+`pkey` signed by `admin_ca`) to access Vitastor and serve all requests correctly.
|
||||||
|
|
||||||
|
Regular users (with `client_ca`-signed certificates), when accessing the API, are only
|
||||||
|
allowed operations on images available to them either for reading (for read-only operations)
|
||||||
|
or for writing (for modification):
|
||||||
|
|
||||||
|
- image/list — for images the user can read.
|
||||||
|
- image/create — for pools in which the user is allowed to create images, or for
|
||||||
|
creating snapshots of images owned by the user.
|
||||||
|
- image/delete, image/flatten, image/modify — for images owned by the user.
|
||||||
|
|
||||||
|
All other operations are allowed only for administrators.
|
||||||
|
|
||||||
|
## Encryption performance
|
||||||
|
|
||||||
|
You may wonder — how fast is all this wonderful encryption?
|
||||||
|
|
||||||
|
The answer is — it depends heavily on the CPU. On modern processors (with AVX512 with VAES
|
||||||
|
support) it is very fast — AES encryption speed can reach 10-20 GB/s and above. This
|
||||||
|
primarily concerns the CPU of client machines, because end-to-end encryption is performed
|
||||||
|
entirely on the client, and even though transport encryption runs both on clients and OSDs,
|
||||||
|
each client has only one thread, while there are many OSDs on the server side.
|
||||||
|
|
||||||
|
On older processors, the speed is noticeably worse — for example, on a Xeon E5 v4 it is
|
||||||
|
only 3 GB/s.
|
||||||
|
|
||||||
|
You can evaluate the performance of your processors using the `vitastor-cli cpubench` command.
|
||||||
|
|
||||||
|
Example output (💪 AMD EPYC 9575F):
|
||||||
|
|
||||||
|
```
|
||||||
|
$ vitastor-cli cpubench
|
||||||
|
Vitastor transport encryption benchmark (AES-256-GCM, AES-256-XTS and xxhash3)
|
||||||
|
|
||||||
|
Warmup...
|
||||||
|
|
||||||
|
No transport encryption, data checksums enabled, e2e unencrypted image
|
||||||
|
xxhash3 1 M block... 209000 iterations in 2001 ms = 104447.78 MB/s
|
||||||
|
xxhash3 4 K block... 37000000 iterations in 2022 ms = 71479.35 MB/s
|
||||||
|
|
||||||
|
Header encryption with payload checksums, e2e unencrypted image
|
||||||
|
AES-256-GCM encrypt header + xxhash3 1 M block... 210000 iterations in 2015 ms = 104218.36 MB/s
|
||||||
|
AES-256-GCM encrypt header + xxhash3 4 K block... 26000000 iterations in 2073 ms = 48993.01 MB/s
|
||||||
|
|
||||||
|
Full transport encryption, e2e unencrypted image
|
||||||
|
AES-256-GCM encrypt header and 1 M block... 54000 iterations in 2000 ms = 27000.00 MB/s
|
||||||
|
AES-256-GCM encrypt header and 4 K block... 11700000 iterations in 2014 ms = 22692.71 MB/s
|
||||||
|
|
||||||
|
No transport encryption, no checksums, e2e encrypted image
|
||||||
|
AES-256-XTS encrypt 1 M block... 50000 iterations in 2039 ms = 24521.82 MB/s
|
||||||
|
AES-256-XTS encrypt 4 K block... 12600000 iterations in 2009 ms = 24499.13 MB/s
|
||||||
|
|
||||||
|
No transport encryption, e2e encrypted image, data checksums enabled
|
||||||
|
AES-256-XTS encrypt + xxhash3 1 M block... 40000 iterations in 2013 ms = 19870.84 MB/s
|
||||||
|
AES-256-XTS encrypt + xxhash3 4 K block... 10200000 iterations in 2011 ms = 19812.90 MB/s
|
||||||
|
|
||||||
|
Header encryption with payload checksums, e2e encrypted image
|
||||||
|
AES-256-GCM encrypt header + AES-256-XTS encrypt + xxhash3 1 M block... 40000 iterations in 2014 ms = 19860.97 MB/s
|
||||||
|
AES-256-GCM encrypt header + AES-256-XTS encrypt + xxhash3 4 K block... 8700000 iterations in 2011 ms = 16899.24 MB/s
|
||||||
|
|
||||||
|
Full transport encryption, e2e encrypted image
|
||||||
|
AES-256-XTS + AES-256-GCM encrypt 1 M block... 26000 iterations in 2062 ms = 12609.12 MB/s
|
||||||
|
AES-256-XTS + AES-256-GCM encrypt 4 K block... 6300000 iterations in 2006 ms = 12267.88 MB/s
|
||||||
|
```
|
||||||
|
|
||||||
|
And here is Xeon E5-2680v4:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ vitastor-cli cpubench
|
||||||
|
Vitastor transport encryption benchmark (AES-256-GCM, AES-256-XTS and xxhash3)
|
||||||
|
|
||||||
|
Warmup...
|
||||||
|
|
||||||
|
No transport encryption, data checksums enabled, e2e unencrypted image
|
||||||
|
xxhash3 1 M block... 62000 iterations in 2021 ms = 30677.88 MB/s
|
||||||
|
xxhash3 4 K block... 12400000 iterations in 2006 ms = 24146.31 MB/s
|
||||||
|
|
||||||
|
Header encryption with payload checksums, e2e unencrypted image
|
||||||
|
AES-256-GCM encrypt header + xxhash3 1 M block... 62000 iterations in 2027 ms = 30587.07 MB/s
|
||||||
|
AES-256-GCM encrypt header + xxhash3 4 K block... 6800000 iterations in 2011 ms = 13208.60 MB/s
|
||||||
|
|
||||||
|
Full transport encryption, e2e unencrypted image
|
||||||
|
AES-256-GCM encrypt header and 1 M block... 7000 iterations in 2317 ms = 3021.15 MB/s
|
||||||
|
AES-256-GCM encrypt header and 4 K block... 1500000 iterations in 2102 ms = 2787.52 MB/s
|
||||||
|
|
||||||
|
No transport encryption, no checksums, e2e encrypted image
|
||||||
|
AES-256-XTS encrypt 1 M block... 7000 iterations in 2317 ms = 3021.15 MB/s
|
||||||
|
AES-256-XTS encrypt 4 K block... 1600000 iterations in 2088 ms = 2993.30 MB/s
|
||||||
|
|
||||||
|
No transport encryption, e2e encrypted image, data checksums enabled
|
||||||
|
AES-256-XTS encrypt + xxhash3 1 M block... 6000 iterations in 2188 ms = 2742.23 MB/s
|
||||||
|
AES-256-XTS encrypt + xxhash3 4 K block... 1400000 iterations in 2053 ms = 2663.78 MB/s
|
||||||
|
|
||||||
|
Header encryption with payload checksums, e2e encrypted image
|
||||||
|
AES-256-GCM encrypt header + AES-256-XTS encrypt + xxhash3 1 M block... 6000 iterations in 2190 ms = 2739.73 MB/s
|
||||||
|
AES-256-GCM encrypt header + AES-256-XTS encrypt + xxhash3 4 K block... 1300000 iterations in 2101 ms = 2417.00 MB/s
|
||||||
|
|
||||||
|
Full transport encryption, e2e encrypted image
|
||||||
|
AES-256-XTS + AES-256-GCM encrypt 1 M block... 4000 iterations in 2666 ms = 1500.38 MB/s
|
||||||
|
AES-256-XTS + AES-256-GCM encrypt 4 K block... 800000 iterations in 2113 ms = 1478.94 MB/s
|
||||||
|
```
|
||||||
670
docs/usage/security.ru.md
Normal file
670
docs/usage/security.ru.md
Normal file
@@ -0,0 +1,670 @@
|
|||||||
|
[Документация](../../README-ru.md#документация) → Использование → Безопасность в Vitastor
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
[Read in English](security.en.md)
|
||||||
|
|
||||||
|
# Безопасность в Vitastor
|
||||||
|
|
||||||
|
- [Обзор](#обзор)
|
||||||
|
- [Быстрая настройка](#быстрая-настройка)
|
||||||
|
- Принципы работы
|
||||||
|
- [Шифрование соединений с etcd (TLS)](#шифрование-соединений-с-etcd-tls)
|
||||||
|
- [Шифрование соединений с OSD (AES-GCM)](#шифрование-соединений-с-osd-aes-gcm)
|
||||||
|
- [Сквозное шифрование данных образов (AES-XTS)](#сквозное-шифрование-данных-образов-aes-xts)
|
||||||
|
- [Аутентификация по сертификатам](#аутентификация-по-сертификатам)
|
||||||
|
- [Пользователи и права доступа](#пользователи-и-права-доступа)
|
||||||
|
- [Привилегии etcd](#привилегии-etcd)
|
||||||
|
- Ручная настройка
|
||||||
|
- [Настройка шифрования соединений OSD](#настройка-шифрования-соединений-osd)
|
||||||
|
- Варианты настройки etcd/Antietcd
|
||||||
|
- [Mon со встроенным Antietcd](#mon-со-встроенным-antietcd)
|
||||||
|
- [Mon в роли Etcd proxy](#mon-в-роли-etcd-proxy)
|
||||||
|
- [Mon с отдельным Antietcd Proxy](#mon-с-отдельным-antietcd-proxy)
|
||||||
|
- [Отдельный Antietcd без etcd](#отдельный-antietcd-без-etcd)
|
||||||
|
- [Настройка Vault/OpenBao](#настройка-vaultopenbao)
|
||||||
|
- [Пример настройки Vault](#пример-настройки-vault)
|
||||||
|
- Списки разрешённых операций
|
||||||
|
- [Права доступа к данным etcd](#права-доступа-к-данным-etcd)
|
||||||
|
- [Права доступа к данным OSD](#права-доступа-к-данным-osd)
|
||||||
|
- [Права доступа к API](#права-доступа-к-api)
|
||||||
|
- [Производительность шифрования](#производительность-шифрования)
|
||||||
|
|
||||||
|
## Обзор
|
||||||
|
|
||||||
|
Начиная с версии 3.1.0, Vitastor предоставляет полную защиту данных: защиту слоя
|
||||||
|
управления (etcd), защиту слоя данных (OSD) и сквозное шифрование данных.
|
||||||
|
|
||||||
|
- Защита слоя управления:
|
||||||
|
- Шифрование соединений с etcd (TLS)
|
||||||
|
- Аутентификация по клиентским TLS (X.509) сертификатам
|
||||||
|
- Контроль доступа клиентов к данным etcd
|
||||||
|
- Защита слоя данных:
|
||||||
|
- Либо полное AES-GCM шифрование соединений с OSD (аналогично TLS, но быстрее)
|
||||||
|
- Либо шифрование AES-GCM только заголовков команд с контрольными суммами данных с секретной "солью"
|
||||||
|
- Аутентификация по клиентским TLS (X.509) сертификатам
|
||||||
|
- Контроль доступа клиентов на стороне OSD
|
||||||
|
- Сквозное шифрование:
|
||||||
|
- Данные шифруются AES-XTS на стороне клиента, кластер Vitastor не имеет доступа к открытым данным
|
||||||
|
- Ключи AES-XTS могут храниться в etcd или во внешнем Vault/OpenBao
|
||||||
|
|
||||||
|
Все функции опциональны и в простейшем варианте настройки выключены. По умолчанию включены
|
||||||
|
только контрольные суммы данных на транспортном уровне ([proto_checksums](../config/security.ru.md#proto_checksums)=payload) для
|
||||||
|
поддерживающих их клиентов (>= 3.1.0). Для более старых клиентов по умолчанию разрешены
|
||||||
|
соединения без контрольных сумм данных ([force_proto_checksums](../config/security.ru.md#force_proto_checksums) пусто).
|
||||||
|
|
||||||
|
Для быстрой настройки перейдите к разделу [Быстрая настройка](#быстрая-настройка).
|
||||||
|
|
||||||
|
Описания всех параметров, связанных с безопасностью, читайте [здесь](../config/security.ru.md).
|
||||||
|
|
||||||
|
## Быстрая настройка
|
||||||
|
|
||||||
|
Для быстрой настройки используйте скрипт `/usr/lib/vitastor/mon/make-etcd`:
|
||||||
|
|
||||||
|
1. Зайдите на узел, на котором будет располагаться первый монитор и etcd.
|
||||||
|
2. Создайте там минимальный `/etc/vitastor/vitastor.conf` с параметрами etcd_address,
|
||||||
|
osd_network и, если хотите включить привилегии - use_perms (обратите внимание на `https://`
|
||||||
|
в адресах etcd):
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"etcd_address": ["https://10.0.0.10:2379","https://10.0.0.11:2379","https://10.0.0.12:2379"],
|
||||||
|
"osd_network": "10.0.0.0/24",
|
||||||
|
"use_perms": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
3. Запустите `/usr/lib/vitastor/mon/make-etcd` без параметров или с параметром `--antietcd-only`,
|
||||||
|
если хотите инициализировать кластер только с Antietcd без etcd.
|
||||||
|
4. Скрипт сгенерирует все необходимые сертификаты и предложит скопировать их на остальные узлы
|
||||||
|
мониторов. Для этого нужен root ssh доступ к этим узлам. Если он есть - соглашайтесь, скрипт
|
||||||
|
автоматически скопирует конфигурацию и инициализирует все etcd и мониторы.
|
||||||
|
5. Если root ssh доступа с первого узла на остальные узлы мониторов нет - скопируйте на них конфигурацию
|
||||||
|
вручную (vitastor.conf и все сертификаты и ключи, кроме ключей CA `*_ca.key`) и повторите на каждом
|
||||||
|
узле вызов `/usr/lib/vitastor/mon/make-etcd`.
|
||||||
|
|
||||||
|
После этого можете переходить к инициализации OSD.
|
||||||
|
|
||||||
|
Если хотите разобраться в настройке подробнее, читайте далее разделы [Принципы работы](#принципы-работы)
|
||||||
|
и [Ручная настройка](#ручная-настройка).
|
||||||
|
|
||||||
|
## Принципы работы
|
||||||
|
|
||||||
|
### Шифрование соединений с etcd (TLS)
|
||||||
|
|
||||||
|
Варианты настройки:
|
||||||
|
- Без шифрования (http)
|
||||||
|
- С шифрованием (https)
|
||||||
|
- С шифрованием и аутентификацией по клиентским сертификатам. Используется либо тот
|
||||||
|
же сертификат, что используется для аутентификации на стороне OSD (`cert`+`pkey` / `osd_cert`+`osd_pkey`),
|
||||||
|
либо отдельно указанный сертификат (`etcd_client_cert`+`etcd_client_key`)
|
||||||
|
|
||||||
|
### Шифрование соединений с OSD (AES-GCM)
|
||||||
|
|
||||||
|
Варианты настройки:
|
||||||
|
- Без шифрования и без контрольных сумм: `proto_checksums=none`.
|
||||||
|
- Без шифрования, с контрольными суммами данных: `proto_checksums=payload` (можно не указывать,
|
||||||
|
т.к. это значение по умолчанию). При этом контрольные суммы можно отключить на стороне
|
||||||
|
клиента и можно использовать более старые версии клиента, не поддерживающие контрольные суммы.
|
||||||
|
Если нужно запретить подключение клиентов без контрольных сумм, можно использовать опцию
|
||||||
|
`force_proto_checksums=payload`.
|
||||||
|
- С шифрованием заголовков и контрольными суммами данных: активируется при установленных опциях
|
||||||
|
`cert`, `pkey`, `osd_ca` на стороне клиента и `osd_cert`, `osd_pkey`, `osd_ca`, `client_ca`
|
||||||
|
на стороне OSD, при `proto_checksums=payload`. При этом по умолчанию запрещается
|
||||||
|
отключение контрольных сумм на уровне клиента, то есть используется `force_proto_checksums=payload`.
|
||||||
|
- С полным шифрованием всего трафика: аналогично прошлому варианту, но с `proto_checksums=gcm`.
|
||||||
|
Клиенту при этом по умолчанию разрешается понизить уровень защиты до контрольных сумм, но
|
||||||
|
это тоже можно запретить через `force_proto_checksums=gcm`. Данный вариант самый медленный и
|
||||||
|
рекомендуется только для небезопасных (публичных) сетей. В том числе потому, что при использовании
|
||||||
|
и полного шифрования трафика, и сквозного шифрования образов AES-XTS, данные шифруются дважды.
|
||||||
|
|
||||||
|
Для шифрования используется алгоритм AES-256-GCM и собственный упрощённый протокол согласования
|
||||||
|
ключей, полностью аналогичный TLS 1.3 ECDHE.
|
||||||
|
|
||||||
|
### Сквозное шифрование данных образов (AES-XTS)
|
||||||
|
|
||||||
|
Клиент Vitastor поддерживает шифрование данных каждого образа своим ключом. В этом случае на OSD
|
||||||
|
уходят уже зашифрованные данные и сами OSD не видят исходные данные клиента. При этом ключ можно
|
||||||
|
менять при клонировании/создании снимков образов. Например, можно сделать базовый образ ВМ
|
||||||
|
(условный Debian Linux) нешифрованным, но наследовать от него шифрованные образы клиентских ВМ.
|
||||||
|
|
||||||
|
Ключи шифрования образов могут храниться либо в etcd, либо во внешнем Vault. Во втором случае
|
||||||
|
в etcd хранятся только ID ключей, а Vitastor вообще не имеет доступа к данным образов. Для
|
||||||
|
использования Vault нужно создать образ с опцией `--enc_key vault:ID`, в конфигурации указать
|
||||||
|
опции vault_url и vault_ca, создать всем клиентам учётные записи в Vault и дать им доступ
|
||||||
|
к требуемым секретам v1.
|
||||||
|
|
||||||
|
Ещё раз повторимся, что если AES-XTS используется с полным шифрованием трафика (`proto_checksums=gcm`),
|
||||||
|
то данные образов шифруются дважды - сначала AES-XTS, а потом AES-GCM. Можете использовать,
|
||||||
|
только если вы совсем параноик :-).
|
||||||
|
|
||||||
|
### Аутентификация по сертификатам
|
||||||
|
|
||||||
|
При включённом шифровании клиенты, OSD и мониторы Vitastor аутентифицируются по сертификатам
|
||||||
|
как при соединениях с etcd (Antietcd), так и с OSD.
|
||||||
|
|
||||||
|
Для OSD и мониторов должны использоваться отдельные сертификаты - либо самоподписанные, либо
|
||||||
|
подписанные отдельными CA (`osd_ca` и `mon_ca`). При этом все OSD могут использовать один и
|
||||||
|
тот же сертификат и все мониторы тоже могут использовать один и тот же сертификат, так как
|
||||||
|
привилегии разных OSD или разных мониторов ничем не отличаются (теоретически можно было бы
|
||||||
|
сделать разграничение сертификатов OSD по пулам, но пока что такой необходимости не было).
|
||||||
|
|
||||||
|
Также сертификат монитора может быть вообще не нужен, если Antietcd встраивается в сам монитор.
|
||||||
|
В этом случае монитор и так имеет доступ ко всем данным etcd прямо в памяти.
|
||||||
|
|
||||||
|
### Пользователи и права доступа
|
||||||
|
|
||||||
|
При отключённом шифровании трафика Vitastor работает без контроля доступа, то есть,
|
||||||
|
любой клиент кластера имеет полный доступ как к слою управлению, так и к слою данных. Такой
|
||||||
|
вариант подходит для выделенных доверенных сетей хранения.
|
||||||
|
|
||||||
|
При включённом шифровании трафика OSD (хотя бы заголовков) есть возможность задействовать
|
||||||
|
контроль доступа, включив опцию `use_perms=true`. При включённой опции каждый пользователь
|
||||||
|
(и даже OSD и монитор) может выполнять только те операции, которые ему разрешены.
|
||||||
|
|
||||||
|
Каждый обычный пользователь, администратор, OSD или монитор должен иметь свой сертификат,
|
||||||
|
подписанный отдельным корневым сертификатом:
|
||||||
|
- Для обычных пользователей - `client_ca`. Common Name сертификата при этом должен быть равен
|
||||||
|
имени пользователя. Обычные пользователи могут только читать и модифицировать явным образом
|
||||||
|
разрешённые им образы и не могут менять общее состояние кластера.
|
||||||
|
- Для администраторов - `admin_ca`. Администраторы могут читать и модифицировать все образы,
|
||||||
|
а также администрировать кластер: смотреть общую статистику и состояние, создавать и удалять
|
||||||
|
OSD и так далее.
|
||||||
|
- Для OSD - `osd_ca`, для мониторов - `mon_ca` (если Antietcd запускаются отдельно от мониторов).
|
||||||
|
OSD и мониторы тоже имеют свои наборы разрешённых операций.
|
||||||
|
|
||||||
|
Настройки привилегий для обычных пользователей хранятся в etcd и на данный момент состоят
|
||||||
|
всего из одного свойства в ключах `/vitastor/config/user/<имя>`:
|
||||||
|
- `groups` - Список имён групп, членом которых пользователь является.
|
||||||
|
|
||||||
|
У образов есть следующие свойства:
|
||||||
|
- Владелец (`owner`) - имя пользователя, которому разрешено и читать, и менять образ
|
||||||
|
- Группа владельцев (`owner_group`) - имя группы владельцев
|
||||||
|
- Группа читателей (`reader_group`) - имя группы пользователей, которым разрешено читать образ
|
||||||
|
|
||||||
|
И также есть свойство у пула:
|
||||||
|
- Группа создателей (`creator_group`) - имя группы пользователей, которым разрешено создавать образы в пуле
|
||||||
|
|
||||||
|
Перечень разрешённых операций с данными образов на стороне OSD смотрите в разделе
|
||||||
|
[Права доступа к данным OSD](#права-доступа-к-данным-osd).
|
||||||
|
|
||||||
|
### Привилегии etcd
|
||||||
|
|
||||||
|
Привилегии etcd реализуются через Antietcd во всех режимах работы.
|
||||||
|
|
||||||
|
Встроенные привилегии etcd не поддерживаются по причине их многочисленных неудобств:
|
||||||
|
- Аутентификация по сертификатам вообще не работает в REST интерфейсе etcd,
|
||||||
|
- Привилегии хранятся отдельно от k/v данных и не могут участвовать в транзакциях,
|
||||||
|
- Менять привилегии может только администратор (root),
|
||||||
|
- Нет поддержки фильтрации диапазонных ответов чтения по привилегиям.
|
||||||
|
|
||||||
|
Если используется etcd, то Antietcd выступает в роли фильтрующего прокси, при этом он
|
||||||
|
может быть встроен в монитор Vitastor или запущен отдельно. В этом случае etcd должен
|
||||||
|
разрешать входящие подключения только от Antietcd, а все остальные компоненты должны
|
||||||
|
соединяться с Antietcd.
|
||||||
|
|
||||||
|
Если Antietcd запускается в составе монитора Vitastor, то достаточно включить опцию
|
||||||
|
`use_perms=true` и задать нужные сертификаты. Если Antietcd запускается отдельно, то
|
||||||
|
привилегии нужно включать отдельно опциями Antietcd. Подробнее о настройке смотрите
|
||||||
|
раздел [Варианты настройки etcd/Antietcd](#варианты-настройки-etcdantietcd).
|
||||||
|
|
||||||
|
Перечень разрешённых операций с данными etcd смотрите в разделе
|
||||||
|
[Права доступа к данным etcd](#права-доступа-к-данным-etcd).
|
||||||
|
|
||||||
|
## Ручная настройка
|
||||||
|
|
||||||
|
### Настройка шифрования соединений OSD
|
||||||
|
|
||||||
|
Вам нужно 3 сертификата: один для OSD и по одному для подписи сертификатов клиентов и администраторов.
|
||||||
|
Для OSD можно использовать самоподписанный сертификат (osd_ca.crt) или отдельный сертификат (osd.crt),
|
||||||
|
подписанный доверенным сертификатом osd_ca.crt. Для клиентов нужно использовать отдельные
|
||||||
|
сертификаты, подписанные общим доверенным (client_ca.crt), для администраторов - отдельным доверенным
|
||||||
|
(admin_ca.crt).
|
||||||
|
|
||||||
|
В конфигурацию Vitastor на серверах OSD нужно добавить:
|
||||||
|
- use_perms: true
|
||||||
|
- osd_ca: osd_ca.crt
|
||||||
|
- client_ca: client_ca.crt
|
||||||
|
- admin_ca: admin_ca.crt
|
||||||
|
- osd_cert: osd_ca.crt
|
||||||
|
- osd_pkey: osd_ca.key
|
||||||
|
|
||||||
|
На стороне клиентов:
|
||||||
|
- use_perms: true
|
||||||
|
- osd_ca: osd_ca.crt
|
||||||
|
- cert: client.crt
|
||||||
|
- pkey: client.key
|
||||||
|
|
||||||
|
### Варианты настройки etcd/Antietcd
|
||||||
|
|
||||||
|
Доступны следующие варианты настройки:
|
||||||
|
|
||||||
|
#### Mon со встроенным Antietcd
|
||||||
|
|
||||||
|
Самый простой вариант. Кроме сертификатов OSD вам нужен всего 1 сертификат для Antietcd (antietcd.crt).
|
||||||
|
|
||||||
|
Настройки Vitastor (`/etc/vitastor/vitastor.conf`):
|
||||||
|
- etcd_address: [ "http://mon1:2379", ... ] (адреса ваших мониторов с портом 2379)
|
||||||
|
- use_perms: true
|
||||||
|
- use_antietcd: true
|
||||||
|
- antietcd_cert: antietcd.crt
|
||||||
|
- antietcd_key: antietcd.key
|
||||||
|
- etcd_ca: antietcd.crt
|
||||||
|
- osd_ca: osd_ca.crt
|
||||||
|
- client_ca: client_ca.crt
|
||||||
|
- admin_ca: admin_ca.crt
|
||||||
|
|
||||||
|
#### Mon в роли Etcd proxy
|
||||||
|
|
||||||
|
Если вы хотите включить привилегии, но остаться на etcd, можно задействовать режим etcd proxy.
|
||||||
|
|
||||||
|
Вам понадобится 2 отдельных сертификата: один для etcd (etcd.crt) и один для antietcd (antietcd.crt).
|
||||||
|
Клиентский порт etcd должен отличаться от стандартного 2379, например, можно выбрать 2381.
|
||||||
|
|
||||||
|
Настройки Vitastor:
|
||||||
|
- etcd_address: [ "http://mon1:2379", ... ] (адреса ваших мониторов с портом 2379)
|
||||||
|
- use_perms: true
|
||||||
|
- use_antietcd: true
|
||||||
|
- etcd_proxy:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"urls": [ "http://mon1:2381", ... ], // адреса ваших etcd с портом 2381
|
||||||
|
"cert": "antietcd.crt",
|
||||||
|
"key": "antietcd.key",
|
||||||
|
"ca": "etcd.crt"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- antietcd_cert: antietcd.crt
|
||||||
|
- antietcd_key: antietcd.key
|
||||||
|
- etcd_ca: antietcd.crt
|
||||||
|
- osd_ca: osd_ca.crt
|
||||||
|
- client_ca: client_ca.crt
|
||||||
|
- admin_ca: admin_ca.crt
|
||||||
|
|
||||||
|
Опции командной строки etcd:
|
||||||
|
```
|
||||||
|
--advertise-client-urls=https://<АДРЕС>:2381 --listen-client-urls=https://<АДРЕС>:2381 \
|
||||||
|
--client-cert-auth --cert-file=etcd.crt --key-file=etcd.key --trusted-ca-file=antietcd.crt \
|
||||||
|
--peer-client-cert-auth --peer-cert-file=etcd.crt --peer-key-file=etcd.key --peer-trusted-ca-file=etcd.crt
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Mon с отдельным Antietcd Proxy
|
||||||
|
|
||||||
|
Если в дополнение к предыдущему варианту вы хотите разгрузить Antietcd от задач монитора Vitastor,
|
||||||
|
можно запустить его отдельно.
|
||||||
|
|
||||||
|
Аналогично предыдущему варианту нужно 2 сертификата: один для etcd и один для antietcd, плюс понадобится
|
||||||
|
отдельный сертификат для монитора.
|
||||||
|
|
||||||
|
Настройки Vitastor:
|
||||||
|
- etcd_address: [ "http://mon1:2379", ... ] (адреса ваших мониторов с портом 2379)
|
||||||
|
- use_perms: true
|
||||||
|
- use_antietcd: false
|
||||||
|
- etcd_ca: antietcd.crt
|
||||||
|
- osd_ca: osd_ca.crt
|
||||||
|
- client_ca: client_ca.crt
|
||||||
|
- admin_ca: admin_ca.crt
|
||||||
|
- mon_etcd_client_cert: mon_ca.crt
|
||||||
|
- mon_etcd_client_key: mon_ca.key
|
||||||
|
|
||||||
|
Опции командной строки Antietcd:
|
||||||
|
```
|
||||||
|
--port 2379 --cert /etc/vitastor/antietcd.crt --key /etc/vitastor/antietcd.key \
|
||||||
|
--client_cert_auth 1 --auth_filter /usr/lib/vitastor/mon/vitastor_auth_filter.js \
|
||||||
|
--ca /etc/vitastor/client_ca.crt --admin_ca /etc/vitastor/admin_ca.crt \
|
||||||
|
--osd_ca /etc/vitastor/osd_ca.crt --mon_ca /etc/vitastor/mon_ca.crt \
|
||||||
|
--etcd_proxy url1,url2,... --etcd_ca /etc/vitastor/etcd.crt \
|
||||||
|
--etcd_cert /etc/vitastor/antietcd.crt --etcd_key /etc/vitastor/antietcd.key
|
||||||
|
```
|
||||||
|
|
||||||
|
Опции командной строки etcd (не отличаются от предыдущего варианта):
|
||||||
|
```
|
||||||
|
--advertise-client-urls=https://<АДРЕС>:2381 --listen-client-urls=https://<АДРЕС>:2381 \
|
||||||
|
--client-cert-auth --cert-file=etcd.crt --key-file=etcd.key --trusted-ca-file=antietcd.crt \
|
||||||
|
--peer-client-cert-auth --peer-cert-file=etcd.crt --peer-key-file=etcd.key --peer-trusted-ca-file=etcd.crt
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Отдельный Antietcd без etcd
|
||||||
|
|
||||||
|
Аналогично предыдущему варианту, но etcd и его сертификат не нужны:
|
||||||
|
|
||||||
|
Настройки Vitastor (не отличаются от предыдущего варианта):
|
||||||
|
- etcd_address: [ "http://mon1:2379", ... ] (адреса ваших мониторов с портом 2379)
|
||||||
|
- use_perms: true
|
||||||
|
- use_antietcd: false
|
||||||
|
- etcd_ca: antietcd.crt
|
||||||
|
- osd_ca: osd_ca.crt
|
||||||
|
- client_ca: client_ca.crt
|
||||||
|
- admin_ca: admin_ca.crt
|
||||||
|
- mon_etcd_client_cert: mon_ca.crt
|
||||||
|
- mon_etcd_client_key: mon_ca.key
|
||||||
|
|
||||||
|
Опции командной строки Antietcd:
|
||||||
|
```
|
||||||
|
--port 2379 --cert /etc/vitastor/antietcd.crt --key /etc/vitastor/antietcd.key \
|
||||||
|
--client_cert_auth 1 --auth_filter /usr/lib/vitastor/mon/vitastor_auth_filter.js \
|
||||||
|
--ca /etc/vitastor/client_ca.crt --admin_ca /etc/vitastor/admin_ca.crt \
|
||||||
|
--osd_ca /etc/vitastor/osd_ca.crt --mon_ca /etc/vitastor/mon_ca.crt \
|
||||||
|
--persist_filter /usr/lib/vitastor/mon/vitastor_persist_filter.js
|
||||||
|
```
|
||||||
|
|
||||||
|
### Настройка Vault/OpenBao
|
||||||
|
|
||||||
|
Для использования Vault каждому клиенту, который будет получать из Vault ключи
|
||||||
|
образов, нужна учётная запись в Vault. Vitastor поддерживает только аутентификацию
|
||||||
|
по клиентским сертификатам, так что все сертификаты клиентов (`cert`+`pkey`) должны
|
||||||
|
быть зарегистрированы в Vault и им должен быть дан доступ к соответствующим секретам
|
||||||
|
(поддерживается API секретов v1).
|
||||||
|
|
||||||
|
Требуемый формат секрета Vault - одно поле `key` в формате шестнадцатеричной строки.
|
||||||
|
Используется алгоритм AES-256-XTS, так что длина ключа - 64 байта, то есть строка
|
||||||
|
должна состоять из 128 шестнадцатеричных цифр.
|
||||||
|
|
||||||
|
Для подключения Vault включите следующие настройки в Vitastor.conf:
|
||||||
|
- `vault_url` - адрес Vault (например, `https://vault:8200`)
|
||||||
|
- `vault_ca` - сертификат самого Vault
|
||||||
|
|
||||||
|
После этого, если создать образ (`vitastor-cli create`) с опцией `--enc_key vault:<ID>`,
|
||||||
|
то для получения ключа клиенты Vitastor сначала обратятся к Vault для получения токена
|
||||||
|
по адресу `/v1/auth/cert/login`, а потом запросят из Vault сам секрет по адресу `/v1/secret/<ID>`.
|
||||||
|
|
||||||
|
#### Пример настройки Vault
|
||||||
|
|
||||||
|
Пошаговая инструкция для настройки тестового Vault на примере OpenBao:
|
||||||
|
|
||||||
|
1. Если ещё не настроен TLS, генерируем самоподписанный TLS сертификат для Vault:
|
||||||
|
```
|
||||||
|
openssl req -days 3650 -x509 -addext basicConstraints=critical,CA:TRUE,pathlen:1 --addext subjectAltName=DNS:vault \
|
||||||
|
-new -newkey rsa:4096 -nodes -keyout /etc/openbao/vault.key -out /etc/openbao/vault.crt
|
||||||
|
```
|
||||||
|
Настраиваем его в `/etc/openbao/openbao.hcl`:
|
||||||
|
```
|
||||||
|
listener "tcp" {
|
||||||
|
address = "0.0.0.0:8200"
|
||||||
|
tls_cert_file = "/etc/openbao/vault.crt"
|
||||||
|
tls_key_file = "/etc/openbao/vault.key"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
И перезапускаем OpenBao (`systemctl restart openbao`).
|
||||||
|
2. Копируем TLS сертификат Vault для Vitastor:
|
||||||
|
```
|
||||||
|
cp /etc/openbao/vault.crt /etc/vitastor/vault.crt
|
||||||
|
```
|
||||||
|
Переносим его на все клиентские ноды и прописываем в `/etc/vitastor/vitastor.conf`:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
...
|
||||||
|
"vault_url": "http://vault:8200",
|
||||||
|
"vault_ca": "/etc/vitastor/vault.crt"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
3. Проверяем статус Vault:
|
||||||
|
```
|
||||||
|
bao status -ca-cert /etc/openbao/vault.crt -address=https://vault:8200
|
||||||
|
```
|
||||||
|
4. Инициализируем Vault в тестовом режиме из 1 ноды (с 1 частью ключа):
|
||||||
|
```
|
||||||
|
bao operator init -n 1 -t 1 -ca-cert /etc/openbao/vault.crt -address=https://vault:8200
|
||||||
|
```
|
||||||
|
5. Разблокируем Vault:
|
||||||
|
```
|
||||||
|
bao operator unseal -ca-cert /etc/openbao/vault.crt -address=https://vault:8200
|
||||||
|
```
|
||||||
|
6. Включаем аутентификацию по сертификатам:
|
||||||
|
```
|
||||||
|
bao auth enable -ca-cert /etc/openbao/vault.crt -address=https://vault:8200 cert
|
||||||
|
```
|
||||||
|
7. Включаем секреты v1:
|
||||||
|
```
|
||||||
|
bao secrets enable -ca-cert /etc/openbao/vault.crt -address=https://vault:8200 -path=secret kv-v1
|
||||||
|
```
|
||||||
|
8. Создаём тестовый секрет:
|
||||||
|
```
|
||||||
|
bao kv put -ca-cert /etc/openbao/vault.crt -address=https://vault:8200 secret/vitastor/testimg3 key=$(openssl rand -hex 64)
|
||||||
|
```
|
||||||
|
9. Генерируем подписанный сертификат для пользователя Vitastor (там, где у вас есть `client_ca.crt` и `client_ca.key`):
|
||||||
|
```
|
||||||
|
openssl req -subj '/CN=testimg3' -nodes -new -keyout testimg3.key -out testimg3.csr
|
||||||
|
openssl x509 -req -days 3650 -CA client_ca.crt -CAkey client_ca.key -CAcreateserial -in testimg3.csr -out testimg3.crt
|
||||||
|
rm testimg3.csr
|
||||||
|
```
|
||||||
|
10. Создаём пользователя в Vault и даём ему доступ к секрету:
|
||||||
|
```
|
||||||
|
cat >testimg3.policy <<EOF
|
||||||
|
path "/secret/vitastor/testimg3" {
|
||||||
|
capabilities = ["read"]
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
bao policy write -ca-cert /etc/openbao/vault.crt -address=https://vault:8200 testimg3 testimg3.policy
|
||||||
|
|
||||||
|
bao write -ca-cert /etc/openbao/vault.crt -address=https://vault:8200 auth/cert/certs/testimg3 \
|
||||||
|
certificate=@testimg3.crt display_name=testimg3 token_ttl=24h token_policies=testimg3
|
||||||
|
```
|
||||||
|
11. Тестируем доступ к секрету:
|
||||||
|
```
|
||||||
|
curl --cacert /etc/vitastor/vault.crt --cert testimg3.crt --key testimg3.key \
|
||||||
|
--json '{}' https://vault:8200/v1/auth/cert/login
|
||||||
|
```
|
||||||
|
Будет выведен токен, подставляем его в следующий запрос:
|
||||||
|
```
|
||||||
|
curl --cacert /etc/vitastor/vault.crt --cert testimg3.crt --key testimg3.key \
|
||||||
|
-H 'X-Vault-Token: <ПОЛУЧЕННЫЙ ТОКЕН>' https://vault:8200/v1/secret/vitastor/testimg3
|
||||||
|
```
|
||||||
|
12. Создаём образ в Vitastor с заданным секретом (от имени администратора или того, кто имеет
|
||||||
|
право создавать образы в вашем пуле):
|
||||||
|
```
|
||||||
|
vitastor-cli create -s 100G --enc_key vault:vitastor/testimg3 --owner testimg3 testimg3
|
||||||
|
```
|
||||||
|
13. Тестируем доступ к образу от имени пользователя testimg3:
|
||||||
|
```
|
||||||
|
vitastor-cli --cert testimg3.crt --pkey testimg3.key dd if=/dev/urandom oimg=testimg3 bs=1M count=100
|
||||||
|
```
|
||||||
|
|
||||||
|
## Списки разрешённых операций
|
||||||
|
|
||||||
|
### Права доступа к данным etcd
|
||||||
|
|
||||||
|
Ниже все названия ключей приведены без общего префикса `/vitastor`.
|
||||||
|
|
||||||
|
Разрешённые операции с ключами в Antietcd для обычных пользователей:
|
||||||
|
- Только чтение:
|
||||||
|
- Разрешено всегда:
|
||||||
|
- `/config/global`
|
||||||
|
- `/config/node_placement`
|
||||||
|
- `/config/pools`
|
||||||
|
- `/pg/config`
|
||||||
|
- `/osd/state/*`
|
||||||
|
- `/pg/state/*`
|
||||||
|
- `/index/maxid/*`
|
||||||
|
- Для образов, которые [может читать пользователь](#пользователи-и-права-доступа):
|
||||||
|
- `/config/inode/*`
|
||||||
|
- `/index/image/*`
|
||||||
|
- `/inode/stats/*`
|
||||||
|
- Чтение и запись:
|
||||||
|
- Для пулов, в которых может создавать образы пользователь:
|
||||||
|
- `/index/maxid/*`
|
||||||
|
- Для образов, которыми владеет пользователь:
|
||||||
|
- `/config/inode/*`
|
||||||
|
- `/index/image/*`
|
||||||
|
|
||||||
|
Разрешённые операции с ключами в Antietcd для администраторов:
|
||||||
|
- Чтение:
|
||||||
|
- `/stats`
|
||||||
|
- `/mon/*`
|
||||||
|
- `/pg/*`
|
||||||
|
- `/pgstats/*`
|
||||||
|
- `/inode/stats/*`
|
||||||
|
- `/pool/stats/*`
|
||||||
|
- Чтение и запись:
|
||||||
|
- `/config/*`
|
||||||
|
- `/osd/*`
|
||||||
|
- `/index/*`
|
||||||
|
- `/pg/config`
|
||||||
|
- `/pg/history/*`
|
||||||
|
- `/stats`
|
||||||
|
- `/history/last_clean_pgs`
|
||||||
|
|
||||||
|
Разрешённые операции с ключами в etcd для OSD:
|
||||||
|
- Чтение:
|
||||||
|
- `/pg/config`
|
||||||
|
- `/config/*`
|
||||||
|
- Чтение и запись:
|
||||||
|
- `/osd/*`
|
||||||
|
- `/pg/state/*`
|
||||||
|
- `/pg/history/*`
|
||||||
|
- `/pgstats/*`
|
||||||
|
|
||||||
|
Разрешённые операции с ключами в etcd для мониторов:
|
||||||
|
- Чтение:
|
||||||
|
- `/config/*`
|
||||||
|
- `/osd/*`
|
||||||
|
- `/pgstats/*`
|
||||||
|
- Чтение и запись:
|
||||||
|
- `/pg/config`
|
||||||
|
- `/stats`
|
||||||
|
- `/history/last_clean_pgs`
|
||||||
|
- `/mon/*`
|
||||||
|
- `/pg/history/*`
|
||||||
|
- `/pg/stats/*`
|
||||||
|
- `/pgstats/*`
|
||||||
|
- `/inode/stats/*`
|
||||||
|
- `/pool/stats/*`
|
||||||
|
|
||||||
|
### Права доступа к данным OSD
|
||||||
|
|
||||||
|
При включённой опции `use_perms` и шифровании OSD аутентифицирует клиентов по сертификатам
|
||||||
|
и разрешает каждому клиенту только то, что ему разрешено согласно модели прав доступа.
|
||||||
|
|
||||||
|
Клиентские операции:
|
||||||
|
- READ - разрешено для образов, доступных пользователю на чтение.
|
||||||
|
- WRITE, DELETE, SCRUB - разрешены для образов, доступных пользователю на запись.
|
||||||
|
- SYNC - операция не связана с образом и разрешена всегда.
|
||||||
|
- DESCRIBE - операция разрешена только для администраторов (используются командами
|
||||||
|
`vitastor-cli describe` и `fix`).
|
||||||
|
- PING - операция разрешена всегда.
|
||||||
|
- SHOW_CONFIG - операция разрешена всегда, однако если в ней клиент представляется
|
||||||
|
как OSD, то проверяется, что он использует сертификат, подписанный `osd_ca`.
|
||||||
|
- SEC_LIST (листинг) - разрешена другим OSD и администраторам с любыми параметрами,
|
||||||
|
а обычным клиентам разрешена только для запросов, ограниченных образом, доступным
|
||||||
|
пользователю на чтение.
|
||||||
|
|
||||||
|
Кластерные операции - разрешаются только другим OSD:
|
||||||
|
- SEC_READ
|
||||||
|
- SEC_WRITE
|
||||||
|
- SEC_WRITE_STABLE
|
||||||
|
- SEC_SYNC
|
||||||
|
- SEC_STABILIZE
|
||||||
|
- SEC_ROLLBACK
|
||||||
|
- SEC_DELETE
|
||||||
|
- SEC_READ_BMP
|
||||||
|
- SEC_LOCK
|
||||||
|
|
||||||
|
### Права доступа к API
|
||||||
|
|
||||||
|
[vitastor-cli serve](../usage/cli.ru.md#serve) также поддерживает клиентскую
|
||||||
|
аутентификацию по сертификатам. Принимаются только сертификаты, подписанные
|
||||||
|
`client_ca`. В качестве серверного сертификата используется отдельный сертификат
|
||||||
|
`api_cert` с ключом `api_pkey`.
|
||||||
|
|
||||||
|
При этом для корректной работы `vitastor-cli serve` он сам должен использовать
|
||||||
|
для доступа в Vitastor сертификат администратора (`cert`+`pkey`, подписанный `admin_ca`),
|
||||||
|
чтобы корректно обрабатывать любые запросы.
|
||||||
|
|
||||||
|
Обычным пользователям (с сертификатами, подписанными `client_ca`) при доступе к API
|
||||||
|
разрешаются только API-операции с образами, доступными им либо на чтение (для чтения),
|
||||||
|
либо на запись (для модификации):
|
||||||
|
|
||||||
|
- image/list - для образов, которые пользователь может читать.
|
||||||
|
- image/create - для пулов, в которых пользователю разрешено создавать образы, либо
|
||||||
|
для создания снимков образов, которыми пользователь владеет.
|
||||||
|
- image/delete, image/flatten, image/modify - для образов, которыми пользователь владеет.
|
||||||
|
|
||||||
|
Все остальные операции разрешаются только администраторам.
|
||||||
|
|
||||||
|
## Производительность шифрования
|
||||||
|
|
||||||
|
У вас может возникнуть вопрос - а как быстро всё это прекрасное шифрование работает?
|
||||||
|
|
||||||
|
Ответ - сильно зависит от процессора. На современных процессорах (при наличии AVX512 с VAES)
|
||||||
|
очень быстро - скорость шифрования AES может составлять 10-20 Гбайт/с и выше. В первую очередь
|
||||||
|
подразумевается CPU клиентских машин, потому что сквозное шифрование выполняется целиком на
|
||||||
|
клиенте, а транспортное хоть также и затрагивает OSD, но у клиента поток один, а OSD на стороне
|
||||||
|
сервера много и добавить там ресурсов легче.
|
||||||
|
|
||||||
|
На более старых процессорах скорость заметно хуже, например, на Xeon E5 v4 она составляет
|
||||||
|
буквально 3 Гбайт/с.
|
||||||
|
|
||||||
|
Вы можете оценить производительность своих процессоров с помощью команды `vitastor-cli cpubench`.
|
||||||
|
|
||||||
|
Пример вывода (💪 AMD EPYC 9575F):
|
||||||
|
|
||||||
|
```
|
||||||
|
$ vitastor-cli cpubench
|
||||||
|
Vitastor transport encryption benchmark (AES-256-GCM, AES-256-XTS and xxhash3)
|
||||||
|
|
||||||
|
Warmup...
|
||||||
|
|
||||||
|
No transport encryption, data checksums enabled, e2e unencrypted image
|
||||||
|
xxhash3 1 M block... 209000 iterations in 2001 ms = 104447.78 MB/s
|
||||||
|
xxhash3 4 K block... 37000000 iterations in 2022 ms = 71479.35 MB/s
|
||||||
|
|
||||||
|
Header encryption with payload checksums, e2e unencrypted image
|
||||||
|
AES-256-GCM encrypt header + xxhash3 1 M block... 210000 iterations in 2015 ms = 104218.36 MB/s
|
||||||
|
AES-256-GCM encrypt header + xxhash3 4 K block... 26000000 iterations in 2073 ms = 48993.01 MB/s
|
||||||
|
|
||||||
|
Full transport encryption, e2e unencrypted image
|
||||||
|
AES-256-GCM encrypt header and 1 M block... 54000 iterations in 2000 ms = 27000.00 MB/s
|
||||||
|
AES-256-GCM encrypt header and 4 K block... 11700000 iterations in 2014 ms = 22692.71 MB/s
|
||||||
|
|
||||||
|
No transport encryption, no checksums, e2e encrypted image
|
||||||
|
AES-256-XTS encrypt 1 M block... 50000 iterations in 2039 ms = 24521.82 MB/s
|
||||||
|
AES-256-XTS encrypt 4 K block... 12600000 iterations in 2009 ms = 24499.13 MB/s
|
||||||
|
|
||||||
|
No transport encryption, e2e encrypted image, data checksums enabled
|
||||||
|
AES-256-XTS encrypt + xxhash3 1 M block... 40000 iterations in 2013 ms = 19870.84 MB/s
|
||||||
|
AES-256-XTS encrypt + xxhash3 4 K block... 10200000 iterations in 2011 ms = 19812.90 MB/s
|
||||||
|
|
||||||
|
Header encryption with payload checksums, e2e encrypted image
|
||||||
|
AES-256-GCM encrypt header + AES-256-XTS encrypt + xxhash3 1 M block... 40000 iterations in 2014 ms = 19860.97 MB/s
|
||||||
|
AES-256-GCM encrypt header + AES-256-XTS encrypt + xxhash3 4 K block... 8700000 iterations in 2011 ms = 16899.24 MB/s
|
||||||
|
|
||||||
|
Full transport encryption, e2e encrypted image
|
||||||
|
AES-256-XTS + AES-256-GCM encrypt 1 M block... 26000 iterations in 2062 ms = 12609.12 MB/s
|
||||||
|
AES-256-XTS + AES-256-GCM encrypt 4 K block... 6300000 iterations in 2006 ms = 12267.88 MB/s
|
||||||
|
```
|
||||||
|
|
||||||
|
А вот Xeon E5-2680v4:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ vitastor-cli cpubench
|
||||||
|
Vitastor transport encryption benchmark (AES-256-GCM, AES-256-XTS and xxhash3)
|
||||||
|
|
||||||
|
Warmup...
|
||||||
|
|
||||||
|
No transport encryption, data checksums enabled, e2e unencrypted image
|
||||||
|
xxhash3 1 M block... 62000 iterations in 2021 ms = 30677.88 MB/s
|
||||||
|
xxhash3 4 K block... 12400000 iterations in 2006 ms = 24146.31 MB/s
|
||||||
|
|
||||||
|
Header encryption with payload checksums, e2e unencrypted image
|
||||||
|
AES-256-GCM encrypt header + xxhash3 1 M block... 62000 iterations in 2027 ms = 30587.07 MB/s
|
||||||
|
AES-256-GCM encrypt header + xxhash3 4 K block... 6800000 iterations in 2011 ms = 13208.60 MB/s
|
||||||
|
|
||||||
|
Full transport encryption, e2e unencrypted image
|
||||||
|
AES-256-GCM encrypt header and 1 M block... 7000 iterations in 2317 ms = 3021.15 MB/s
|
||||||
|
AES-256-GCM encrypt header and 4 K block... 1500000 iterations in 2102 ms = 2787.52 MB/s
|
||||||
|
|
||||||
|
No transport encryption, no checksums, e2e encrypted image
|
||||||
|
AES-256-XTS encrypt 1 M block... 7000 iterations in 2317 ms = 3021.15 MB/s
|
||||||
|
AES-256-XTS encrypt 4 K block... 1600000 iterations in 2088 ms = 2993.30 MB/s
|
||||||
|
|
||||||
|
No transport encryption, e2e encrypted image, data checksums enabled
|
||||||
|
AES-256-XTS encrypt + xxhash3 1 M block... 6000 iterations in 2188 ms = 2742.23 MB/s
|
||||||
|
AES-256-XTS encrypt + xxhash3 4 K block... 1400000 iterations in 2053 ms = 2663.78 MB/s
|
||||||
|
|
||||||
|
Header encryption with payload checksums, e2e encrypted image
|
||||||
|
AES-256-GCM encrypt header + AES-256-XTS encrypt + xxhash3 1 M block... 6000 iterations in 2190 ms = 2739.73 MB/s
|
||||||
|
AES-256-GCM encrypt header + AES-256-XTS encrypt + xxhash3 4 K block... 1300000 iterations in 2101 ms = 2417.00 MB/s
|
||||||
|
|
||||||
|
Full transport encryption, e2e encrypted image
|
||||||
|
AES-256-XTS + AES-256-GCM encrypt 1 M block... 4000 iterations in 2666 ms = 1500.38 MB/s
|
||||||
|
AES-256-XTS + AES-256-GCM encrypt 4 K block... 800000 iterations in 2113 ms = 1478.94 MB/s
|
||||||
|
```
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
const AntiEtcd = require('antietcd');
|
const AntiEtcd = require('antietcd');
|
||||||
|
|
||||||
|
const vitastor_auth_filter = require('./vitastor_auth_filter.js');
|
||||||
const vitastor_persist_filter = require('./vitastor_persist_filter.js');
|
const vitastor_persist_filter = require('./vitastor_persist_filter.js');
|
||||||
const { b64, local_ips } = require('./utils.js');
|
const { b64, local_ips } = require('./utils.js');
|
||||||
|
|
||||||
@@ -27,7 +28,17 @@ class AntiEtcdAdapter
|
|||||||
is_local['::'] = true;
|
is_local['::'] = true;
|
||||||
is_local[''] = true;
|
is_local[''] = true;
|
||||||
// split :, 3 -> <schema>:<//ip>:<port>
|
// split :, 3 -> <schema>:<//ip>:<port>
|
||||||
const selected = cluster.map(s => s.split(':', 3)).filter(ip => is_local[ip[1].substr(2)] && (!cfg_port || ip[2] == cfg_port));
|
const selected = [];
|
||||||
|
for (let i = 0; i < cluster.length; i++)
|
||||||
|
{
|
||||||
|
const m = /^(https?:\/\/)?(?:\[(.*)\]|([^\[\:]+))(?::(\d+))?$/.exec(cluster[i]);
|
||||||
|
if (!m)
|
||||||
|
continue;
|
||||||
|
const ip = m[3] || m[2];
|
||||||
|
const port = m[4] || 2379;
|
||||||
|
if (is_local[ip] && (!cfg_port || port == cfg_port))
|
||||||
|
selected.push({ idx: i, ip, port });
|
||||||
|
}
|
||||||
if (selected.length > 1)
|
if (selected.length > 1)
|
||||||
{
|
{
|
||||||
console.error('More than 1 etcd_address matches local IPs, please specify port');
|
console.error('More than 1 etcd_address matches local IPs, please specify port');
|
||||||
@@ -36,29 +47,45 @@ class AntiEtcdAdapter
|
|||||||
else if (selected.length == 1)
|
else if (selected.length == 1)
|
||||||
{
|
{
|
||||||
const antietcd_config = {
|
const antietcd_config = {
|
||||||
ip: selected[0][1].substr(2),
|
ip: selected[0].ip,
|
||||||
port: selected[0][2],
|
port: selected[0].port,
|
||||||
cert: config.antietcd_cert,
|
cert: config.antietcd_cert,
|
||||||
key: config.antietcd_key,
|
key: config.antietcd_key,
|
||||||
ca: config.etcd_ca,
|
ca: config.client_ca,
|
||||||
data: config.antietcd_data_file || ((config.antietcd_data_dir || '/var/lib/vitastor') + '/mon_'+selected[0][2]+'.json.gz'),
|
data: config.antietcd_data_file || ((config.antietcd_data_dir || '/var/lib/vitastor') + '/mon_'+selected[0].port+'.json.gz'),
|
||||||
persist_filter: vitastor_persist_filter({ vitastor_prefix: config.etcd_prefix || '/vitastor' }),
|
persist_filter: vitastor_persist_filter({ vitastor_prefix: config.etcd_prefix || '/vitastor' }),
|
||||||
node_id: selected[0][1].substr(2)+':'+selected[0][2], // node_id = ip:port
|
node_id: cluster[selected[0].idx].replace(/^(https?:\/\/)/, ''), // same as in <cluster> below
|
||||||
cluster: (cluster.length == 1 ? null : cluster.reduce((a, c) => { a[c.replace(/^(https?:\/\/)/, '')] = c; return a; }, {})),
|
cluster: (cluster.length == 1 ? null : cluster.reduce((a, c) => { a[c.replace(/^(https?:\/\/)/, '')] = c; return a; }, {})),
|
||||||
cluster_key: (config.etcd_prefix || '/vitastor'),
|
cluster_key: (config.etcd_prefix || '/vitastor'),
|
||||||
stale_read: 1,
|
stale_read: 1,
|
||||||
log_level: 1,
|
log_level: 1,
|
||||||
|
logs: { cluster: true },
|
||||||
};
|
};
|
||||||
if (config.use_auth)
|
if (config.etcd_proxy)
|
||||||
{
|
{
|
||||||
antietcd_config.client_cert_auth = true;
|
// Monitor may use the builtin etcd_proxy mode
|
||||||
antietcd_config.auth_filter = require('./vitastor_auth_filter.js');
|
if (!config.etcd_proxy.urls)
|
||||||
antietcd_config.peer_ca = config.antietcd_server_ca;
|
|
||||||
if (!config.antietcd_server_ca || config.antietcd_server_ca == config.etcd_ca)
|
|
||||||
{
|
{
|
||||||
console.error('Secure setup requires separate antietcd_server_ca (for signing antietcd server certificates) and etcd_ca (for signing client certificates)');
|
console.error('etcd_proxy.urls are empty');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
antietcd_config.etcd_proxy = config.etcd_proxy.urls;
|
||||||
|
antietcd_config.etcd_cert = config.etcd_proxy.cert;
|
||||||
|
antietcd_config.etcd_key = config.etcd_proxy.key;
|
||||||
|
antietcd_config.etcd_ca = config.etcd_proxy.ca;
|
||||||
|
delete antietcd_config.data;
|
||||||
|
delete antietcd_config.persist_filter;
|
||||||
|
delete antietcd_config.cluster;
|
||||||
|
delete antietcd_config.cluster_key;
|
||||||
|
}
|
||||||
|
if (config.use_perms)
|
||||||
|
{
|
||||||
|
antietcd_config.client_cert_auth = true;
|
||||||
|
antietcd_config.auth_filter = vitastor_auth_filter;
|
||||||
|
antietcd_config.ca = config.client_ca;
|
||||||
|
antietcd_config.osd_ca = config.osd_ca;
|
||||||
|
antietcd_config.mon_ca = config.mon_ca;
|
||||||
|
antietcd_config.admin_ca = config.admin_ca;
|
||||||
}
|
}
|
||||||
for (const key in config)
|
for (const key in config)
|
||||||
{
|
{
|
||||||
@@ -184,7 +211,7 @@ class AntiEtcdAdapter
|
|||||||
await new Promise(ok => setTimeout(ok, timeout-(Date.now()-prev)));
|
await new Promise(ok => setTimeout(ok, timeout-(Date.now()-prev)));
|
||||||
}
|
}
|
||||||
prev = Date.now();
|
prev = Date.now();
|
||||||
const res = await this.antietcd.api(path.replace(/^\/+/, '').replace(/\/+$/, '').replace(/\/+/g, '_'), body, { username: 'root' });
|
const res = await this.antietcd.api(path.replace(/^\/+/, '').replace(/\/+$/, '').replace(/\/+/g, '_'), body, { user_type: 'mon' });
|
||||||
if (res.error)
|
if (res.error)
|
||||||
{
|
{
|
||||||
console.error('Failed to query antietcd '+path+' (retry '+retry+'/'+retries+'): '+res.error);
|
console.error('Failed to query antietcd '+path+' (retry '+retry+'/'+retries+'): '+res.error);
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ const etcd_tree = {
|
|||||||
placement_levels: { datacenter: 1, rack: 2, host: 3, osd: 4, ... },
|
placement_levels: { datacenter: 1, rack: 2, host: 3, osd: 4, ... },
|
||||||
use_old_pg_combinator: false,
|
use_old_pg_combinator: false,
|
||||||
osd_backfillfull_ratio: 0.99,
|
osd_backfillfull_ratio: 0.99,
|
||||||
|
mon_metrics_path: '/prometheus',
|
||||||
|
mon_healthcheck_path: '/healthcheck',
|
||||||
// client and osd
|
// client and osd
|
||||||
tcp_header_buffer_size: 65536,
|
tcp_header_buffer_size: 65536,
|
||||||
use_sync_send_recv: false,
|
use_sync_send_recv: false,
|
||||||
|
|||||||
@@ -112,9 +112,10 @@ function make_cyclic(pgs, parity_space)
|
|||||||
{
|
{
|
||||||
if (parity_space > 1)
|
if (parity_space > 1)
|
||||||
{
|
{
|
||||||
for (const pg in pgs)
|
for (const id in pgs)
|
||||||
{
|
{
|
||||||
for (let i = 1; i < pg.size; i++)
|
const pg = pgs[id];
|
||||||
|
for (let i = 1; i < pg.length; i++)
|
||||||
{
|
{
|
||||||
const cyclic = [ ...pg.slice(i), ...pg.slice(0, i) ];
|
const cyclic = [ ...pg.slice(i), ...pg.slice(0, i) ];
|
||||||
pgs['pg_'+cyclic.join('_')] = cyclic;
|
pgs['pg_'+cyclic.join('_')] = cyclic;
|
||||||
|
|||||||
19
mon/mon.js
19
mon/mon.js
@@ -87,7 +87,7 @@ class Mon
|
|||||||
this.http = await create_http_server(this.config, (req, res) =>
|
this.http = await create_http_server(this.config, (req, res) =>
|
||||||
{
|
{
|
||||||
const u = new URL(req.url, 'http://'+(req.headers.host || 'localhost'));
|
const u = new URL(req.url, 'http://'+(req.headers.host || 'localhost'));
|
||||||
if (u.pathname.replace(/\/+$/, '') == (this.config.prometheus_path||'/metrics'))
|
if (u.pathname.replace(/\/+$/, '') == (this.config.mon_metrics_path||'/metrics'))
|
||||||
{
|
{
|
||||||
if (!this.watcher_active)
|
if (!this.watcher_active)
|
||||||
{
|
{
|
||||||
@@ -100,6 +100,19 @@ class Mon
|
|||||||
res.write(export_prometheus_metrics(this.state));
|
res.write(export_prometheus_metrics(this.state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (u.pathname.replace(/\/+$/, '') == (this.config.mon_healthcheck_path||'/healthcheck'))
|
||||||
|
{
|
||||||
|
if (!this.watcher_active)
|
||||||
|
{
|
||||||
|
res.writeHead(503);
|
||||||
|
res.write('Monitor is in standby mode. Please retrieve metrics from master monitor instance\n');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res.setHeader('Content-Type', 'text/plain; version=0.0.4; charset=utf-8');
|
||||||
|
res.write('Monitor is active');
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
res.writeHead(404);
|
res.writeHead(404);
|
||||||
@@ -627,7 +640,7 @@ class Mon
|
|||||||
if (this.state.pg.history[pool_id] &&
|
if (this.state.pg.history[pool_id] &&
|
||||||
this.state.pg.history[pool_id][pg])
|
this.state.pg.history[pool_id][pg])
|
||||||
{
|
{
|
||||||
pg_history[pg-1] = this.state.pg.history[pool_id][pg];
|
pg_history[pg-1] = JSON.parse(JSON.stringify(this.state.pg.history[pool_id][pg]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const real_prev_pgs = [];
|
const real_prev_pgs = [];
|
||||||
@@ -719,7 +732,7 @@ class Mon
|
|||||||
this.next_recheck_timer = null;
|
this.next_recheck_timer = null;
|
||||||
this.next_recheck_at = 0;
|
this.next_recheck_at = 0;
|
||||||
this.schedule_recheck();
|
this.schedule_recheck();
|
||||||
}, now-this.next_recheck_at);
|
}, (this.next_recheck_at-now)*1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vitastor-mon",
|
"name": "vitastor-mon",
|
||||||
"version": "3.0.9",
|
"version": "3.2.1",
|
||||||
"description": "Vitastor SDS monitor service",
|
"description": "Vitastor SDS monitor service",
|
||||||
"main": "mon-main.js",
|
"main": "mon-main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
"author": "Vitaliy Filippov",
|
"author": "Vitaliy Filippov",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"antietcd": "^1.2.4",
|
"antietcd": "^1.3.3",
|
||||||
"sprintf-js": "^1.1.2",
|
"sprintf-js": "^1.1.2",
|
||||||
"ws": "^7.2.5"
|
"ws": "^7.2.5"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ function scale_pg_history(prev_pg_history, prev_pgs, new_pgs)
|
|||||||
finish_pg_history(merged_history[1]);
|
finish_pg_history(merged_history[1]);
|
||||||
for (let i = 0; i < new_pg_count; i++)
|
for (let i = 0; i < new_pg_count; i++)
|
||||||
{
|
{
|
||||||
new_pg_history[i] = { ...merged_history[1] };
|
new_pg_history[i] = JSON.parse(JSON.stringify(merged_history[1]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Mark history keys for removed PGs as removed
|
// Mark history keys for removed PGs as removed
|
||||||
@@ -102,7 +102,7 @@ function scale_pg_count(prev_pgs, new_pg_count)
|
|||||||
{
|
{
|
||||||
for (let i = prev_pgs.length; i < new_pg_count; i++)
|
for (let i = prev_pgs.length; i < new_pg_count; i++)
|
||||||
{
|
{
|
||||||
prev_pgs[i] = prev_pgs[i % prev_pgs.length];
|
prev_pgs[i] = [ ...prev_pgs[i % prev_pgs.length] ];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (prev_pgs.length > new_pg_count)
|
else if (prev_pgs.length > new_pg_count)
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
const { parse_level_indexes, parse_pg_dsl } = require('./lp_optimizer/dsl_pgs.js');
|
const { parse_level_indexes, parse_pg_dsl } = require('./lp_optimizer/dsl_pgs.js');
|
||||||
|
|
||||||
|
const MIN_DATA_BLOCK_SIZE = 4096;
|
||||||
|
const MAX_DATA_BLOCK_SIZE = 128*1024*1024;
|
||||||
|
|
||||||
function validate_pool_cfg(pool_id, pool_cfg, placement_levels, warn)
|
function validate_pool_cfg(pool_id, pool_cfg, placement_levels, warn)
|
||||||
{
|
{
|
||||||
pool_cfg.pg_size = Math.floor(pool_cfg.pg_size);
|
pool_cfg.pg_size = Math.floor(pool_cfg.pg_size);
|
||||||
@@ -23,7 +26,8 @@ function validate_pool_cfg(pool_id, pool_cfg, placement_levels, warn)
|
|||||||
console.log('Pool '+pool_id+' has invalid coding scheme (one of "xor", "replicated", "ec" and "jerasure" required)');
|
console.log('Pool '+pool_id+' has invalid coding scheme (one of "xor", "replicated", "ec" and "jerasure" required)');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!pool_cfg.pg_size || pool_cfg.pg_size < 1 || pool_cfg.pg_size > 256 ||
|
// OSDs don't support pg_size > 64 for now
|
||||||
|
if (!pool_cfg.pg_size || pool_cfg.pg_size < 1 || pool_cfg.pg_size > 64 ||
|
||||||
pool_cfg.scheme !== 'replicated' && pool_cfg.pg_size < 3)
|
pool_cfg.scheme !== 'replicated' && pool_cfg.pg_size < 3)
|
||||||
{
|
{
|
||||||
if (warn)
|
if (warn)
|
||||||
@@ -50,6 +54,18 @@ function validate_pool_cfg(pool_id, pool_cfg, placement_levels, warn)
|
|||||||
console.log('Pool '+pool_id+' has invalid parity_chunks (must be between 1 and pg_size-2)');
|
console.log('Pool '+pool_id+' has invalid parity_chunks (must be between 1 and pg_size-2)');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (pool_cfg.block_size &&
|
||||||
|
(!Number(pool_cfg.block_size) || (pool_cfg.block_size & (pool_cfg.block_size-1)) ||
|
||||||
|
pool_cfg.block_size < MIN_DATA_BLOCK_SIZE || pool_cfg.block_size > MAX_DATA_BLOCK_SIZE ||
|
||||||
|
pool_cfg.scheme !== 'replicated' && pool_cfg.block_size*pool_cfg.pg_size > 0xFFFFFFFF))
|
||||||
|
{
|
||||||
|
if (warn)
|
||||||
|
{
|
||||||
|
console.log('Pool '+pool_id+' has invalid block_size (must be a power of two between '+
|
||||||
|
MIN_DATA_BLOCK_SIZE+' and '+MAX_DATA_BLOCK_SIZE+'; must not exceed 2^32-1 when multiplied by pg_size)');
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (!pool_cfg.pg_count || pool_cfg.pg_count < 1)
|
if (!pool_cfg.pg_count || pool_cfg.pg_count < 1)
|
||||||
{
|
{
|
||||||
if (warn)
|
if (warn)
|
||||||
|
|||||||
@@ -1,50 +1,104 @@
|
|||||||
#!/usr/bin/node
|
#!/usr/bin/node
|
||||||
// Simple systemd unit generator for etcd
|
// Simple Vitastor etcd / antietcd / TLS configurator
|
||||||
// Copyright (c) Vitaliy Filippov, 2019+
|
// Copyright (c) Vitaliy Filippov, 2019+
|
||||||
// License: MIT
|
// License: MIT
|
||||||
|
|
||||||
// USAGE:
|
|
||||||
// 1) Put the same etcd_address into /etc/vitastor/vitastor.conf on all monitor nodes
|
|
||||||
// 2) Run ./make-etcd.js. It will create the etcd service on one of specified IPs
|
|
||||||
|
|
||||||
const child_process = require('child_process');
|
const child_process = require('child_process');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
|
const path = require('path');
|
||||||
|
const readline = require('readline');
|
||||||
|
|
||||||
run().catch(e => { console.error(e); process.exit(1); });
|
const help_text = `Initialize a Vitastor cluster (etcd, vitastor.conf and TLS certificates)
|
||||||
|
(c) Vitaliy Filippov, 2026+ (MIT)
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
1) Create a minimal vitastor.conf with etcd_address, osd_network and (optionally) use_perms.
|
||||||
|
Non-encrypted: {"etcd_address":["http://10.0.0.10:2379","http://10.0.0.11:2379","http://10.0.0.12:2379"],"osd_network":"10.0.0.0/24"}
|
||||||
|
Encrypted: {"etcd_address":["https://10.0.0.10:2379","https://10.0.0.11:2379","https://10.0.0.12:2379"],"use_perms":true,"osd_network":"10.0.0.0/24"}
|
||||||
|
(Note https:// etcd URLs!)
|
||||||
|
2) Run: ${process.argv[1]} [./vitastor.conf] [--antietcd-only]
|
||||||
|
You can run it on etcd/monitor nodes or on an external node.
|
||||||
|
It configures etcd, generates TLS certificates (on the first or external node), copies
|
||||||
|
them to other etcd/monitor nodes using root ssh, and updates vitastor.conf with TLS options.
|
||||||
|
You can also copy configuration and certificates manually.
|
||||||
|
3) If you have OSD-only nodes, run:
|
||||||
|
${process.argv[1]} --copy-to-osd-node NODE_NAME ./vitastor.conf
|
||||||
|
It copies vitastor.conf and required TLS certificates to that node.
|
||||||
|
|
||||||
|
OPTIONS:
|
||||||
|
--antietcd-only
|
||||||
|
disable etcd (proxy or direct mode), use only antietcd
|
||||||
|
--gen-certs
|
||||||
|
force certificate generation even if it's not the first node
|
||||||
|
--no-certs
|
||||||
|
disable certificate generation
|
||||||
|
--copy yes|no|ask
|
||||||
|
copy vitastor.conf and TLS certificates for monitor&etcd to monitor nodes using scp
|
||||||
|
(default is ask)
|
||||||
|
--copy-to-osd-node NODE[,NODE2,...]
|
||||||
|
copy vitastor.conf and TLS certificates for OSDs to NODES using scp
|
||||||
|
`;
|
||||||
|
|
||||||
async function run()
|
async function run()
|
||||||
{
|
{
|
||||||
const config_path = process.argv[2] || '/etc/vitastor/vitastor.conf';
|
let config_path = '/etc/vitastor/vitastor.conf';
|
||||||
if (config_path == '-h' || config_path == '--help')
|
let config_dir = '/etc/vitastor';
|
||||||
|
let gen_certs = 'auto';
|
||||||
|
let antietcd_only = false;
|
||||||
|
let copy = 'ask';
|
||||||
|
let copy_to_osd = null;
|
||||||
|
for (let i = 2; i < process.argv.length; i++)
|
||||||
{
|
{
|
||||||
console.log(
|
const arg = process.argv[i];
|
||||||
'Initialize systemd etcd service for Vitastor\n'+
|
if (arg == '-h' || arg == '--help')
|
||||||
'(c) Vitaliy Filippov, 2019+ (MIT)\n'+
|
{
|
||||||
'\n'+
|
console.log(help_text);
|
||||||
'USAGE:\n'+
|
process.exit(0);
|
||||||
'1) Put the same etcd_address into /etc/vitastor/vitastor.conf on all monitor nodes\n'+
|
}
|
||||||
'2) Run '+process.argv[1]+' [config_path]\n'
|
else if (arg == '--gen-certs')
|
||||||
);
|
{
|
||||||
process.exit(0);
|
gen_certs = true;
|
||||||
|
}
|
||||||
|
else if (arg == '--no-certs')
|
||||||
|
{
|
||||||
|
gen_certs = false;
|
||||||
|
}
|
||||||
|
else if (arg == '--antietcd-only')
|
||||||
|
{
|
||||||
|
antietcd_only = true;
|
||||||
|
}
|
||||||
|
else if (arg == '--copy-to-osd-node' && i < process.argv.length-1)
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
copy_to_osd = process.argv[i].split(/,/);
|
||||||
|
}
|
||||||
|
else if (arg == '--copy' && i < process.argv.length-1)
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
copy = process.argv[i];
|
||||||
|
if (copy !== 'ask' && copy !== 'yes' && copy !== 'no')
|
||||||
|
{
|
||||||
|
console.error('--copy should be "ask", "yes" or "no"');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (arg[0] == '-')
|
||||||
|
{
|
||||||
|
console.error('Unknown option: '+arg[0]);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
config_path = arg;
|
||||||
|
config_dir = path.dirname(arg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!fs.existsSync(config_path))
|
if (!fs.existsSync(config_path))
|
||||||
{
|
{
|
||||||
console.log(config_path+' is missing');
|
console.log(config_path+' is missing');
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
const in_docker = fs.existsSync("/etc/vitastor/etcd.conf") &&
|
|
||||||
fs.existsSync("/etc/vitastor/docker.conf");
|
|
||||||
if (!in_docker && fs.existsSync("/etc/systemd/system/vitastor-etcd.service"))
|
|
||||||
{
|
|
||||||
console.log("/etc/systemd/system/vitastor-etcd.service already exists");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
if (!in_docker && fs.existsSync("/etc/systemd/system/etcd.service"))
|
|
||||||
{
|
|
||||||
console.log("/etc/systemd/system/etcd.service already exists");
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
const config = JSON.parse(fs.readFileSync(config_path, { encoding: 'utf-8' }));
|
const config = JSON.parse(fs.readFileSync(config_path, { encoding: 'utf-8' }));
|
||||||
if (!config.etcd_address)
|
if (!config.etcd_address)
|
||||||
{
|
{
|
||||||
@@ -52,30 +106,307 @@ async function run()
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
const etcds = (config.etcd_address instanceof Array ? config.etcd_address : (''+config.etcd_address).split(/,/))
|
const etcds = (config.etcd_address instanceof Array ? config.etcd_address : (''+config.etcd_address).split(/,/))
|
||||||
.map(s => (''+s).replace(/^https?:\/\/|(:\d+)?(\/.*)?$/g, '').replace(/^\[(.*)\]$/, '$1').toLowerCase());
|
.map(s => /^(https?):\/\/(\[[^\]]+\]|[^\[\]\:\/]+)(?::(\d+))?/.exec(s.toLowerCase()))
|
||||||
|
.filter(s => s)
|
||||||
|
.map(s => ({
|
||||||
|
scheme: s[1],
|
||||||
|
addr: s[2].indexOf(':') >= 0 && s[2][0] != '[' ? '['+s[2]+']' : s[2],
|
||||||
|
ip: s[2][0] == '[' ? s[2].substr(1, s[2].length-2) : s[2],
|
||||||
|
port: s[3],
|
||||||
|
}));
|
||||||
|
const tls = etcds.filter(e => e.scheme === 'https').length > 0;
|
||||||
|
const use_perms = tls && config.use_perms;
|
||||||
const num = select_local_etcd(etcds);
|
const num = select_local_etcd(etcds);
|
||||||
|
if (copy_to_osd)
|
||||||
|
{
|
||||||
|
copy_to_osd_nodes(copy_to_osd, config_dir, use_perms, antietcd_only);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
if (tls)
|
||||||
|
{
|
||||||
|
const etcd_ca = config_dir+'/'+path.basename(config.etcd_ca || '/etc/vitastor/etcd_ca.crt');
|
||||||
|
if (gen_certs === true)
|
||||||
|
{
|
||||||
|
console.log('Certificate generation is requested explicitly, generating');
|
||||||
|
}
|
||||||
|
else if (gen_certs === false)
|
||||||
|
{
|
||||||
|
console.log('Certificate generation is disabled explicitly, skipping');
|
||||||
|
}
|
||||||
|
else if (num < 0)
|
||||||
|
{
|
||||||
|
gen_certs = true;
|
||||||
|
console.log('No matching IPs in etcd_address from '+config_path+', only generating certificates');
|
||||||
|
}
|
||||||
|
else if (config.etcd_ca && fs.existsSync(etcd_ca))
|
||||||
|
{
|
||||||
|
gen_certs = false;
|
||||||
|
console.log(etcd_ca+' already exists, assuming certificates are already generated');
|
||||||
|
}
|
||||||
|
else if (num === 0)
|
||||||
|
{
|
||||||
|
gen_certs = true;
|
||||||
|
console.log('This is monitor node 1, generating certificates');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
console.log('This is monitor node '+(num+1)+', '+etcd_ca+' does not exist, please copy certificates to this node');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
await write_auth_config(config, config_path, etcds, use_perms, antietcd_only);
|
||||||
|
if (gen_certs)
|
||||||
|
{
|
||||||
|
if (copy === 'ask')
|
||||||
|
copy = await ask_copy('Copy certificates and vitastor.conf to other nodes and start them (requires root ssh)? ');
|
||||||
|
copy = (copy === 'y' || copy === 'yes');
|
||||||
|
await make_certs(config_dir, copy, etcds, num, use_perms, antietcd_only);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (num <= 0)
|
||||||
|
{
|
||||||
|
if (copy === 'ask')
|
||||||
|
copy = await ask_copy('Copy vitastor.conf to other nodes and start them (requires root ssh)? ');
|
||||||
|
if (copy === 'y' || copy === 'yes')
|
||||||
|
await copy_nontls(config_dir, etcds, num);
|
||||||
|
}
|
||||||
if (num < 0)
|
if (num < 0)
|
||||||
{
|
{
|
||||||
console.log('No matching IPs in etcd_address from '+config_path);
|
console.log('No matching IPs in etcd_address from '+config_path);
|
||||||
process.exit(0);
|
process.exit(tls && gen_certs ? 0 : 1);
|
||||||
}
|
}
|
||||||
const etcd_url = 'http://' + (etcds[num].indexOf(':') >= 0 ? '['+etcds[num]+']' : etcds[num]);
|
await configure_etcd(etcds, num, tls, use_perms);
|
||||||
const etcd_name = 'etcd'+etcds[num].replace(/[^0-9a-z_]/ig, '_');
|
process.exit(0);
|
||||||
const etcd_cluster = etcds.map(e => `etcd${e.replace(/[^0-9a-z_]/ig, '_')}=http://${e.indexOf(':') >= 0 ? '['+e+']' : e}:2380`).join(',');
|
}
|
||||||
|
|
||||||
|
async function ask_copy(question)
|
||||||
|
{
|
||||||
|
const rl = readline.createInterface({
|
||||||
|
input: process.stdin,
|
||||||
|
output: process.stdout,
|
||||||
|
prompt: '> ',
|
||||||
|
});
|
||||||
|
let copy;
|
||||||
|
while (copy != 'y' && copy != 'n' && copy != 'yes' && copy != 'no')
|
||||||
|
{
|
||||||
|
if (copy)
|
||||||
|
console.log('Please type "yes" or "no"');
|
||||||
|
copy = await new Promise(ok => rl.question(question, ok));
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copy_to_osd_nodes(to, dir, use_perms, antietcd_only)
|
||||||
|
{
|
||||||
|
const osd_to_copy = [ 'vitastor.conf' ];
|
||||||
|
if (!antietcd_only && !use_perms)
|
||||||
|
osd_to_copy.push('etcd_ca.crt');
|
||||||
|
else
|
||||||
|
osd_to_copy.push('antietcd_ca.crt');
|
||||||
|
if (use_perms)
|
||||||
|
osd_to_copy.push('osd.crt', 'osd.key', 'client_ca.crt');
|
||||||
|
console.warn('Copying configuration to OSD nodes '+to.join(', '));
|
||||||
|
for (const node of to)
|
||||||
|
await system("scp "+dir+"/"+osd_to_copy.join(" "+dir+"/")+" root@"+(node.indexOf(':') >= 0 ? "["+node+"]" : node)+":/etc/vitastor/");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function make_certs(dir, copy, etcds, num, use_perms, antietcd_only)
|
||||||
|
{
|
||||||
|
console.log(`-----
|
||||||
|
Generating certificates in ${dir}
|
||||||
|
-----
|
||||||
|
`);
|
||||||
|
const to_copy = [ 'vitastor.conf' ];
|
||||||
|
const osd_to_copy = [ 'vitastor.conf' ];
|
||||||
|
if (!antietcd_only)
|
||||||
|
{
|
||||||
|
await make_ca("/O=Vitastor etcd CA", dir+"/etcd_ca");
|
||||||
|
await make_signed("/CN=Vitastor etcd", dir+"/etcd", dir+"/etcd_ca", etcds.map(e => "IP:"+e.ip).join(','));
|
||||||
|
to_copy.push('etcd_ca.crt', 'etcd.crt', 'etcd.key');
|
||||||
|
if (!use_perms)
|
||||||
|
osd_to_copy.push('etcd_ca.crt');
|
||||||
|
}
|
||||||
|
if (use_perms || antietcd_only)
|
||||||
|
{
|
||||||
|
await make_ca("/O=Vitastor Antietcd CA", dir+"/antietcd_ca");
|
||||||
|
await make_signed("/CN=Vitastor Antietcd", dir+"/antietcd", dir+"/antietcd_ca", etcds.map(e => "IP:"+e.ip).join(','));
|
||||||
|
to_copy.push('antietcd_ca.crt', 'antietcd.crt', 'antietcd.key');
|
||||||
|
osd_to_copy.push('antietcd_ca.crt');
|
||||||
|
}
|
||||||
|
if (use_perms)
|
||||||
|
{
|
||||||
|
await make_ca("/CN=Vitastor OSD", dir+"/osd");
|
||||||
|
await make_ca("/O=Vitastor Client CA", dir+"/client_ca");
|
||||||
|
await make_ca("/O=Vitastor Admin CA", dir+"/admin_ca");
|
||||||
|
await make_signed("/CN=admin", dir+"/admin", dir+"/admin_ca");
|
||||||
|
to_copy.push('osd.crt', 'osd.key', 'client_ca.crt', 'admin_ca.crt', 'admin.crt', 'admin.key');
|
||||||
|
osd_to_copy.push('osd.crt', 'osd.key', 'client_ca.crt', 'admin_ca.crt', 'admin.crt', 'admin.key');
|
||||||
|
}
|
||||||
|
console.log(`-----
|
||||||
|
Certificates generated, commands to copy them:
|
||||||
|
- Monitor+OSD node:
|
||||||
|
cd ${dir} && scp ${to_copy.join(' ')} root@NODE:/etc/vitastor/
|
||||||
|
- Monitor node:
|
||||||
|
cd ${dir} && scp ${to_copy.filter(f => f != 'osd.key').join(' ')} root@NODE:/etc/vitastor/
|
||||||
|
- OSD node:
|
||||||
|
cd ${dir} && scp ${osd_to_copy.join(' ')} root@NODE:/etc/vitastor/
|
||||||
|
Then don't forget to run:
|
||||||
|
chown vitastor:vitastor /etc/vitastor/*.key && /usr/lib/vitastor/mon/make-etcd
|
||||||
|
-----
|
||||||
|
`);
|
||||||
|
if (copy)
|
||||||
|
{
|
||||||
|
for (let i = 0; i < etcds.length; i++)
|
||||||
|
{
|
||||||
|
if (i == num)
|
||||||
|
continue;
|
||||||
|
const node = etcds[i];
|
||||||
|
await system("scp "+dir+"/"+to_copy.join(" "+dir+"/")+" root@"+(node.ip.indexOf(':') >= 0 ? "["+node.ip+"]" : node.ip)+
|
||||||
|
":/etc/vitastor/ && ssh root@"+node.ip+" 'chown vitastor:vitastor /etc/vitastor/*.key && /usr/lib/vitastor/mon/make-etcd'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
console.warn('Certificates generated in '+dir+', please copy them to other nodes');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copy_nontls(dir, etcds, num)
|
||||||
|
{
|
||||||
|
const to_copy = [ 'vitastor.conf' ];
|
||||||
|
for (let i = 0; i < etcds.length; i++)
|
||||||
|
{
|
||||||
|
if (i == num)
|
||||||
|
continue;
|
||||||
|
const node = etcds[i];
|
||||||
|
await system("scp "+dir+"/"+to_copy.join(" "+dir+"/")+" root@"+(node.ip.indexOf(':') >= 0 ? "["+node.ip+"]" : node.ip)+
|
||||||
|
":/etc/vitastor/ && ssh root@"+node.ip+" '/usr/lib/vitastor/mon/make-etcd'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function write_auth_config(config, config_path, etcds, use_perms, antietcd_only)
|
||||||
|
{
|
||||||
|
const auth = {};
|
||||||
|
if (use_perms)
|
||||||
|
{
|
||||||
|
auth["use_antietcd"] = true;
|
||||||
|
if (!antietcd_only)
|
||||||
|
{
|
||||||
|
auth["etcd_proxy"] = {
|
||||||
|
urls: etcds.map(e => (e.ip.indexOf(':') >= 0 ? '['+e.ip+']' : e.ip)+':2381'),
|
||||||
|
cert: "/etc/vitastor/antietcd.crt",
|
||||||
|
key: "/etc/vitastor/antietcd.key",
|
||||||
|
ca: "/etc/vitastor/etcd_ca.crt",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
auth["antietcd_cert"] = "/etc/vitastor/antietcd.crt";
|
||||||
|
auth["antietcd_key"] = "/etc/vitastor/antietcd.key";
|
||||||
|
auth["etcd_ca"] = "/etc/vitastor/antietcd_ca.crt";
|
||||||
|
auth["osd_cert"] = "/etc/vitastor/osd.crt";
|
||||||
|
auth["osd_pkey"] = "/etc/vitastor/osd.key";
|
||||||
|
auth["osd_ca"] = "/etc/vitastor/osd.crt";
|
||||||
|
auth["client_ca"] = "/etc/vitastor/client_ca.crt";
|
||||||
|
auth["admin_ca"] = "/etc/vitastor/admin_ca.crt";
|
||||||
|
auth["cert"] = "/etc/vitastor/admin.crt";
|
||||||
|
auth["pkey"] = "/etc/vitastor/admin.key";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (antietcd_only)
|
||||||
|
{
|
||||||
|
auth["use_antietcd"] = true;
|
||||||
|
auth["antietcd_cert"] = "/etc/vitastor/antietcd.crt";
|
||||||
|
auth["antietcd_key"] = "/etc/vitastor/antietcd.key";
|
||||||
|
auth["etcd_ca"] = "/etc/vitastor/antietcd_ca.crt";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auth["etcd_ca"] = "/etc/vitastor/etcd.crt";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const k in auth)
|
||||||
|
{
|
||||||
|
if ((k in config) && JSON.stringify(auth[k]) != JSON.stringify(config[k]))
|
||||||
|
{
|
||||||
|
// Auth options already overridden with non-default
|
||||||
|
console.log(k+" is already overridden in "+config_path+", skipping config update");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const k in auth)
|
||||||
|
{
|
||||||
|
config[k] = auth[k];
|
||||||
|
}
|
||||||
|
console.log(`-----
|
||||||
|
Updating ${config_path}
|
||||||
|
-----
|
||||||
|
`);
|
||||||
|
fs.writeFileSync(config_path, JSON.stringify(config, 0, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function configure_etcd(etcds, num, tls, use_perms)
|
||||||
|
{
|
||||||
|
const in_docker = fs.existsSync("/etc/vitastor/etcd.conf") &&
|
||||||
|
fs.existsSync("/etc/vitastor/docker.conf");
|
||||||
|
if (!in_docker && fs.existsSync("/etc/systemd/system/vitastor-etcd.service") &&
|
||||||
|
fs.existsSync("/etc/vitastor/etcd.conf"))
|
||||||
|
{
|
||||||
|
console.log("/etc/systemd/system/vitastor-etcd.service and /etc/vitastor/etcd.conf already exists. Remove them to reinitialize the cluster");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
if (!in_docker && fs.existsSync("/etc/systemd/system/etcd.service"))
|
||||||
|
{
|
||||||
|
console.log("/etc/systemd/system/etcd.service already exists");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
const etcd_url = etcds[num].scheme + '://' + etcds[num].addr;
|
||||||
|
const options = {
|
||||||
|
name: 'etcd'+etcds[num].ip.replace(/[^0-9a-z_]/ig, '_'),
|
||||||
|
advertise_client_urls: etcd_url+':'+(use_perms ? 2381 : 2379),
|
||||||
|
listen_client_urls: etcd_url+':'+(use_perms ? 2381 : 2379),
|
||||||
|
initial_advertise_peer_urls: etcd_url+':2380',
|
||||||
|
listen_peer_urls: etcd_url+':2380',
|
||||||
|
initial_cluster_token: 'vitastor-etcd-1',
|
||||||
|
initial_cluster_state: 'new',
|
||||||
|
initial_cluster: etcds.map(e => `etcd${e.ip.replace(/[^0-9a-z_]/ig, '_')}=${e.scheme}://${e.addr}:2380`).join(','),
|
||||||
|
snapshot_count: 10000,
|
||||||
|
max_txn_ops: 100000,
|
||||||
|
max_request_bytes: 104857600,
|
||||||
|
auto_compaction_retention: 10,
|
||||||
|
auto_compaction_mode: 'revision',
|
||||||
|
};
|
||||||
|
if (tls)
|
||||||
|
{
|
||||||
|
options['cert_file'] = '/etc/vitastor/etcd.crt';
|
||||||
|
options['key_file'] = '/etc/vitastor/etcd.key';
|
||||||
|
if (use_perms)
|
||||||
|
{
|
||||||
|
options['client_cert_auth'] = '1';
|
||||||
|
options['trusted_ca_file'] = '/etc/vitastor/antietcd_ca.crt';
|
||||||
|
}
|
||||||
|
options['peer_cert_file'] = '/etc/vitastor/etcd.crt';
|
||||||
|
options['peer_key_file'] = '/etc/vitastor/etcd.key';
|
||||||
|
if (use_perms)
|
||||||
|
{
|
||||||
|
options['peer_client_cert_auth'] = '1';
|
||||||
|
options['peer_trusted_ca_file'] = '/etc/vitastor/etcd_ca.crt';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let etcd_conf = fs.existsSync("/etc/vitastor/etcd.conf")
|
||||||
|
? fs.readFileSync("/etc/vitastor/etcd.conf", { encoding: 'utf-8' })
|
||||||
|
: "";
|
||||||
|
for (const k in options)
|
||||||
|
{
|
||||||
|
etcd_conf = replace_env(etcd_conf, 'ETCD_'+k.toUpperCase().replace(/-/, '_'), options[k]);
|
||||||
|
}
|
||||||
|
fs.writeFileSync("/etc/vitastor/etcd.conf", etcd_conf);
|
||||||
if (in_docker)
|
if (in_docker)
|
||||||
{
|
{
|
||||||
let etcd_conf = fs.readFileSync("/etc/vitastor/etcd.conf", { encoding: 'utf-8' });
|
|
||||||
etcd_conf = replace_env(etcd_conf, 'ETCD_NAME', etcd_name);
|
|
||||||
etcd_conf = replace_env(etcd_conf, 'ETCD_IP', etcds[num]);
|
|
||||||
etcd_conf = replace_env(etcd_conf, 'ETCD_INITIAL_CLUSTER', etcd_cluster);
|
|
||||||
fs.writeFileSync("/etc/vitastor/etcd.conf", etcd_conf);
|
|
||||||
console.log('etcd for Vitastor configured. Run `systemctl enable --now vitastor-etcd` to start etcd');
|
console.log('etcd for Vitastor configured. Run `systemctl enable --now vitastor-etcd` to start etcd');
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
await system(`mkdir -p /var/lib/etcd/vitastor`);
|
await system(`mkdir -p /var/lib/etcd/vitastor`);
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
"/etc/systemd/system/vitastor-etcd.service",
|
"/etc/systemd/system/vitastor-etcd.service", `[Unit]
|
||||||
`[Unit]
|
|
||||||
Description=etcd for vitastor
|
Description=etcd for vitastor
|
||||||
After=network-online.target local-fs.target time-sync.target
|
After=network-online.target local-fs.target time-sync.target
|
||||||
Wants=network-online.target local-fs.target time-sync.target
|
Wants=network-online.target local-fs.target time-sync.target
|
||||||
@@ -83,12 +414,8 @@ Wants=network-online.target local-fs.target time-sync.target
|
|||||||
[Service]
|
[Service]
|
||||||
Restart=always
|
Restart=always
|
||||||
Environment=GOGC=50
|
Environment=GOGC=50
|
||||||
ExecStart=etcd --name ${etcd_name} --data-dir /var/lib/etcd/vitastor \\
|
EnvironmentFile=/etc/vitastor/etcd.conf
|
||||||
--snapshot-count 10000 --advertise-client-urls ${etcd_url}:2379 --listen-client-urls ${etcd_url}:2379 \\
|
ExecStart=etcd --data-dir /var/lib/etcd/vitastor
|
||||||
--initial-advertise-peer-urls ${etcd_url}:2380 --listen-peer-urls ${etcd_url}:2380 \\
|
|
||||||
--initial-cluster-token vitastor-etcd-1 --initial-cluster ${etcd_cluster} \\
|
|
||||||
--initial-cluster-state new --max-txn-ops=100000 --max-request-bytes=104857600 \\
|
|
||||||
--auto-compaction-retention=10 --auto-compaction-mode=revision
|
|
||||||
WorkingDirectory=/var/lib/etcd/vitastor
|
WorkingDirectory=/var/lib/etcd/vitastor
|
||||||
ExecStartPre=+chown -R etcd /var/lib/etcd/vitastor
|
ExecStartPre=+chown -R etcd /var/lib/etcd/vitastor
|
||||||
User=etcd
|
User=etcd
|
||||||
@@ -102,37 +429,77 @@ RestartSec=10
|
|||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
`);
|
`);
|
||||||
await system(`useradd etcd`);
|
await system(`useradd etcd`);
|
||||||
|
await system(`chown vitastor:vitastor /etc/vitastor/*.key`);
|
||||||
|
if (options['key_file'])
|
||||||
|
{
|
||||||
|
await system(`chown etcd `+options['key_file']);
|
||||||
|
}
|
||||||
await system(`systemctl daemon-reload`);
|
await system(`systemctl daemon-reload`);
|
||||||
// Disable distribution etcd unit and enable our one
|
// Disable distribution etcd unit and enable our one
|
||||||
await system(`systemctl disable --now etcd`);
|
await system(`systemctl disable --now etcd`);
|
||||||
await system(`systemctl enable --now vitastor-etcd`);
|
await system(`systemctl enable --now vitastor-etcd`);
|
||||||
process.exit(0);
|
await system(`systemctl enable --now vitastor-mon`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function replace_env(text, key, value)
|
function replace_env(text, key, value)
|
||||||
{
|
{
|
||||||
let found = false;
|
let found = false;
|
||||||
text = text.replace(new RegExp('^'+key+'\\s*=.*', 'm'), () => { found = true; return key+'='+value; });
|
text = text.replace(new RegExp('^'+key+'\\s*=.*', 'm'), () => { found = true; return key+'='+value; });
|
||||||
return found ? text : text.replace(/\s*$/, '\n')+key+'='+value+'\n';
|
if (found)
|
||||||
|
return text;
|
||||||
|
return (text != '' ? text.replace(/\s*$/, '\n') : '')+key+'='+value+'\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
function select_local_etcd(etcds)
|
function select_local_etcd(etcds)
|
||||||
{
|
{
|
||||||
const ifaces = os.networkInterfaces();
|
const ifaces = os.networkInterfaces();
|
||||||
|
const local = {};
|
||||||
for (const ifname in ifaces)
|
for (const ifname in ifaces)
|
||||||
|
{
|
||||||
for (const iface of ifaces[ifname])
|
for (const iface of ifaces[ifname])
|
||||||
for (let i = 0; i < etcds.length; i++)
|
{
|
||||||
if (etcds[i] == iface.address.toLowerCase())
|
const addr = iface.address;
|
||||||
return i;
|
if (iface.family == 'IPv6')
|
||||||
|
local[addr.toLowerCase()] = local['['+addr.toLowerCase()+']'] = true;
|
||||||
|
else
|
||||||
|
local[addr] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = 0; i < etcds.length; i++)
|
||||||
|
{
|
||||||
|
if (local[etcds[i].addr])
|
||||||
|
return i;
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function system(cmd)
|
async function system(cmd)
|
||||||
{
|
{
|
||||||
|
console.log('Running '+cmd);
|
||||||
|
process.stdin.pause();
|
||||||
const cp = child_process.spawn(cmd, { shell: true, stdio: [ 0, 1, 2 ] });
|
const cp = child_process.spawn(cmd, { shell: true, stdio: [ 0, 1, 2 ] });
|
||||||
let finish_cb;
|
let finish_cb;
|
||||||
cp.on('exit', () => finish_cb && finish_cb());
|
cp.on('exit', () => finish_cb && finish_cb());
|
||||||
if (cp.exitCode == null)
|
if (cp.exitCode == null)
|
||||||
await new Promise(ok => finish_cb = ok);
|
await new Promise(ok => finish_cb = ok);
|
||||||
|
process.stdin.resume();
|
||||||
return cp.exitCode;
|
return cp.exitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function make_ca(subj, filename)
|
||||||
|
{
|
||||||
|
if (await system("openssl req -days 3650 -x509 -subj '"+subj+"' -addext basicConstraints=critical,CA:TRUE,pathlen:1"+
|
||||||
|
" -new -newkey rsa:4096 -nodes -keyout "+filename+".key -out "+filename+".crt"))
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function make_signed(subj, f, ca, san)
|
||||||
|
{
|
||||||
|
if (await system(`openssl req -subj '${subj}' ${san ? "-addext 'subjectAltName="+san+"'" : ""} -nodes -new -keyout ${f}.key -out ${f}.csr`))
|
||||||
|
process.exit(1);
|
||||||
|
if (await system(`openssl x509 -req -days 3650 -copy_extensions copy -CA ${ca}.crt -CAkey ${ca}.key -CAcreateserial -in ${f}.csr -out ${f}.crt`))
|
||||||
|
process.exit(1);
|
||||||
|
fs.unlinkSync(f+".csr");
|
||||||
|
}
|
||||||
|
|
||||||
|
run().catch(e => { console.error(e); process.exit(1); });
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ function derive_osd_stats(st, prev, prev_diff)
|
|||||||
const n = c.count - BigInt(pr && pr.count||0);
|
const n = c.count - BigInt(pr && pr.count||0);
|
||||||
diff.recovery_stats[op] = { ...c, bps: n > 0 ? b*1000n/timediff : 0n, iops: n > 0 ? n*1000n/timediff : 0n };
|
diff.recovery_stats[op] = { ...c, bps: n > 0 ? b*1000n/timediff : 0n, iops: n > 0 ? n*1000n/timediff : 0n };
|
||||||
}
|
}
|
||||||
|
diff.inode_stats = {};
|
||||||
for (const pool_id in st.inode_stats||{})
|
for (const pool_id in st.inode_stats||{})
|
||||||
{
|
{
|
||||||
diff.inode_stats[pool_id] = {};
|
diff.inode_stats[pool_id] = {};
|
||||||
|
|||||||
@@ -5,17 +5,19 @@
|
|||||||
// Permissions are based on:
|
// Permissions are based on:
|
||||||
// 1. Users.
|
// 1. Users.
|
||||||
// Stored in /vitastor/config/user/<username>.
|
// Stored in /vitastor/config/user/<username>.
|
||||||
// Has 2 properties:
|
// Has 1 property:
|
||||||
// - type, one of: osd, mon, admin, client.
|
|
||||||
// osd, mon types should be used by OSDs/monitors.
|
|
||||||
// admin should be used for administrative access from vitastor-cli.
|
|
||||||
// client should be used for regular clients.
|
|
||||||
// - groups, a list of group names the user is included in.
|
// - groups, a list of group names the user is included in.
|
||||||
// 2. Images.
|
// 2. Images.
|
||||||
// Stored in /vitastor/config/inode/<pool>/<inode>. Has the following properties:
|
// Stored in /vitastor/config/inode/<pool>/<inode>. Has the following properties:
|
||||||
// - owner (user name)
|
// - owner (user name)
|
||||||
// - owner_group (group name)
|
// - owner_group (group name)
|
||||||
// - reader_group
|
// - reader_group
|
||||||
|
// 3. Certificates.
|
||||||
|
// - osd_ca identifies OSDs.
|
||||||
|
// - admin_ca (optional) identifies administrators.
|
||||||
|
// - mon_ca (optional) identifies monitors.
|
||||||
|
|
||||||
|
const { X509Certificate } = require('node:crypto');
|
||||||
|
|
||||||
const static_perms = {
|
const static_perms = {
|
||||||
invalid: {
|
invalid: {
|
||||||
@@ -24,17 +26,17 @@ const static_perms = {
|
|||||||
},
|
},
|
||||||
osd: {
|
osd: {
|
||||||
keys: { '/pg/config': false },
|
keys: { '/pg/config': false },
|
||||||
prefixes: { '/osd/': true, '/pg/state/': true, '/pg/history/': true, '/pgstats/': true },
|
prefixes: { '/config/': false, '/osd/': true, '/pg/state/': true, '/pg/history/': true, '/pgstats/': true },
|
||||||
},
|
},
|
||||||
mon: {
|
mon: {
|
||||||
keys: { '/pg/config': true, '/stats': true, '/history/last_clean_pgs': true },
|
keys: { '/pg/config': true, '/stats': true, '/history/last_clean_pgs': true },
|
||||||
prefixes: {
|
prefixes: {
|
||||||
'/config/': false, '/osd/': false, '/mon/': true, '/pg/history/': true,
|
'/config/': false, '/osd/': false, '/mon/': true, '/pg/history/': true,
|
||||||
'/pgstats/': false, '/inode/stats/': true, '/pool/stats/': true,
|
'/pg/stats/': true, '/pgstats/': true, '/inode/stats/': true, '/pool/stats/': true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
admin: {
|
admin: {
|
||||||
keys: { '/stats': false },
|
keys: { '/pg/config': true, '/stats': true, '/history/last_clean_pgs': true },
|
||||||
prefixes: {
|
prefixes: {
|
||||||
'/config/': true, '/osd/': true, '/index/': true, '/pg/history/': true,
|
'/config/': true, '/osd/': true, '/index/': true, '/pg/history/': true,
|
||||||
'/mon/': false, '/pg/': false, '/pgstats/': false, '/inode/stats/': false, '/pool/stats/': false,
|
'/mon/': false, '/pg/': false, '/pgstats/': false, '/inode/stats/': false, '/pool/stats/': false,
|
||||||
@@ -42,15 +44,15 @@ const static_perms = {
|
|||||||
},
|
},
|
||||||
client: {
|
client: {
|
||||||
keys: { '/config/global': false, '/config/node_placement': false, '/config/pools': false, '/pg/config': false },
|
keys: { '/config/global': false, '/config/node_placement': false, '/config/pools': false, '/pg/config': false },
|
||||||
prefixes: { '/osd/stats/': false, '/pg/state/': false, '/index/maxid/': false },
|
prefixes: { '/osd/state/': false, '/pg/state/': false, '/index/maxid/': false },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const api_perms = {
|
const api_perms = {
|
||||||
osd: { lease_grant: true, lease_revoke: true, lease_keepalive: true },
|
osd: { lease_grant: true, lease_revoke: true, lease_keepalive: true, maintenance_status: true },
|
||||||
mon: { lease_grant: true, lease_revoke: true, lease_keepalive: true },
|
mon: { lease_grant: true, lease_revoke: true, lease_keepalive: true, maintenance_status: true },
|
||||||
admin: { maintenance_status: true },
|
admin: { maintenance_status: true },
|
||||||
client: {},
|
client: { maintenance_status: true },
|
||||||
};
|
};
|
||||||
|
|
||||||
class VitastorAuthFilter
|
class VitastorAuthFilter
|
||||||
@@ -63,6 +65,47 @@ class VitastorAuthFilter
|
|||||||
this.prefix_parts = this.prefix.split('/');
|
this.prefix_parts = this.prefix.split('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async init()
|
||||||
|
{
|
||||||
|
if (!this.cfg.cert || !this.cfg.key || !this.cfg.ca || !this.cfg.osd_ca || !this.cfg.client_cert_auth)
|
||||||
|
{
|
||||||
|
throw new Error('Authenticated Vitastor setups require enabled client_cert_auth, cert, key'+
|
||||||
|
' and separate ca (client CA), osd_ca and optionally admin_ca and mon_ca');
|
||||||
|
}
|
||||||
|
this.osd_ca = await this.antietcd.readPEM(this.cfg.osd_ca);
|
||||||
|
this.osd_ca_obj = new X509Certificate(this.osd_ca);
|
||||||
|
this.antietcd.tls.ca.push(this.osd_ca);
|
||||||
|
if (this.cfg.mon_ca)
|
||||||
|
{
|
||||||
|
this.mon_ca = await this.antietcd.readPEM(this.cfg.mon_ca);
|
||||||
|
this.mon_ca_obj = new X509Certificate(this.mon_ca);
|
||||||
|
this.antietcd.tls.ca.push(this.mon_ca);
|
||||||
|
}
|
||||||
|
if (this.cfg.admin_ca)
|
||||||
|
{
|
||||||
|
this.admin_ca = await this.antietcd.readPEM(this.cfg.admin_ca);
|
||||||
|
this.admin_ca_obj = new X509Certificate(this.admin_ca);
|
||||||
|
this.antietcd.tls.ca.push(this.admin_ca);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init_context(context, socket)
|
||||||
|
{
|
||||||
|
let cert = socket.getPeerX509Certificate();
|
||||||
|
if (cert.issuer == this.osd_ca_obj.subject && cert.verify(this.osd_ca_obj.publicKey))
|
||||||
|
{
|
||||||
|
context.user_type = 'osd';
|
||||||
|
}
|
||||||
|
else if (this.mon_ca_obj && cert.issuer == this.mon_ca_obj.subject && cert.verify(this.mon_ca_obj.publicKey))
|
||||||
|
{
|
||||||
|
context.user_type = 'mon';
|
||||||
|
}
|
||||||
|
else if (this.admin_ca_obj && cert.issuer == this.admin_ca_obj.subject && cert.verify(this.admin_ca_obj.publicKey))
|
||||||
|
{
|
||||||
|
context.user_type = 'admin';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_get(path, decode)
|
_get(path, decode)
|
||||||
{
|
{
|
||||||
let cur = this.antietcd.etctree.state;
|
let cur = this.antietcd.etctree.state;
|
||||||
@@ -106,7 +149,7 @@ class VitastorAuthFilter
|
|||||||
return cur;
|
return cur;
|
||||||
}
|
}
|
||||||
|
|
||||||
// userInfo: { name: string, type: string, perms: static_perms[type], groups: { [string]: true } }
|
// userInfo: { name: string, perms: static_perms[type], groups: { [string]: true } }
|
||||||
_check_compare(check, userInfo, checked)
|
_check_compare(check, userInfo, checked)
|
||||||
{
|
{
|
||||||
let key = String(check.key);
|
let key = String(check.key);
|
||||||
@@ -263,6 +306,18 @@ class VitastorAuthFilter
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (key.substr(0, 13) == '/inode/stats/')
|
||||||
|
{
|
||||||
|
const [ pool_id, id ] = key.substr(13).split('/');
|
||||||
|
const inode = this._get([ ...this.prefix_parts, 'config', 'inode', pool_id, id ], true);
|
||||||
|
if (inode && (inode.reader_group && userInfo.groups[inode.reader_group] ||
|
||||||
|
inode.owner_group && userInfo.groups[inode.owner_group] ||
|
||||||
|
inode.owner === userInfo.name))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -349,19 +404,27 @@ class VitastorAuthFilter
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_get_user(username)
|
_get_user(context)
|
||||||
{
|
{
|
||||||
if (!username)
|
if (context.user_type === 'osd' || context.user_type === 'mon' || context.user_type === 'admin')
|
||||||
{
|
{
|
||||||
return null;
|
return {
|
||||||
|
name: context.user_type,
|
||||||
|
type: context.user_type,
|
||||||
|
perms: static_perms[context.user_type],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
let userInfo = this._get([ ...this.prefix_parts, 'config', 'user', username ], true);
|
if (!context.username)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
let userInfo = this._get([ ...this.prefix_parts, 'config', 'user', context.username ], true);
|
||||||
if (!userInfo)
|
if (!userInfo)
|
||||||
{
|
{
|
||||||
userInfo = { type: 'client' };
|
userInfo = {};
|
||||||
}
|
}
|
||||||
userInfo.perms = static_perms[userInfo.type] || static_perms['invalid'];
|
userInfo.perms = static_perms[userInfo.type] || static_perms['invalid'];
|
||||||
userInfo.name = username;
|
userInfo.name = context.username;
|
||||||
if (userInfo.groups instanceof Array)
|
if (userInfo.groups instanceof Array)
|
||||||
{
|
{
|
||||||
userInfo.groups = userInfo.groups.reduce((a, c) => { a[c] = true; return a; }, {});
|
userInfo.groups = userInfo.groups.reduce((a, c) => { a[c] = true; return a; }, {});
|
||||||
@@ -373,23 +436,19 @@ class VitastorAuthFilter
|
|||||||
return userInfo;
|
return userInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
filter_api(username, api/*, data*/)
|
filter_api(context, api/*, data*/)
|
||||||
{
|
{
|
||||||
if (username === 'root')
|
let type = 'client';
|
||||||
|
if (context.user_type === 'osd' || context.user_type === 'mon' || context.user_type === 'admin')
|
||||||
{
|
{
|
||||||
return true;
|
type = context.user_type;
|
||||||
}
|
}
|
||||||
const userInfo = this._get([ ...this.prefix_parts, 'config', 'user', username ], true);
|
return api_perms[type] && api_perms[type][api];
|
||||||
return userInfo && api_perms[userInfo.type] && api_perms[userInfo.type][api];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
filter_txn(username, txn)
|
filter_txn(context, txn)
|
||||||
{
|
{
|
||||||
if (username === 'root')
|
const userInfo = this._get_user(context);
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const userInfo = this._get_user(username);
|
|
||||||
if (!userInfo)
|
if (!userInfo)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@@ -425,13 +484,13 @@ class VitastorAuthFilter
|
|||||||
return txn;
|
return txn;
|
||||||
}
|
}
|
||||||
|
|
||||||
filter_txn_response(username, txn, res)
|
filter_txn_response(context, txn, res)
|
||||||
{
|
{
|
||||||
if (!res.responses || username === 'root')
|
if (!res.responses)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const userInfo = this._get_user(username);
|
const userInfo = this._get_user(context);
|
||||||
if (!userInfo)
|
if (!userInfo)
|
||||||
{
|
{
|
||||||
for (const resp of res.responses)
|
for (const resp of res.responses)
|
||||||
@@ -452,13 +511,13 @@ class VitastorAuthFilter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
filter_watch_message(username, msg)
|
filter_watch_message(context, msg)
|
||||||
{
|
{
|
||||||
if (!msg.result || !msg.result.events || username === 'root')
|
if (!msg.result || !msg.result.events)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const userInfo = this._get_user(username);
|
const userInfo = this._get_user(context);
|
||||||
if (!userInfo)
|
if (!userInfo)
|
||||||
{
|
{
|
||||||
msg.result.events = [];
|
msg.result.events = [];
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "vitastor",
|
"name": "vitastor",
|
||||||
"version": "3.0.9",
|
"version": "3.2.1",
|
||||||
"description": "Low-level native bindings to Vitastor client library",
|
"description": "Low-level native bindings to Vitastor client library",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -366,15 +366,38 @@ sub map_volume
|
|||||||
my $prefix = defined $scfg->{vitastor_prefix} ? $scfg->{vitastor_prefix} : 'pve/';
|
my $prefix = defined $scfg->{vitastor_prefix} ? $scfg->{vitastor_prefix} : 'pve/';
|
||||||
|
|
||||||
my ($vtype, $img_name, $vmid) = $class->parse_volname($volname);
|
my ($vtype, $img_name, $vmid) = $class->parse_volname($volname);
|
||||||
my $name = $img_name;
|
my $name = $prefix.$img_name;
|
||||||
$name .= '@'.$snapname if $snapname;
|
$name .= '@'.$snapname if $snapname;
|
||||||
|
|
||||||
my $mapped = run_cli($scfg, [ 'ls' ], binary => '/usr/bin/vitastor-nbd');
|
my $mapped = run_cli($scfg, [ 'ls' ], binary => '/usr/bin/vitastor-nbd');
|
||||||
my ($kerneldev) = grep { $mapped->{$_}->{image} eq $prefix.$name } keys %$mapped;
|
my ($kerneldev) = grep {
|
||||||
return $kerneldev if $kerneldev && -b $kerneldev; # already mapped
|
$mapped->{$_} && $mapped->{$_}->{image} && $mapped->{$_}->{image} eq $name
|
||||||
|
} keys %$mapped;
|
||||||
|
|
||||||
$kerneldev = run_cli($scfg, [ 'map', '--image', $prefix.$name ], binary => '/usr/bin/vitastor-nbd', json => 0);
|
if ($kerneldev && -b $kerneldev)
|
||||||
return $kerneldev;
|
{
|
||||||
|
my $size = `/usr/sbin/blockdev --getsize64 $kerneldev`;
|
||||||
|
return $kerneldev if $size && $size > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $map_out = run_cli($scfg, [ 'map', '--image', $name ], binary => '/usr/bin/vitastor-nbd', json => 0);
|
||||||
|
$map_out =~ s/^\s+|\s+$//gso;
|
||||||
|
|
||||||
|
# Wait until the device is started
|
||||||
|
for (my $i = 0; $i < 100; $i++)
|
||||||
|
{
|
||||||
|
$mapped = run_cli($scfg, [ 'ls' ], binary => '/usr/bin/vitastor-nbd');
|
||||||
|
($kerneldev) = grep { $mapped->{$_} && $mapped->{$_}->{image} && $mapped->{$_}->{image} eq $name } keys %$mapped;
|
||||||
|
if ($kerneldev && -b $kerneldev)
|
||||||
|
{
|
||||||
|
my $size = `/usr/sbin/blockdev --getsize64 $kerneldev`;
|
||||||
|
return $kerneldev if $size && $size > 0;
|
||||||
|
}
|
||||||
|
select(undef, undef, undef, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
die "Failed to map Vitastor image $name via NBD".
|
||||||
|
($map_out ? ", vitastor-nbd map returned '$map_out'" : "")."\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
sub unmap_volume
|
sub unmap_volume
|
||||||
@@ -383,13 +406,19 @@ sub unmap_volume
|
|||||||
my $prefix = defined $scfg->{vitastor_prefix} ? $scfg->{vitastor_prefix} : 'pve/';
|
my $prefix = defined $scfg->{vitastor_prefix} ? $scfg->{vitastor_prefix} : 'pve/';
|
||||||
|
|
||||||
my ($vtype, $name, $vmid) = $class->parse_volname($volname);
|
my ($vtype, $name, $vmid) = $class->parse_volname($volname);
|
||||||
|
$name = $prefix.$name;
|
||||||
$name .= '@'.$snapname if $snapname;
|
$name .= '@'.$snapname if $snapname;
|
||||||
|
|
||||||
my $mapped = run_cli($scfg, [ 'ls' ], binary => '/usr/bin/vitastor-nbd');
|
my $mapped = run_cli($scfg, [ 'ls' ], binary => '/usr/bin/vitastor-nbd');
|
||||||
my ($kerneldev) = grep { $mapped->{$_}->{image} eq $prefix.$name } keys %$mapped;
|
|
||||||
if ($kerneldev && -b $kerneldev)
|
my @kerneldevs = grep {
|
||||||
|
$mapped->{$_} && $mapped->{$_}->{image} && $mapped->{$_}->{image} eq $name
|
||||||
|
} keys %$mapped;
|
||||||
|
|
||||||
|
for my $kerneldev (@kerneldevs)
|
||||||
{
|
{
|
||||||
run_cli($scfg, [ 'unmap', $kerneldev ], binary => '/usr/bin/vitastor-nbd', json => 0);
|
next if !$kerneldev || !-b $kerneldev;
|
||||||
|
eval { run_cli($scfg, [ 'unmap', $kerneldev ], binary => '/usr/bin/vitastor-nbd', json => 0); };
|
||||||
|
warn "Failed to unmap Vitastor image $name from $kerneldev: $@" if $@;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@@ -405,7 +434,13 @@ sub activate_volume
|
|||||||
sub deactivate_volume
|
sub deactivate_volume
|
||||||
{
|
{
|
||||||
my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
|
my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
|
||||||
$class->unmap_volume($storeid, $scfg, $volname, $snapname) if $scfg->{vitastor_nbd};
|
|
||||||
|
# Even with vitastor_nbd=0, Proxmox may call map_volume() for special
|
||||||
|
# volumes like tpmstate0 because swtpm needs a local file/block path.
|
||||||
|
# Therefore, always try to unmap an existing NBD mapping here.
|
||||||
|
# unmap_volume() is a no-op if the volume is not currently mapped.
|
||||||
|
$class->unmap_volume($storeid, $scfg, $volname, $snapname);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ from cinder.volume import configuration
|
|||||||
from cinder.volume import driver
|
from cinder.volume import driver
|
||||||
from cinder.volume import volume_utils
|
from cinder.volume import volume_utils
|
||||||
|
|
||||||
VITASTOR_VERSION = '3.0.9'
|
VITASTOR_VERSION = '3.2.1'
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -608,7 +608,7 @@ class VitastorDriver(driver.CloneableImageVD,
|
|||||||
"""Extend an existing volume."""
|
"""Extend an existing volume."""
|
||||||
vol_name = utils.convert_str(volume.name)
|
vol_name = utils.convert_str(volume.name)
|
||||||
size = int(new_size) * units.Gi
|
size = int(new_size) * units.Gi
|
||||||
self._cli('extend volume', 'modify', vol_name, '--resize', new_size)
|
self._cli('extend volume', 'modify', vol_name, '--resize', size)
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
"Extend volume from %(old_size)s GB to %(new_size)s GB.",
|
"Extend volume from %(old_size)s GB to %(new_size)s GB.",
|
||||||
{'old_size': volume.size, 'new_size': new_size}
|
{'old_size': volume.size, 'new_size': new_size}
|
||||||
|
|||||||
699
patches/libvirt-11.10-vitastor.patch
Normal file
699
patches/libvirt-11.10-vitastor.patch
Normal file
@@ -0,0 +1,699 @@
|
|||||||
|
From 560382fdf86fdbbd08c38187ef602c04e0abece0 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Vitaliy Filippov <vitalif@yourcmc.ru>
|
||||||
|
Date: Sat, 5 Sep 2026 20:41:38 +0300
|
||||||
|
Subject: [PATCH] Add Vitastor support
|
||||||
|
|
||||||
|
---
|
||||||
|
include/libvirt/libvirt-storage.h | 1 +
|
||||||
|
src/conf/domain_conf.c | 4 +-
|
||||||
|
src/conf/domain_validate.c | 11 +-
|
||||||
|
src/conf/schemas/domaincommon.rng | 30 +++++
|
||||||
|
src/conf/storage_conf.c | 20 ++-
|
||||||
|
src/conf/storage_conf.h | 2 +
|
||||||
|
src/conf/storage_source_conf.c | 2 +
|
||||||
|
src/conf/storage_source_conf.h | 1 +
|
||||||
|
src/conf/virstorageobj.c | 3 +
|
||||||
|
src/libvirt-storage.c | 1 +
|
||||||
|
src/libxl/libxl_conf.c | 1 +
|
||||||
|
src/libxl/xen_xl.c | 1 +
|
||||||
|
src/qemu/qemu_block.c | 45 +++++++
|
||||||
|
src/qemu/qemu_domain.c | 5 +-
|
||||||
|
src/qemu/qemu_nbdkit.c | 2 +
|
||||||
|
src/qemu/qemu_snapshot.c | 2 +
|
||||||
|
src/storage/storage_driver.c | 1 +
|
||||||
|
.../storage_source_backingstore.c | 123 ++++++++++++++++++
|
||||||
|
src/test/test_driver.c | 1 +
|
||||||
|
.../storagepoolcapsschemadata/poolcaps-fs.xml | 7 +
|
||||||
|
.../poolcaps-full.xml | 7 +
|
||||||
|
tests/storagepoolxml2argvtest.c | 1 +
|
||||||
|
tools/virsh-pool.c | 3 +
|
||||||
|
23 files changed, 268 insertions(+), 6 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/include/libvirt/libvirt-storage.h b/include/libvirt/libvirt-storage.h
|
||||||
|
index aaad4a3da1..5f5daa8341 100644
|
||||||
|
--- a/include/libvirt/libvirt-storage.h
|
||||||
|
+++ b/include/libvirt/libvirt-storage.h
|
||||||
|
@@ -326,6 +326,7 @@ typedef enum {
|
||||||
|
VIR_CONNECT_LIST_STORAGE_POOLS_ZFS = 1 << 17, /* (Since: 1.2.8) */
|
||||||
|
VIR_CONNECT_LIST_STORAGE_POOLS_VSTORAGE = 1 << 18, /* (Since: 3.1.0) */
|
||||||
|
VIR_CONNECT_LIST_STORAGE_POOLS_ISCSI_DIRECT = 1 << 19, /* (Since: 5.6.0) */
|
||||||
|
+ VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR = 1 << 20, /* (Since: 5.0.0) */
|
||||||
|
} virConnectListAllStoragePoolsFlags;
|
||||||
|
|
||||||
|
int virConnectListAllStoragePools(virConnectPtr conn,
|
||||||
|
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
|
||||||
|
index 541dad5bdc..5cb94e1fed 100644
|
||||||
|
--- a/src/conf/domain_conf.c
|
||||||
|
+++ b/src/conf/domain_conf.c
|
||||||
|
@@ -7453,7 +7453,8 @@ virDomainDiskSourceNetworkParse(xmlNodePtr node,
|
||||||
|
src->configFile = virXPathString("string(./config/@file)", ctxt);
|
||||||
|
|
||||||
|
if (src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTP ||
|
||||||
|
- src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS)
|
||||||
|
+ src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS ||
|
||||||
|
+ src->protocol == VIR_STORAGE_NET_PROTOCOL_VITASTOR)
|
||||||
|
src->query = virXMLPropString(node, "query");
|
||||||
|
|
||||||
|
if (virDomainStorageNetworkParseHosts(node, ctxt, &src->hosts, &src->nhosts) < 0)
|
||||||
|
@@ -32196,6 +32197,7 @@ virDomainStorageSourceTranslateSourcePool(virStorageSource *src,
|
||||||
|
|
||||||
|
case VIR_STORAGE_POOL_MPATH:
|
||||||
|
case VIR_STORAGE_POOL_RBD:
|
||||||
|
+ case VIR_STORAGE_POOL_VITASTOR:
|
||||||
|
case VIR_STORAGE_POOL_SHEEPDOG:
|
||||||
|
case VIR_STORAGE_POOL_GLUSTER:
|
||||||
|
case VIR_STORAGE_POOL_LAST:
|
||||||
|
diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c
|
||||||
|
index 4558e7b210..3c6b2a8ee8 100644
|
||||||
|
--- a/src/conf/domain_validate.c
|
||||||
|
+++ b/src/conf/domain_validate.c
|
||||||
|
@@ -520,6 +520,7 @@ virDomainDiskDefValidateSourceChainOne(const virStorageSource *src)
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
||||||
|
break;
|
||||||
|
|
||||||
|
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_NBD:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
|
||||||
|
@@ -592,7 +593,7 @@ virDomainDiskDefValidateSourceChainOne(const virStorageSource *src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- /* internal snapshots and config files are currently supported only with rbd: */
|
||||||
|
+ /* internal snapshots are currently supported only with rbd: */
|
||||||
|
if (virStorageSourceGetActualType(src) != VIR_STORAGE_TYPE_NETWORK &&
|
||||||
|
src->protocol != VIR_STORAGE_NET_PROTOCOL_RBD) {
|
||||||
|
if (src->snapshot) {
|
||||||
|
@@ -600,10 +601,14 @@ virDomainDiskDefValidateSourceChainOne(const virStorageSource *src)
|
||||||
|
_("<snapshot> element is currently supported only with 'rbd' disks"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
-
|
||||||
|
+ }
|
||||||
|
+ /* config files are currently supported only with rbd and vitastor: */
|
||||||
|
+ if (virStorageSourceGetActualType(src) != VIR_STORAGE_TYPE_NETWORK &&
|
||||||
|
+ src->protocol != VIR_STORAGE_NET_PROTOCOL_RBD &&
|
||||||
|
+ src->protocol != VIR_STORAGE_NET_PROTOCOL_VITASTOR) {
|
||||||
|
if (src->configFile) {
|
||||||
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
||||||
|
- _("<config> element is currently supported only with 'rbd' disks"));
|
||||||
|
+ _("<config> element is currently supported only with 'rbd' and 'vitastor' disks"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng
|
||||||
|
index 1f9ac102a0..51b55ea72b 100644
|
||||||
|
--- a/src/conf/schemas/domaincommon.rng
|
||||||
|
+++ b/src/conf/schemas/domaincommon.rng
|
||||||
|
@@ -2092,6 +2092,35 @@
|
||||||
|
</element>
|
||||||
|
</define>
|
||||||
|
|
||||||
|
+ <define name="diskSourceNetworkProtocolVitastor">
|
||||||
|
+ <element name="source">
|
||||||
|
+ <interleave>
|
||||||
|
+ <attribute name="protocol">
|
||||||
|
+ <value>vitastor</value>
|
||||||
|
+ </attribute>
|
||||||
|
+ <ref name="diskSourceCommon"/>
|
||||||
|
+ <optional>
|
||||||
|
+ <attribute name="name"/>
|
||||||
|
+ </optional>
|
||||||
|
+ <optional>
|
||||||
|
+ <attribute name="query"/>
|
||||||
|
+ </optional>
|
||||||
|
+ <zeroOrMore>
|
||||||
|
+ <ref name="diskSourceNetworkHost"/>
|
||||||
|
+ </zeroOrMore>
|
||||||
|
+ <optional>
|
||||||
|
+ <element name="config">
|
||||||
|
+ <attribute name="file">
|
||||||
|
+ <ref name="absFilePath"/>
|
||||||
|
+ </attribute>
|
||||||
|
+ <empty/>
|
||||||
|
+ </element>
|
||||||
|
+ </optional>
|
||||||
|
+ <empty/>
|
||||||
|
+ </interleave>
|
||||||
|
+ </element>
|
||||||
|
+ </define>
|
||||||
|
+
|
||||||
|
<define name="diskSourceNetworkProtocolISCSI">
|
||||||
|
<element name="source">
|
||||||
|
<attribute name="protocol">
|
||||||
|
@@ -2442,6 +2471,7 @@
|
||||||
|
<ref name="diskSourceNetworkProtocolSimple"/>
|
||||||
|
<ref name="diskSourceNetworkProtocolVxHS"/>
|
||||||
|
<ref name="diskSourceNetworkProtocolNFS"/>
|
||||||
|
+ <ref name="diskSourceNetworkProtocolVitastor"/>
|
||||||
|
</choice>
|
||||||
|
</define>
|
||||||
|
|
||||||
|
diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c
|
||||||
|
index 1dc9365bf2..a8a736be81 100644
|
||||||
|
--- a/src/conf/storage_conf.c
|
||||||
|
+++ b/src/conf/storage_conf.c
|
||||||
|
@@ -56,7 +56,7 @@ VIR_ENUM_IMPL(virStoragePool,
|
||||||
|
"logical", "disk", "iscsi",
|
||||||
|
"iscsi-direct", "scsi", "mpath",
|
||||||
|
"rbd", "sheepdog", "gluster",
|
||||||
|
- "zfs", "vstorage",
|
||||||
|
+ "zfs", "vstorage", "vitastor",
|
||||||
|
);
|
||||||
|
|
||||||
|
VIR_ENUM_IMPL(virStoragePoolFormatFileSystem,
|
||||||
|
@@ -242,6 +242,18 @@ static virStoragePoolTypeInfo poolTypeInfo[] = {
|
||||||
|
.formatToString = virStorageFileFormatTypeToString,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
+ {.poolType = VIR_STORAGE_POOL_VITASTOR,
|
||||||
|
+ .poolOptions = {
|
||||||
|
+ .flags = (VIR_STORAGE_POOL_SOURCE_HOST |
|
||||||
|
+ VIR_STORAGE_POOL_SOURCE_NETWORK |
|
||||||
|
+ VIR_STORAGE_POOL_SOURCE_NAME),
|
||||||
|
+ },
|
||||||
|
+ .volOptions = {
|
||||||
|
+ .defaultFormat = VIR_STORAGE_FILE_RAW,
|
||||||
|
+ .formatFromString = virStorageVolumeFormatFromString,
|
||||||
|
+ .formatToString = virStorageFileFormatTypeToString,
|
||||||
|
+ }
|
||||||
|
+ },
|
||||||
|
{.poolType = VIR_STORAGE_POOL_SHEEPDOG,
|
||||||
|
.poolOptions = {
|
||||||
|
.flags = (VIR_STORAGE_POOL_SOURCE_HOST |
|
||||||
|
@@ -538,6 +550,11 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt,
|
||||||
|
_("element 'name' is mandatory for RBD pool"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
+ if (pool_type == VIR_STORAGE_POOL_VITASTOR && source->name == NULL) {
|
||||||
|
+ virReportError(VIR_ERR_XML_ERROR, "%s",
|
||||||
|
+ _("element 'name' is mandatory for Vitastor pool"));
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
if (options->formatFromString) {
|
||||||
|
g_autofree char *format = NULL;
|
||||||
|
@@ -1127,6 +1144,7 @@ virStoragePoolDefFormatBuf(virBuffer *buf,
|
||||||
|
/* RBD, Sheepdog, Gluster and Iscsi-direct devices are not local block devs nor
|
||||||
|
* files, so they don't have a target */
|
||||||
|
if (def->type != VIR_STORAGE_POOL_RBD &&
|
||||||
|
+ def->type != VIR_STORAGE_POOL_VITASTOR &&
|
||||||
|
def->type != VIR_STORAGE_POOL_SHEEPDOG &&
|
||||||
|
def->type != VIR_STORAGE_POOL_GLUSTER &&
|
||||||
|
def->type != VIR_STORAGE_POOL_ISCSI_DIRECT) {
|
||||||
|
diff --git a/src/conf/storage_conf.h b/src/conf/storage_conf.h
|
||||||
|
index fc67957cfe..720c07ef74 100644
|
||||||
|
--- a/src/conf/storage_conf.h
|
||||||
|
+++ b/src/conf/storage_conf.h
|
||||||
|
@@ -103,6 +103,7 @@ typedef enum {
|
||||||
|
VIR_STORAGE_POOL_GLUSTER, /* Gluster device */
|
||||||
|
VIR_STORAGE_POOL_ZFS, /* ZFS */
|
||||||
|
VIR_STORAGE_POOL_VSTORAGE, /* Virtuozzo Storage */
|
||||||
|
+ VIR_STORAGE_POOL_VITASTOR, /* Vitastor */
|
||||||
|
|
||||||
|
VIR_STORAGE_POOL_LAST,
|
||||||
|
} virStoragePoolType;
|
||||||
|
@@ -454,6 +455,7 @@ VIR_ENUM_DECL(virStoragePartedFs);
|
||||||
|
VIR_CONNECT_LIST_STORAGE_POOLS_SCSI | \
|
||||||
|
VIR_CONNECT_LIST_STORAGE_POOLS_MPATH | \
|
||||||
|
VIR_CONNECT_LIST_STORAGE_POOLS_RBD | \
|
||||||
|
+ VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR | \
|
||||||
|
VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG | \
|
||||||
|
VIR_CONNECT_LIST_STORAGE_POOLS_GLUSTER | \
|
||||||
|
VIR_CONNECT_LIST_STORAGE_POOLS_ZFS | \
|
||||||
|
diff --git a/src/conf/storage_source_conf.c b/src/conf/storage_source_conf.c
|
||||||
|
index 087de1eaf2..acc046816b 100644
|
||||||
|
--- a/src/conf/storage_source_conf.c
|
||||||
|
+++ b/src/conf/storage_source_conf.c
|
||||||
|
@@ -89,6 +89,7 @@ VIR_ENUM_IMPL(virStorageNetProtocol,
|
||||||
|
"ssh",
|
||||||
|
"vxhs",
|
||||||
|
"nfs",
|
||||||
|
+ "vitastor",
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1313,6 +1314,7 @@ virStorageSourceNetworkDefaultPort(virStorageNetProtocol protocol)
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
|
||||||
|
return 24007;
|
||||||
|
|
||||||
|
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
||||||
|
/* we don't provide a default for RBD */
|
||||||
|
return 0;
|
||||||
|
diff --git a/src/conf/storage_source_conf.h b/src/conf/storage_source_conf.h
|
||||||
|
index fc868b31af..1c9fbc02b9 100644
|
||||||
|
--- a/src/conf/storage_source_conf.h
|
||||||
|
+++ b/src/conf/storage_source_conf.h
|
||||||
|
@@ -130,6 +130,7 @@ typedef enum {
|
||||||
|
VIR_STORAGE_NET_PROTOCOL_SSH,
|
||||||
|
VIR_STORAGE_NET_PROTOCOL_VXHS,
|
||||||
|
VIR_STORAGE_NET_PROTOCOL_NFS,
|
||||||
|
+ VIR_STORAGE_NET_PROTOCOL_VITASTOR,
|
||||||
|
|
||||||
|
VIR_STORAGE_NET_PROTOCOL_LAST
|
||||||
|
} virStorageNetProtocol;
|
||||||
|
diff --git a/src/conf/virstorageobj.c b/src/conf/virstorageobj.c
|
||||||
|
index 59fa5da372..4739167f5f 100644
|
||||||
|
--- a/src/conf/virstorageobj.c
|
||||||
|
+++ b/src/conf/virstorageobj.c
|
||||||
|
@@ -1438,6 +1438,7 @@ virStoragePoolObjSourceFindDuplicateCb(const void *payload,
|
||||||
|
return 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
+ case VIR_STORAGE_POOL_VITASTOR:
|
||||||
|
case VIR_STORAGE_POOL_ISCSI_DIRECT:
|
||||||
|
case VIR_STORAGE_POOL_RBD:
|
||||||
|
case VIR_STORAGE_POOL_LAST:
|
||||||
|
@@ -1921,6 +1922,8 @@ virStoragePoolObjMatch(virStoragePoolObj *obj,
|
||||||
|
(obj->def->type == VIR_STORAGE_POOL_MPATH)) ||
|
||||||
|
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_RBD) &&
|
||||||
|
(obj->def->type == VIR_STORAGE_POOL_RBD)) ||
|
||||||
|
+ (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR) &&
|
||||||
|
+ (obj->def->type == VIR_STORAGE_POOL_VITASTOR)) ||
|
||||||
|
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG) &&
|
||||||
|
(obj->def->type == VIR_STORAGE_POOL_SHEEPDOG)) ||
|
||||||
|
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_GLUSTER) &&
|
||||||
|
diff --git a/src/libvirt-storage.c b/src/libvirt-storage.c
|
||||||
|
index db7660aac4..561df34709 100644
|
||||||
|
--- a/src/libvirt-storage.c
|
||||||
|
+++ b/src/libvirt-storage.c
|
||||||
|
@@ -94,6 +94,7 @@ virStoragePoolGetConnect(virStoragePoolPtr pool)
|
||||||
|
* VIR_CONNECT_LIST_STORAGE_POOLS_SCSI
|
||||||
|
* VIR_CONNECT_LIST_STORAGE_POOLS_MPATH
|
||||||
|
* VIR_CONNECT_LIST_STORAGE_POOLS_RBD
|
||||||
|
+ * VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR
|
||||||
|
* VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG
|
||||||
|
* VIR_CONNECT_LIST_STORAGE_POOLS_GLUSTER
|
||||||
|
* VIR_CONNECT_LIST_STORAGE_POOLS_ZFS
|
||||||
|
diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c
|
||||||
|
index 2b988157fa..9d0eb47b25 100644
|
||||||
|
--- a/src/libxl/libxl_conf.c
|
||||||
|
+++ b/src/libxl/libxl_conf.c
|
||||||
|
@@ -1069,6 +1069,7 @@ libxlMakeNetworkDiskSrcStr(virStorageSource *src,
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_SSH:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_VXHS:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_NFS:
|
||||||
|
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_LAST:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_NONE:
|
||||||
|
virReportError(VIR_ERR_NO_SUPPORT,
|
||||||
|
diff --git a/src/libxl/xen_xl.c b/src/libxl/xen_xl.c
|
||||||
|
index b2ff0edcf2..5d9f50c88c 100644
|
||||||
|
--- a/src/libxl/xen_xl.c
|
||||||
|
+++ b/src/libxl/xen_xl.c
|
||||||
|
@@ -1461,6 +1461,7 @@ xenFormatXLDiskSrcNet(virStorageSource *src)
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_SSH:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_VXHS:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_NFS:
|
||||||
|
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_LAST:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_NONE:
|
||||||
|
virReportError(VIR_ERR_NO_SUPPORT,
|
||||||
|
diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c
|
||||||
|
index a7062d3e96..4f2b6cc0e6 100644
|
||||||
|
--- a/src/qemu/qemu_block.c
|
||||||
|
+++ b/src/qemu/qemu_block.c
|
||||||
|
@@ -743,6 +743,38 @@ qemuBlockStorageSourceGetRBDProps(virStorageSource *src,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
+static virJSONValue *
|
||||||
|
+qemuBlockStorageSourceGetVitastorProps(virStorageSource *src)
|
||||||
|
+{
|
||||||
|
+ virJSONValue *ret = NULL;
|
||||||
|
+ virStorageNetHostDef *host;
|
||||||
|
+ size_t i;
|
||||||
|
+ g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
|
||||||
|
+ g_autofree char *etcd = NULL;
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < src->nhosts; i++) {
|
||||||
|
+ host = src->hosts + i;
|
||||||
|
+ if ((virStorageNetHostTransport)host->transport != VIR_STORAGE_NET_HOST_TRANS_TCP) {
|
||||||
|
+ return NULL;
|
||||||
|
+ }
|
||||||
|
+ virBufferAsprintf(&buf, i > 0 ? ",%s:%u" : "%s:%u", host->name, host->port);
|
||||||
|
+ }
|
||||||
|
+ if (src->nhosts > 0) {
|
||||||
|
+ etcd = virBufferContentAndReset(&buf);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (virJSONValueObjectAdd(&ret,
|
||||||
|
+ "S:etcd-host", etcd,
|
||||||
|
+ "S:etcd-prefix", src->query,
|
||||||
|
+ "S:config-path", src->configFile,
|
||||||
|
+ "s:image", src->path,
|
||||||
|
+ NULL) < 0)
|
||||||
|
+ return NULL;
|
||||||
|
+
|
||||||
|
+ return ret;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
static virJSONValue *
|
||||||
|
qemuBlockStorageSourceGetSshProps(virStorageSource *src)
|
||||||
|
{
|
||||||
|
@@ -1093,6 +1125,12 @@ qemuBlockStorageSourceGetBackendProps(virStorageSource *src,
|
||||||
|
return NULL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||||
|
+ driver = "vitastor";
|
||||||
|
+ if (!(fileprops = qemuBlockStorageSourceGetVitastorProps(src)))
|
||||||
|
+ return NULL;
|
||||||
|
+ break;
|
||||||
|
+
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_SSH:
|
||||||
|
driver = "ssh";
|
||||||
|
if (!(fileprops = qemuBlockStorageSourceGetSshProps(src)))
|
||||||
|
@@ -1996,6 +2034,7 @@ qemuBlockGetBackingStoreString(virStorageSource *src,
|
||||||
|
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
||||||
|
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_VXHS:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_NFS:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_SSH:
|
||||||
|
@@ -2376,6 +2415,12 @@ qemuBlockStorageSourceCreateGetStorageProps(virStorageSource *src,
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||||
|
+ driver = "vitastor";
|
||||||
|
+ if (!(location = qemuBlockStorageSourceGetVitastorProps(src)))
|
||||||
|
+ return -1;
|
||||||
|
+ break;
|
||||||
|
+
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_SSH:
|
||||||
|
if (srcPriv->nbdkitProcess) {
|
||||||
|
/* disk creation not yet supported with nbdkit, and even if it
|
||||||
|
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
|
||||||
|
index ac56fc7cb4..b80b311eea 100644
|
||||||
|
--- a/src/qemu/qemu_domain.c
|
||||||
|
+++ b/src/qemu/qemu_domain.c
|
||||||
|
@@ -4677,7 +4677,8 @@ qemuDomainValidateStorageSource(virStorageSource *src,
|
||||||
|
if (src->query &&
|
||||||
|
(actualType != VIR_STORAGE_TYPE_NETWORK ||
|
||||||
|
(src->protocol != VIR_STORAGE_NET_PROTOCOL_HTTPS &&
|
||||||
|
- src->protocol != VIR_STORAGE_NET_PROTOCOL_HTTP))) {
|
||||||
|
+ src->protocol != VIR_STORAGE_NET_PROTOCOL_HTTP &&
|
||||||
|
+ src->protocol != VIR_STORAGE_NET_PROTOCOL_VITASTOR))) {
|
||||||
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
||||||
|
_("query is supported only with HTTP(S) protocols"));
|
||||||
|
return -1;
|
||||||
|
@@ -4693,6 +4694,7 @@ qemuDomainValidateStorageSource(virStorageSource *src,
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_NBD:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
||||||
|
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_SSH:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
||||||
|
break;
|
||||||
|
@@ -9103,6 +9105,7 @@ qemuDomainPrepareStorageSourceTLS(virStorageSource *src,
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
||||||
|
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
|
||||||
|
diff --git a/src/qemu/qemu_nbdkit.c b/src/qemu/qemu_nbdkit.c
|
||||||
|
index c1bc6bc363..9d3433bf75 100644
|
||||||
|
--- a/src/qemu/qemu_nbdkit.c
|
||||||
|
+++ b/src/qemu/qemu_nbdkit.c
|
||||||
|
@@ -869,6 +869,7 @@ qemuNbdkitInitStorageSource(qemuNbdkitCaps *caps WITHOUT_NBDKIT_UNUSED,
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_NONE:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_NBD:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
||||||
|
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
|
||||||
|
@@ -1133,6 +1134,7 @@ qemuNbdkitProcessBuildCommand(qemuNbdkitProcess *proc)
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_NONE:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_NBD:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
||||||
|
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
|
||||||
|
diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c
|
||||||
|
index 302775af92..9d8fa0ded2 100644
|
||||||
|
--- a/src/qemu/qemu_snapshot.c
|
||||||
|
+++ b/src/qemu/qemu_snapshot.c
|
||||||
|
@@ -665,6 +665,7 @@ qemuSnapshotPrepareDiskExternalInactive(virDomainSnapshotDiskDef *snapdisk,
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_NONE:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_NBD:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
||||||
|
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
|
||||||
|
@@ -890,6 +891,7 @@ qemuSnapshotPrepareDiskInternal(virDomainDiskDef *disk,
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_NONE:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_NBD:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_RBD:
|
||||||
|
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
|
||||||
|
diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c
|
||||||
|
index e19e032427..59f91f4710 100644
|
||||||
|
--- a/src/storage/storage_driver.c
|
||||||
|
+++ b/src/storage/storage_driver.c
|
||||||
|
@@ -1626,6 +1626,7 @@ storageVolLookupByPathCallback(virStoragePoolObj *obj,
|
||||||
|
|
||||||
|
case VIR_STORAGE_POOL_GLUSTER:
|
||||||
|
case VIR_STORAGE_POOL_RBD:
|
||||||
|
+ case VIR_STORAGE_POOL_VITASTOR:
|
||||||
|
case VIR_STORAGE_POOL_SHEEPDOG:
|
||||||
|
case VIR_STORAGE_POOL_ZFS:
|
||||||
|
case VIR_STORAGE_POOL_LAST:
|
||||||
|
diff --git a/src/storage_file/storage_source_backingstore.c b/src/storage_file/storage_source_backingstore.c
|
||||||
|
index 821378883c..2211f6891b 100644
|
||||||
|
--- a/src/storage_file/storage_source_backingstore.c
|
||||||
|
+++ b/src/storage_file/storage_source_backingstore.c
|
||||||
|
@@ -264,6 +264,75 @@ virStorageSourceParseRBDColonString(const char *rbdstr,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
+static int
|
||||||
|
+virStorageSourceParseVitastorColonString(const char *colonstr,
|
||||||
|
+ virStorageSource *src)
|
||||||
|
+{
|
||||||
|
+ char *p, *e, *next;
|
||||||
|
+ g_autofree char *options = NULL;
|
||||||
|
+
|
||||||
|
+ /* optionally skip the "vitastor:" prefix if provided */
|
||||||
|
+ if (STRPREFIX(colonstr, "vitastor:"))
|
||||||
|
+ colonstr += strlen("vitastor:");
|
||||||
|
+
|
||||||
|
+ options = g_strdup(colonstr);
|
||||||
|
+
|
||||||
|
+ p = options;
|
||||||
|
+ while (*p) {
|
||||||
|
+ /* find : delimiter or end of string */
|
||||||
|
+ for (e = p; *e && *e != ':'; ++e) {
|
||||||
|
+ if (*e == '\\') {
|
||||||
|
+ e++;
|
||||||
|
+ if (*e == '\0')
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ if (*e == '\0') {
|
||||||
|
+ next = e; /* last kv pair */
|
||||||
|
+ } else {
|
||||||
|
+ next = e + 1;
|
||||||
|
+ *e = '\0';
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (STRPREFIX(p, "image=")) {
|
||||||
|
+ src->path = g_strdup(p + strlen("image="));
|
||||||
|
+ } else if (STRPREFIX(p, "etcd-prefix=")) {
|
||||||
|
+ src->query = g_strdup(p + strlen("etcd-prefix="));
|
||||||
|
+ } else if (STRPREFIX(p, "config-path=")) {
|
||||||
|
+ src->configFile = g_strdup(p + strlen("config-path="));
|
||||||
|
+ } else if (STRPREFIX(p, "etcd-host=")) {
|
||||||
|
+ char *h, *sep;
|
||||||
|
+
|
||||||
|
+ h = p + strlen("etcd-host=");
|
||||||
|
+ while (h < e) {
|
||||||
|
+ for (sep = h; sep < e; ++sep) {
|
||||||
|
+ if (*sep == '\\' && (sep[1] == ',' ||
|
||||||
|
+ sep[1] == ';' ||
|
||||||
|
+ sep[1] == ' ')) {
|
||||||
|
+ *sep = '\0';
|
||||||
|
+ sep += 2;
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (virStorageSourceRBDAddHost(src, h) < 0)
|
||||||
|
+ return -1;
|
||||||
|
+
|
||||||
|
+ h = sep;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ p = next;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (!src->path) {
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
static int
|
||||||
|
virStorageSourceParseNBDColonString(const char *nbdstr,
|
||||||
|
virStorageSource *src)
|
||||||
|
@@ -379,6 +448,11 @@ virStorageSourceParseBackingColon(virStorageSource *src,
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
|
||||||
|
+ if (virStorageSourceParseVitastorColonString(path, src) < 0)
|
||||||
|
+ return -1;
|
||||||
|
+ break;
|
||||||
|
+
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_LAST:
|
||||||
|
case VIR_STORAGE_NET_PROTOCOL_NONE:
|
||||||
|
@@ -953,6 +1027,54 @@ virStorageSourceParseBackingJSONRBD(virStorageSource *src,
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
+static int
|
||||||
|
+virStorageSourceParseBackingJSONVitastor(virStorageSource *src,
|
||||||
|
+ virJSONValue *json,
|
||||||
|
+ const char *jsonstr G_GNUC_UNUSED,
|
||||||
|
+ int opaque G_GNUC_UNUSED)
|
||||||
|
+{
|
||||||
|
+ const char *filename;
|
||||||
|
+ const char *image = virJSONValueObjectGetString(json, "image");
|
||||||
|
+ const char *conf = virJSONValueObjectGetString(json, "config-path");
|
||||||
|
+ const char *etcd_prefix = virJSONValueObjectGetString(json, "etcd-prefix");
|
||||||
|
+ virJSONValue *servers = virJSONValueObjectGetArray(json, "server");
|
||||||
|
+ size_t nservers;
|
||||||
|
+ size_t i;
|
||||||
|
+
|
||||||
|
+ src->type = VIR_STORAGE_TYPE_NETWORK;
|
||||||
|
+ src->protocol = VIR_STORAGE_NET_PROTOCOL_VITASTOR;
|
||||||
|
+
|
||||||
|
+ /* legacy syntax passed via 'filename' option */
|
||||||
|
+ if ((filename = virJSONValueObjectGetString(json, "filename")))
|
||||||
|
+ return virStorageSourceParseVitastorColonString(filename, src);
|
||||||
|
+
|
||||||
|
+ if (!image) {
|
||||||
|
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
|
||||||
|
+ _("missing image name in Vitastor backing volume "
|
||||||
|
+ "JSON specification"));
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ src->path = g_strdup(image);
|
||||||
|
+ src->configFile = g_strdup(conf);
|
||||||
|
+ src->query = g_strdup(etcd_prefix);
|
||||||
|
+
|
||||||
|
+ if (servers) {
|
||||||
|
+ nservers = virJSONValueArraySize(servers);
|
||||||
|
+
|
||||||
|
+ src->hosts = g_new0(virStorageNetHostDef, nservers);
|
||||||
|
+ src->nhosts = nservers;
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < nservers; i++) {
|
||||||
|
+ if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts + i,
|
||||||
|
+ virJSONValueArrayGet(servers, i)) < 0)
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static int
|
||||||
|
virStorageSourceParseBackingJSONRaw(virStorageSource *src,
|
||||||
|
virJSONValue *json,
|
||||||
|
@@ -1130,6 +1252,7 @@ static const struct virStorageSourceJSONDriverParser jsonParsers[] = {
|
||||||
|
{"sheepdog", false, virStorageSourceParseBackingJSONSheepdog, 0},
|
||||||
|
{"ssh", false, virStorageSourceParseBackingJSONSSH, 0},
|
||||||
|
{"rbd", false, virStorageSourceParseBackingJSONRBD, 0},
|
||||||
|
+ {"vitastor", false, virStorageSourceParseBackingJSONVitastor, 0},
|
||||||
|
{"raw", true, virStorageSourceParseBackingJSONRaw, 0},
|
||||||
|
{"nfs", false, virStorageSourceParseBackingJSONNFS, 0},
|
||||||
|
{"vxhs", false, virStorageSourceParseBackingJSONVxHS, 0},
|
||||||
|
diff --git a/src/test/test_driver.c b/src/test/test_driver.c
|
||||||
|
index 1165689de7..bba846351c 100644
|
||||||
|
--- a/src/test/test_driver.c
|
||||||
|
+++ b/src/test/test_driver.c
|
||||||
|
@@ -7345,6 +7345,7 @@ testStorageVolumeTypeForPool(int pooltype)
|
||||||
|
case VIR_STORAGE_POOL_ISCSI_DIRECT:
|
||||||
|
case VIR_STORAGE_POOL_GLUSTER:
|
||||||
|
case VIR_STORAGE_POOL_RBD:
|
||||||
|
+ case VIR_STORAGE_POOL_VITASTOR:
|
||||||
|
return VIR_STORAGE_VOL_NETWORK;
|
||||||
|
case VIR_STORAGE_POOL_LOGICAL:
|
||||||
|
case VIR_STORAGE_POOL_DISK:
|
||||||
|
diff --git a/tests/storagepoolcapsschemadata/poolcaps-fs.xml b/tests/storagepoolcapsschemadata/poolcaps-fs.xml
|
||||||
|
index eee75af746..8bd0a57bdd 100644
|
||||||
|
--- a/tests/storagepoolcapsschemadata/poolcaps-fs.xml
|
||||||
|
+++ b/tests/storagepoolcapsschemadata/poolcaps-fs.xml
|
||||||
|
@@ -204,4 +204,11 @@
|
||||||
|
</enum>
|
||||||
|
</volOptions>
|
||||||
|
</pool>
|
||||||
|
+ <pool type='vitastor' supported='no'>
|
||||||
|
+ <volOptions>
|
||||||
|
+ <defaultFormat type='raw'/>
|
||||||
|
+ <enum name='targetFormatType'>
|
||||||
|
+ </enum>
|
||||||
|
+ </volOptions>
|
||||||
|
+ </pool>
|
||||||
|
</storagepoolCapabilities>
|
||||||
|
diff --git a/tests/storagepoolcapsschemadata/poolcaps-full.xml b/tests/storagepoolcapsschemadata/poolcaps-full.xml
|
||||||
|
index 805950a937..852df0de16 100644
|
||||||
|
--- a/tests/storagepoolcapsschemadata/poolcaps-full.xml
|
||||||
|
+++ b/tests/storagepoolcapsschemadata/poolcaps-full.xml
|
||||||
|
@@ -204,4 +204,11 @@
|
||||||
|
</enum>
|
||||||
|
</volOptions>
|
||||||
|
</pool>
|
||||||
|
+ <pool type='vitastor' supported='yes'>
|
||||||
|
+ <volOptions>
|
||||||
|
+ <defaultFormat type='raw'/>
|
||||||
|
+ <enum name='targetFormatType'>
|
||||||
|
+ </enum>
|
||||||
|
+ </volOptions>
|
||||||
|
+ </pool>
|
||||||
|
</storagepoolCapabilities>
|
||||||
|
diff --git a/tests/storagepoolxml2argvtest.c b/tests/storagepoolxml2argvtest.c
|
||||||
|
index d5c2531ab8..b19308ac38 100644
|
||||||
|
--- a/tests/storagepoolxml2argvtest.c
|
||||||
|
+++ b/tests/storagepoolxml2argvtest.c
|
||||||
|
@@ -57,6 +57,7 @@ testCompareXMLToArgvFiles(bool shouldFail,
|
||||||
|
case VIR_STORAGE_POOL_GLUSTER:
|
||||||
|
case VIR_STORAGE_POOL_ZFS:
|
||||||
|
case VIR_STORAGE_POOL_VSTORAGE:
|
||||||
|
+ case VIR_STORAGE_POOL_VITASTOR:
|
||||||
|
case VIR_STORAGE_POOL_LAST:
|
||||||
|
default:
|
||||||
|
VIR_TEST_DEBUG("pool type '%s' has no xml2argv test", defTypeStr);
|
||||||
|
diff --git a/tools/virsh-pool.c b/tools/virsh-pool.c
|
||||||
|
index 2010ef1356..072e2ff9e8 100644
|
||||||
|
--- a/tools/virsh-pool.c
|
||||||
|
+++ b/tools/virsh-pool.c
|
||||||
|
@@ -1187,6 +1187,9 @@ cmdPoolList(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED)
|
||||||
|
case VIR_STORAGE_POOL_VSTORAGE:
|
||||||
|
flags |= VIR_CONNECT_LIST_STORAGE_POOLS_VSTORAGE;
|
||||||
|
break;
|
||||||
|
+ case VIR_STORAGE_POOL_VITASTOR:
|
||||||
|
+ flags |= VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR;
|
||||||
|
+ break;
|
||||||
|
case VIR_STORAGE_POOL_LAST:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.51.0
|
||||||
|
|
||||||
91
rpm/qemu-kvm-10.1-el10.spec.patch
Normal file
91
rpm/qemu-kvm-10.1-el10.spec.patch
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
--- qemu-kvm.spec.orig 2026-08-24 00:00:00.000000000 +0000
|
||||||
|
+++ qemu-kvm.spec 2026-09-05 16:48:22.226624885 +0000
|
||||||
|
@@ -102,8 +102,6 @@
|
||||||
|
|
||||||
|
|
||||||
|
%global target_list %{kvm_target}-softmmu
|
||||||
|
-%global block_drivers_rw_list qcow2,raw,file,host_device,nbd,iscsi,rbd,blkdebug,luks,null-co,nvme,copy-on-read,throttle,compress,virtio-blk-vhost-vdpa,virtio-blk-vfio-pci,virtio-blk-vhost-user,io_uring,nvme-io_uring
|
||||||
|
-%global block_drivers_ro_list vdi,vmdk,vhdx,vpc,https
|
||||||
|
%define qemudocdir %{_docdir}/%{name}
|
||||||
|
%global firmwaredirs "%{_datadir}/qemu-firmware:%{_datadir}/ipxe/qemu:%{_datadir}/seavgabios:%{_datadir}/seabios"
|
||||||
|
|
||||||
|
@@ -134,6 +132,7 @@ Requires: %{name}-block-blkio = %{epoch}
|
||||||
|
%if %{have_block_rbd} \
|
||||||
|
Requires: %{name}-block-rbd = %{epoch}:%{version}-%{release} \
|
||||||
|
%endif \
|
||||||
|
+Requires: %{name}-block-vitastor = %{epoch}:%{version}-%{release}\
|
||||||
|
Requires: %{name}-audio-pa = %{epoch}:%{version}-%{release}
|
||||||
|
|
||||||
|
# Since SPICE is removed from RHEL-9, the following Obsoletes:
|
||||||
|
@@ -158,7 +157,7 @@ Obsoletes: %{name}-block-ssh <= %{epoch}
|
||||||
|
Summary: QEMU is a machine emulator and virtualizer
|
||||||
|
Name: qemu-kvm
|
||||||
|
Version: 10.1.0
|
||||||
|
-Release: 16%{?rcrel}%{?dist}%{?cc_suffix}.5.alma.1
|
||||||
|
+Release: 16.vitastor%{?rcrel}%{?dist}%{?cc_suffix}.5.alma.1
|
||||||
|
# Epoch because we pushed a qemu-1.0 package. AIUI this can't ever be dropped
|
||||||
|
# Epoch 15 used for RHEL 8
|
||||||
|
# Epoch 17 used for RHEL 9 (due to release versioning offset in RHEL 8.5)
|
||||||
|
@@ -181,6 +180,7 @@ Source28: 95-kvm-memlock.conf
|
||||||
|
Source30: kvm-s390x.conf
|
||||||
|
Source31: kvm-x86.conf
|
||||||
|
Source36: README.tests
|
||||||
|
+Source37: qemu-vitastor.c
|
||||||
|
|
||||||
|
|
||||||
|
Patch0004: 0004-Initial-redhat-build.patch
|
||||||
|
@@ -471,6 +471,7 @@ Patch149: kvm-file-posix-populate-pwrite
|
||||||
|
Patch150: kvm-block-use-pwrite_zeroes_alignment-when-writing-first.patch
|
||||||
|
# For RHEL-207389 - qemu-img create/convert fails on target block device with 4k sector size [rhel-10.2.z]
|
||||||
|
Patch151: kvm-iotests-add-Linux-loop-device-image-creation-test.patch
|
||||||
|
+Patch152: qemu-10.1-vitastor.patch
|
||||||
|
|
||||||
|
# AlmaLinux Patch
|
||||||
|
Patch2001: 2001-Add-ppc64-support.patch
|
||||||
|
@@ -509,6 +510,7 @@ BuildRequires: libcurl-devel
|
||||||
|
%if %{have_block_rbd}
|
||||||
|
BuildRequires: librbd-devel
|
||||||
|
%endif
|
||||||
|
+BuildRequires: vitastor-client-devel
|
||||||
|
# We need both because the 'stap' binary is probed for by configure
|
||||||
|
BuildRequires: systemtap
|
||||||
|
BuildRequires: systemtap-sdt-devel
|
||||||
|
@@ -729,6 +731,13 @@ using the rbd protocol.
|
||||||
|
%endif
|
||||||
|
|
||||||
|
|
||||||
|
+%package block-vitastor
|
||||||
|
+Summary: QEMU Vitastor block driver
|
||||||
|
+Requires: %{name}-common%{?_isa} = %{epoch}:%{version}-%{release}
|
||||||
|
+%description block-vitastor
|
||||||
|
+This package provides the additional Vitastor block driver for QEMU.
|
||||||
|
+
|
||||||
|
+
|
||||||
|
%package audio-pa
|
||||||
|
Summary: QEMU PulseAudio audio driver
|
||||||
|
Requires: %{name}-common%{?_isa} = %{epoch}:%{version}-%{release}
|
||||||
|
@@ -831,6 +840,7 @@ This package provides the additional D-B
|
||||||
|
%prep
|
||||||
|
%setup -q -n qemu-%{version}%{?rcstr}
|
||||||
|
%autopatch -p1
|
||||||
|
+cp %{SOURCE37} ./block/vitastor.c
|
||||||
|
|
||||||
|
%global qemu_kvm_build qemu_kvm_build
|
||||||
|
mkdir -p %{qemu_kvm_build}
|
||||||
|
@@ -1067,6 +1077,7 @@ run_configure \
|
||||||
|
%if %{have_block_rbd}
|
||||||
|
--enable-rbd \
|
||||||
|
%endif
|
||||||
|
+ --enable-vitastor \
|
||||||
|
%if %{have_librdma}
|
||||||
|
--enable-rdma \
|
||||||
|
%endif
|
||||||
|
@@ -1564,6 +1575,8 @@ useradd -r -u 107 -g qemu -G kvm -d / -s
|
||||||
|
%files block-rbd
|
||||||
|
%{_libdir}/%{name}/block-rbd.so
|
||||||
|
%endif
|
||||||
|
+%files block-vitastor
|
||||||
|
+%{_libdir}/%{name}/block-vitastor.so
|
||||||
|
%files audio-pa
|
||||||
|
%{_libdir}/%{name}/audio-pa.so
|
||||||
|
|
||||||
@@ -11,7 +11,10 @@ WORKDIR /root
|
|||||||
RUN sed -i 's/enabled=0/enabled=1/' /etc/yum.repos.d/*.repo
|
RUN sed -i 's/enabled=0/enabled=1/' /etc/yum.repos.d/*.repo
|
||||||
RUN dnf -y install epel-release dnf-plugins-core
|
RUN dnf -y install epel-release dnf-plugins-core
|
||||||
RUN dnf -y install https://vitastor.io/rpms/centos/10/vitastor-release-1.0-1.el10.noarch.rpm
|
RUN dnf -y install https://vitastor.io/rpms/centos/10/vitastor-release-1.0-1.el10.noarch.rpm
|
||||||
RUN dnf -y install gcc-c++ gperftools-devel fio nodejs rpm-build jerasure-devel isa-l-devel gf-complete-devel rdma-core-devel cmake libnl3-devel c-ares-devel
|
RUN sed -i 's/metalink=/#metalink=/' /etc/yum.repos.d/epel.repo
|
||||||
|
RUN sed -i 's!#baseurl=.*epel/!baseurl=https://mirror.yandex.ru/epel/!' /etc/yum.repos.d/epel.repo
|
||||||
|
RUN dnf -y install gcc-c++ gperftools-devel fio nodejs rpm-build jerasure-devel isa-l-devel openssl-devel libisa-l_crypto-devel \
|
||||||
|
gf-complete-devel rdma-core-devel cmake libnl3-devel c-ares-devel
|
||||||
RUN dnf download --source fio
|
RUN dnf download --source fio
|
||||||
RUN rpm --nomd5 -i fio*.src.rpm
|
RUN rpm --nomd5 -i fio*.src.rpm
|
||||||
RUN cd ~/rpmbuild/SPECS && dnf builddep -y --spec fio.spec
|
RUN cd ~/rpmbuild/SPECS && dnf builddep -y --spec fio.spec
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
Name: vitastor
|
Name: vitastor
|
||||||
Version: 3.0.9
|
Version: 3.2.1
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Vitastor, a fast software-defined clustered block storage
|
Summary: Vitastor, a fast software-defined clustered block storage
|
||||||
|
|
||||||
License: Vitastor Network Public License 1.1
|
License: Vitastor Network Public License 1.1
|
||||||
URL: https://vitastor.io/
|
URL: https://vitastor.io/
|
||||||
Source0: vitastor-3.0.9.el10.tar.gz
|
Source0: vitastor-3.2.1.el10.tar.gz
|
||||||
|
|
||||||
BuildRequires: gperftools-devel
|
BuildRequires: gperftools-devel
|
||||||
BuildRequires: gcc-c++
|
BuildRequires: gcc-c++
|
||||||
BuildRequires: nodejs >= 10
|
BuildRequires: nodejs >= 10
|
||||||
BuildRequires: jerasure-devel
|
BuildRequires: jerasure-devel
|
||||||
BuildRequires: isa-l-devel
|
BuildRequires: isa-l-devel
|
||||||
|
BuildRequires: libisa-l_crypto-devel
|
||||||
BuildRequires: gf-complete-devel
|
BuildRequires: gf-complete-devel
|
||||||
BuildRequires: rdma-core-devel
|
BuildRequires: rdma-core-devel
|
||||||
BuildRequires: cmake
|
BuildRequires: cmake
|
||||||
@@ -161,6 +162,8 @@ chown vitastor:vitastor /var/lib/vitastor
|
|||||||
%_bindir/vitastor-nbd
|
%_bindir/vitastor-nbd
|
||||||
%_bindir/vitastor-ublk
|
%_bindir/vitastor-ublk
|
||||||
%_bindir/vitastor-nfs
|
%_bindir/vitastor-nfs
|
||||||
|
%_sbindir/mount.vitastorfs
|
||||||
|
%_sbindir/mount.vitastorblk
|
||||||
%_bindir/vitastor-cli
|
%_bindir/vitastor-cli
|
||||||
%_bindir/vitastor-rm
|
%_bindir/vitastor-rm
|
||||||
%_bindir/vitastor-kv
|
%_bindir/vitastor-kv
|
||||||
|
|||||||
@@ -13,9 +13,11 @@ RUN rm -f /etc/yum.repos.d/CentOS-Media.repo
|
|||||||
RUN sed -i 's/^mirrorlist=/#mirrorlist=/; s!#baseurl=http://mirror.centos.org/centos/\$releasever!baseurl=http://vault.centos.org/7.9.2009!' /etc/yum.repos.d/*.repo
|
RUN sed -i 's/^mirrorlist=/#mirrorlist=/; s!#baseurl=http://mirror.centos.org/centos/\$releasever!baseurl=http://vault.centos.org/7.9.2009!' /etc/yum.repos.d/*.repo
|
||||||
RUN yum -y --enablerepo=extras install centos-release-scl epel-release yum-utils rpm-build
|
RUN yum -y --enablerepo=extras install centos-release-scl epel-release yum-utils rpm-build
|
||||||
RUN perl -i -pe 's!mirrorlist=!#mirrorlist=!s; s!#\s*baseurl=http://mirror.centos.org!baseurl=http://vault.centos.org!' /etc/yum.repos.d/CentOS-SCLo-scl*.repo
|
RUN perl -i -pe 's!mirrorlist=!#mirrorlist=!s; s!#\s*baseurl=http://mirror.centos.org!baseurl=http://vault.centos.org!' /etc/yum.repos.d/CentOS-SCLo-scl*.repo
|
||||||
RUN yum -y install https://vitastor.io/rpms/centos/7/vitastor-release-1.0-1.el7.noarch.rpm
|
RUN yum -y install https://vitastor.io/rpms/centos/7/vitastor-release-1.0-2.el7.noarch.rpm
|
||||||
|
RUN sed -i 's/metalink=/#metalink=/' /etc/yum.repos.d/epel.repo
|
||||||
|
RUN sed -i 's!#baseurl=.*epel/!baseurl=https://archives.fedoraproject.org/pub/archive/epel/!' /etc/yum.repos.d/epel.repo
|
||||||
RUN yum -y install devtoolset-9-gcc-c++ devtoolset-9-libatomic-devel gcc make cmake gperftools-devel \
|
RUN yum -y install devtoolset-9-gcc-c++ devtoolset-9-libatomic-devel gcc make cmake gperftools-devel \
|
||||||
fio rh-nodejs12 jerasure-devel libisa-l-devel gf-complete-devel rdma-core-devel libnl3-devel c-ares-devel
|
fio rh-nodejs12 jerasure-devel libisa-l-devel openssl3-devel libisa-l_crypto-devel gf-complete-devel rdma-core-devel libnl3-devel c-ares-devel
|
||||||
RUN yumdownloader --disablerepo=centos-sclo-rh --source fio
|
RUN yumdownloader --disablerepo=centos-sclo-rh --source fio
|
||||||
RUN rpm --nomd5 -i fio*.src.rpm
|
RUN rpm --nomd5 -i fio*.src.rpm
|
||||||
RUN rm -f /etc/yum.repos.d/CentOS-Media.repo
|
RUN rm -f /etc/yum.repos.d/CentOS-Media.repo
|
||||||
|
|||||||
@@ -1,23 +1,25 @@
|
|||||||
Name: vitastor
|
Name: vitastor
|
||||||
Version: 3.0.9
|
Version: 3.2.1
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Vitastor, a fast software-defined clustered block storage
|
Summary: Vitastor, a fast software-defined clustered block storage
|
||||||
|
|
||||||
License: Vitastor Network Public License 1.1
|
License: Vitastor Network Public License 1.1
|
||||||
URL: https://vitastor.io/
|
URL: https://vitastor.io/
|
||||||
Source0: vitastor-3.0.9.el7.tar.gz
|
Source0: vitastor-3.2.1.el7.tar.gz
|
||||||
|
|
||||||
BuildRequires: gperftools-devel
|
BuildRequires: gperftools-devel
|
||||||
BuildRequires: devtoolset-9-gcc-c++
|
BuildRequires: devtoolset-9-gcc-c++
|
||||||
BuildRequires: rh-nodejs12
|
BuildRequires: rh-nodejs12
|
||||||
BuildRequires: rh-nodejs12-npm
|
BuildRequires: rh-nodejs12-npm
|
||||||
BuildRequires: jerasure-devel
|
BuildRequires: jerasure-devel
|
||||||
|
BuildRequires: openssl3-devel
|
||||||
BuildRequires: libisa-l-devel
|
BuildRequires: libisa-l-devel
|
||||||
|
BuildRequires: libisa-l_crypto-devel
|
||||||
BuildRequires: gf-complete-devel
|
BuildRequires: gf-complete-devel
|
||||||
BuildRequires: rdma-core-devel
|
BuildRequires: rdma-core-devel
|
||||||
BuildRequires: cmake3
|
BuildRequires: cmake3
|
||||||
BuildRequires: libnl3-devel
|
BuildRequires: libnl3-devel
|
||||||
BuildRequires: c-ares-devel
|
BuildRequires: c-ares-devel >= 1.16
|
||||||
Requires: vitastor-osd = %{version}-%{release}
|
Requires: vitastor-osd = %{version}-%{release}
|
||||||
Requires: vitastor-mon = %{version}-%{release}
|
Requires: vitastor-mon = %{version}-%{release}
|
||||||
Requires: vitastor-client = %{version}-%{release}
|
Requires: vitastor-client = %{version}-%{release}
|
||||||
@@ -105,7 +107,7 @@ Vitastor storage plugin for OpenNebula.
|
|||||||
|
|
||||||
%build
|
%build
|
||||||
. /opt/rh/devtoolset-9/enable
|
. /opt/rh/devtoolset-9/enable
|
||||||
%cmake3 .
|
%cmake3 . -DOPENSSL_CRYPTO_LIBRARY=%{_libdir}/openssl3/libcrypto.so -DOPENSSL_SSL_LIBRARY=%{_libdir}/openssl3/libssl.so -DOPENSSL_INCLUDE_DIR=%{_includedir}/openssl3
|
||||||
%make_build
|
%make_build
|
||||||
|
|
||||||
|
|
||||||
@@ -167,6 +169,8 @@ chown vitastor:vitastor /var/lib/vitastor
|
|||||||
%_bindir/vitastor-nbd
|
%_bindir/vitastor-nbd
|
||||||
%_bindir/vitastor-ublk
|
%_bindir/vitastor-ublk
|
||||||
%_bindir/vitastor-nfs
|
%_bindir/vitastor-nfs
|
||||||
|
%_sbindir/mount.vitastorfs
|
||||||
|
%_sbindir/mount.vitastorblk
|
||||||
%_bindir/vitastor-cli
|
%_bindir/vitastor-cli
|
||||||
%_bindir/vitastor-rm
|
%_bindir/vitastor-rm
|
||||||
%_bindir/vitastor-kv
|
%_bindir/vitastor-kv
|
||||||
|
|||||||
@@ -11,9 +11,11 @@ RUN rm -f /etc/yum.repos.d/CentOS-Media.repo
|
|||||||
RUN sed -i 's/^mirrorlist=/#mirrorlist=/; s!#baseurl=http://mirror.centos.org/!baseurl=http://vault.centos.org/!' /etc/yum.repos.d/*.repo
|
RUN sed -i 's/^mirrorlist=/#mirrorlist=/; s!#baseurl=http://mirror.centos.org/!baseurl=http://vault.centos.org/!' /etc/yum.repos.d/*.repo
|
||||||
RUN dnf -y install centos-release-advanced-virtualization epel-release dnf-plugins-core
|
RUN dnf -y install centos-release-advanced-virtualization epel-release dnf-plugins-core
|
||||||
RUN sed -i 's/^mirrorlist=/#mirrorlist=/; s!#baseurl=.*!baseurl=http://vault.centos.org/centos/8.4.2105/virt/$basearch/$avdir/!; s!^baseurl=.*Source/.*!baseurl=http://vault.centos.org/centos/8.4.2105/virt/Source/advanced-virtualization/!' /etc/yum.repos.d/CentOS-Advanced-Virtualization.repo
|
RUN sed -i 's/^mirrorlist=/#mirrorlist=/; s!#baseurl=.*!baseurl=http://vault.centos.org/centos/8.4.2105/virt/$basearch/$avdir/!; s!^baseurl=.*Source/.*!baseurl=http://vault.centos.org/centos/8.4.2105/virt/Source/advanced-virtualization/!' /etc/yum.repos.d/CentOS-Advanced-Virtualization.repo
|
||||||
RUN yum -y install https://vitastor.io/rpms/centos/8/vitastor-release-1.0-1.el8.noarch.rpm
|
RUN yum -y install https://vitastor.io/rpms/centos/8/vitastor-release-1.0-2.el8.noarch.rpm
|
||||||
RUN dnf -y install gcc-toolset-9 gcc-toolset-9-gcc-c++ gperftools-devel \
|
RUN sed -i 's/metalink=/#metalink=/' /etc/yum.repos.d/epel.repo
|
||||||
fio nodejs rpm-build jerasure-devel libisa-l-devel gf-complete-devel libibverbs-devel libarchive cmake libnl3-devel c-ares-devel
|
RUN sed -i 's!#baseurl=.*epel/!baseurl=https://mirror.yandex.ru/epel/!' /etc/yum.repos.d/epel.repo
|
||||||
|
RUN dnf -y install gcc-toolset-9 gcc-toolset-9-gcc-c++ gperftools-devel fio nodejs rpm-build jerasure-devel \
|
||||||
|
libisa-l-devel openssl3-devel libisa-l_crypto-devel gf-complete-devel libibverbs-devel libarchive cmake libnl3-devel c-ares-devel
|
||||||
RUN dnf download --source fio
|
RUN dnf download --source fio
|
||||||
RUN rpm --nomd5 -i fio*.src.rpm
|
RUN rpm --nomd5 -i fio*.src.rpm
|
||||||
RUN cd ~/rpmbuild/SPECS && dnf builddep -y --enablerepo=powertools --spec fio.spec
|
RUN cd ~/rpmbuild/SPECS && dnf builddep -y --enablerepo=powertools --spec fio.spec
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
Name: vitastor
|
Name: vitastor
|
||||||
Version: 3.0.9
|
Version: 3.2.1
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Vitastor, a fast software-defined clustered block storage
|
Summary: Vitastor, a fast software-defined clustered block storage
|
||||||
|
|
||||||
License: Vitastor Network Public License 1.1
|
License: Vitastor Network Public License 1.1
|
||||||
URL: https://vitastor.io/
|
URL: https://vitastor.io/
|
||||||
Source0: vitastor-3.0.9.el8.tar.gz
|
Source0: vitastor-3.2.1.el8.tar.gz
|
||||||
|
|
||||||
BuildRequires: gperftools-devel
|
BuildRequires: gperftools-devel
|
||||||
BuildRequires: gcc-toolset-9-gcc-c++
|
BuildRequires: gcc-toolset-9-gcc-c++
|
||||||
BuildRequires: nodejs >= 10
|
BuildRequires: nodejs >= 10
|
||||||
BuildRequires: jerasure-devel
|
BuildRequires: jerasure-devel
|
||||||
|
BuildRequires: openssl3-devel
|
||||||
BuildRequires: libisa-l-devel
|
BuildRequires: libisa-l-devel
|
||||||
|
BuildRequires: libisa-l_crypto-devel
|
||||||
BuildRequires: gf-complete-devel
|
BuildRequires: gf-complete-devel
|
||||||
BuildRequires: rdma-core-devel
|
BuildRequires: rdma-core-devel
|
||||||
BuildRequires: cmake
|
BuildRequires: cmake
|
||||||
BuildRequires: libnl3-devel
|
BuildRequires: libnl3-devel
|
||||||
BuildRequires: c-ares-devel
|
BuildRequires: c-ares-devel >= 1.16
|
||||||
Requires: vitastor-osd = %{version}-%{release}
|
Requires: vitastor-osd = %{version}-%{release}
|
||||||
Requires: vitastor-mon = %{version}-%{release}
|
Requires: vitastor-mon = %{version}-%{release}
|
||||||
Requires: vitastor-client = %{version}-%{release}
|
Requires: vitastor-client = %{version}-%{release}
|
||||||
@@ -103,7 +105,7 @@ Vitastor storage plugin for OpenNebula.
|
|||||||
|
|
||||||
%build
|
%build
|
||||||
. /opt/rh/gcc-toolset-9/enable
|
. /opt/rh/gcc-toolset-9/enable
|
||||||
%cmake .
|
%cmake . -DOPENSSL_CRYPTO_LIBRARY=%{_libdir}/openssl3/libcrypto.so -DOPENSSL_SSL_LIBRARY=%{_libdir}/openssl3/libssl.so -DOPENSSL_INCLUDE_DIR=%{_includedir}/openssl3
|
||||||
%make_build
|
%make_build
|
||||||
|
|
||||||
|
|
||||||
@@ -164,6 +166,8 @@ chown vitastor:vitastor /var/lib/vitastor
|
|||||||
%_bindir/vitastor-nbd
|
%_bindir/vitastor-nbd
|
||||||
%_bindir/vitastor-ublk
|
%_bindir/vitastor-ublk
|
||||||
%_bindir/vitastor-nfs
|
%_bindir/vitastor-nfs
|
||||||
|
%_sbindir/mount.vitastorfs
|
||||||
|
%_sbindir/mount.vitastorblk
|
||||||
%_bindir/vitastor-cli
|
%_bindir/vitastor-cli
|
||||||
%_bindir/vitastor-rm
|
%_bindir/vitastor-rm
|
||||||
%_bindir/vitastor-kv
|
%_bindir/vitastor-kv
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ WORKDIR /root
|
|||||||
RUN sed -i 's/enabled=0/enabled=1/' /etc/yum.repos.d/*.repo
|
RUN sed -i 's/enabled=0/enabled=1/' /etc/yum.repos.d/*.repo
|
||||||
RUN dnf -y install epel-release dnf-plugins-core
|
RUN dnf -y install epel-release dnf-plugins-core
|
||||||
RUN dnf -y install https://vitastor.io/rpms/centos/9/vitastor-release-1.0-1.el9.noarch.rpm
|
RUN dnf -y install https://vitastor.io/rpms/centos/9/vitastor-release-1.0-1.el9.noarch.rpm
|
||||||
RUN dnf -y install gcc-c++ gperftools-devel fio nodejs rpm-build jerasure-devel libisa-l-devel gf-complete-devel rdma-core-devel libarchive cmake libnl3-devel c-ares-devel
|
RUN sed -i 's/metalink=/#metalink=/' /etc/yum.repos.d/epel.repo
|
||||||
|
RUN sed -i 's!#baseurl=.*epel/!baseurl=https://mirror.yandex.ru/epel/!' /etc/yum.repos.d/epel.repo
|
||||||
|
RUN dnf -y install gcc-c++ gperftools-devel fio nodejs rpm-build jerasure-devel libisa-l-devel \
|
||||||
|
openssl-devel libisa-l_crypto-devel gf-complete-devel rdma-core-devel libarchive cmake libnl3-devel c-ares-devel
|
||||||
RUN dnf download --source fio
|
RUN dnf download --source fio
|
||||||
RUN rpm --nomd5 -i fio*.src.rpm
|
RUN rpm --nomd5 -i fio*.src.rpm
|
||||||
RUN cd ~/rpmbuild/SPECS && dnf builddep -y --spec fio.spec
|
RUN cd ~/rpmbuild/SPECS && dnf builddep -y --spec fio.spec
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
Name: vitastor
|
Name: vitastor
|
||||||
Version: 3.0.9
|
Version: 3.2.1
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Vitastor, a fast software-defined clustered block storage
|
Summary: Vitastor, a fast software-defined clustered block storage
|
||||||
|
|
||||||
License: Vitastor Network Public License 1.1
|
License: Vitastor Network Public License 1.1
|
||||||
URL: https://vitastor.io/
|
URL: https://vitastor.io/
|
||||||
Source0: vitastor-3.0.9.el9.tar.gz
|
Source0: vitastor-3.2.1.el9.tar.gz
|
||||||
|
|
||||||
BuildRequires: gperftools-devel
|
BuildRequires: gperftools-devel
|
||||||
BuildRequires: gcc-c++
|
BuildRequires: gcc-c++
|
||||||
BuildRequires: nodejs >= 10
|
BuildRequires: nodejs >= 10
|
||||||
BuildRequires: jerasure-devel
|
BuildRequires: jerasure-devel
|
||||||
|
BuildRequires: openssl-devel
|
||||||
BuildRequires: libisa-l-devel
|
BuildRequires: libisa-l-devel
|
||||||
|
BuildRequires: libisa-l_crypto-devel
|
||||||
BuildRequires: gf-complete-devel
|
BuildRequires: gf-complete-devel
|
||||||
BuildRequires: rdma-core-devel
|
BuildRequires: rdma-core-devel
|
||||||
BuildRequires: cmake
|
BuildRequires: cmake
|
||||||
BuildRequires: libnl3-devel
|
BuildRequires: libnl3-devel
|
||||||
BuildRequires: c-ares-devel
|
BuildRequires: c-ares-devel >= 1.16
|
||||||
Requires: vitastor-osd = %{version}-%{release}
|
Requires: vitastor-osd = %{version}-%{release}
|
||||||
Requires: vitastor-mon = %{version}-%{release}
|
Requires: vitastor-mon = %{version}-%{release}
|
||||||
Requires: vitastor-client = %{version}-%{release}
|
Requires: vitastor-client = %{version}-%{release}
|
||||||
@@ -161,6 +163,8 @@ chown vitastor:vitastor /var/lib/vitastor
|
|||||||
%_bindir/vitastor-nbd
|
%_bindir/vitastor-nbd
|
||||||
%_bindir/vitastor-ublk
|
%_bindir/vitastor-ublk
|
||||||
%_bindir/vitastor-nfs
|
%_bindir/vitastor-nfs
|
||||||
|
%_sbindir/mount.vitastorfs
|
||||||
|
%_sbindir/mount.vitastorblk
|
||||||
%_bindir/vitastor-cli
|
%_bindir/vitastor-cli
|
||||||
%_bindir/vitastor-rm
|
%_bindir/vitastor-rm
|
||||||
%_bindir/vitastor-kv
|
%_bindir/vitastor-kv
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ set(WITH_QEMU false CACHE BOOL "Build QEMU driver inside Vitastor source tree")
|
|||||||
set(WITH_FIO true CACHE BOOL "Build FIO driver")
|
set(WITH_FIO true CACHE BOOL "Build FIO driver")
|
||||||
set(QEMU_PLUGINDIR qemu CACHE STRING "QEMU plugin directory suffix (qemu-kvm on RHEL)")
|
set(QEMU_PLUGINDIR qemu CACHE STRING "QEMU plugin directory suffix (qemu-kvm on RHEL)")
|
||||||
set(WITH_ASAN false CACHE BOOL "Build with AddressSanitizer")
|
set(WITH_ASAN false CACHE BOOL "Build with AddressSanitizer")
|
||||||
|
set(WITH_UBSAN false CACHE BOOL "Build with UBSanitizer")
|
||||||
set(WITH_SYSTEM_LIBURING false CACHE BOOL "Use system liburing")
|
set(WITH_SYSTEM_LIBURING false CACHE BOOL "Use system liburing")
|
||||||
if("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/local/?$")
|
if("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/local/?$")
|
||||||
if(EXISTS "/etc/debian_version")
|
if(EXISTS "/etc/debian_version")
|
||||||
@@ -20,13 +21,17 @@ if("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/local/?$")
|
|||||||
endif()
|
endif()
|
||||||
set(ENABLE_COVERAGE false CACHE BOOL "Enable code coverage")
|
set(ENABLE_COVERAGE false CACHE BOOL "Enable code coverage")
|
||||||
|
|
||||||
add_definitions(-DVITASTOR_VERSION="3.0.9")
|
add_definitions(-DVITASTOR_VERSION="3.2.1")
|
||||||
add_definitions(-D_GNU_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -Wno-sign-compare -Wno-comment -Wno-parentheses -Wno-pointer-arith -fdiagnostics-color=always -fno-omit-frame-pointer -fvisibility=hidden -I ${CMAKE_SOURCE_DIR}/src)
|
add_definitions(-D_GNU_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -Wno-sign-compare -Wno-comment -Wno-parentheses -Wno-pointer-arith -fdiagnostics-color=always -fno-omit-frame-pointer -fvisibility=hidden -I ${CMAKE_SOURCE_DIR}/src)
|
||||||
add_link_options(-fno-omit-frame-pointer)
|
add_link_options(-fno-omit-frame-pointer)
|
||||||
if (${WITH_ASAN})
|
if (${WITH_ASAN})
|
||||||
add_definitions(-fsanitize=address)
|
add_definitions(-fsanitize=address)
|
||||||
add_link_options(-fsanitize=address -fno-omit-frame-pointer)
|
add_link_options(-fsanitize=address -fno-omit-frame-pointer)
|
||||||
endif (${WITH_ASAN})
|
endif (${WITH_ASAN})
|
||||||
|
if (${WITH_UBSAN})
|
||||||
|
add_definitions(-fsanitize=undefined -fno-sanitize=alignment)
|
||||||
|
add_link_options(-fsanitize=undefined -fno-sanitize=alignment)
|
||||||
|
endif (${WITH_UBSAN})
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fvisibility-inlines-hidden")
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fvisibility-inlines-hidden")
|
||||||
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -fvisibility-inlines-hidden")
|
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -fvisibility-inlines-hidden")
|
||||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -fvisibility-inlines-hidden")
|
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -fvisibility-inlines-hidden")
|
||||||
@@ -50,9 +55,13 @@ string(REGEX REPLACE "([\\/\\-]D) *NDEBUG" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_F
|
|||||||
string(REGEX REPLACE "([\\/\\-]D) *NDEBUG" "" CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}")
|
string(REGEX REPLACE "([\\/\\-]D) *NDEBUG" "" CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}")
|
||||||
string(REGEX REPLACE "([\\/\\-]D) *NDEBUG" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
|
string(REGEX REPLACE "([\\/\\-]D) *NDEBUG" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
|
||||||
|
|
||||||
macro(install_symlink filepath sympath)
|
macro(install_symlink _filepath _sympath)
|
||||||
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${filepath} \$ENV{DESTDIR}${sympath})")
|
get_filename_component(_symname ${_sympath} NAME)
|
||||||
install(CODE "message(\"-- Created symlink: ${sympath} -> ${filepath}\")")
|
get_filename_component(_installdir ${_sympath} PATH)
|
||||||
|
install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink
|
||||||
|
${_filepath} ${CMAKE_CURRENT_BINARY_DIR}/${_symname}.link)")
|
||||||
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${_symname}.link DESTINATION ${_installdir} RENAME ${_symname})
|
||||||
|
install(CODE "file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/${_symname}.link)")
|
||||||
endmacro(install_symlink)
|
endmacro(install_symlink)
|
||||||
|
|
||||||
check_include_file("linux/nbd-netlink.h" HAVE_NBD_NETLINK_H)
|
check_include_file("linux/nbd-netlink.h" HAVE_NBD_NETLINK_H)
|
||||||
@@ -69,17 +78,20 @@ pkg_check_modules(ISAL libisal)
|
|||||||
if (ISAL_LIBRARIES)
|
if (ISAL_LIBRARIES)
|
||||||
add_definitions(-DWITH_ISAL)
|
add_definitions(-DWITH_ISAL)
|
||||||
endif (ISAL_LIBRARIES)
|
endif (ISAL_LIBRARIES)
|
||||||
|
pkg_check_modules(ISAL_CRYPTO libisal_crypto)
|
||||||
|
if (ISAL_CRYPTO_LIBRARIES)
|
||||||
|
add_definitions(-DWITH_ISAL_CRYPTO)
|
||||||
|
endif (ISAL_CRYPTO_LIBRARIES)
|
||||||
pkg_check_modules(RDMACM librdmacm)
|
pkg_check_modules(RDMACM librdmacm)
|
||||||
if (RDMACM_LIBRARIES)
|
if (RDMACM_LIBRARIES)
|
||||||
add_definitions(-DWITH_RDMACM)
|
add_definitions(-DWITH_RDMACM)
|
||||||
endif (RDMACM_LIBRARIES)
|
endif (RDMACM_LIBRARIES)
|
||||||
|
|
||||||
find_package(OpenSSL REQUIRED)
|
find_package(OpenSSL 3.0 REQUIRED)
|
||||||
if (OPENSSL_FOUND)
|
add_definitions(-DWITH_OPENSSL)
|
||||||
add_definitions(-DWITH_OPENSSL)
|
include_directories(${OPENSSL_INCLUDE_DIR})
|
||||||
endif (OPENSSL_FOUND)
|
|
||||||
|
|
||||||
pkg_check_modules(CARES REQUIRED libcares)
|
pkg_check_modules(CARES REQUIRED libcares>=1.16)
|
||||||
include_directories(${CARES_INCLUDE_DIRS})
|
include_directories(${CARES_INCLUDE_DIRS})
|
||||||
|
|
||||||
if (${WITH_SYSTEM_LIBURING})
|
if (${WITH_SYSTEM_LIBURING})
|
||||||
@@ -114,6 +126,7 @@ add_subdirectory(kv)
|
|||||||
add_subdirectory(nfs)
|
add_subdirectory(nfs)
|
||||||
add_subdirectory(osd)
|
add_subdirectory(osd)
|
||||||
add_subdirectory(test)
|
add_subdirectory(test)
|
||||||
|
add_subdirectory(util)
|
||||||
|
|
||||||
### Install
|
### Install
|
||||||
|
|
||||||
@@ -121,6 +134,8 @@ install(TARGETS vitastor-osd vitastor-disk vitastor-nbd vitastor-ublk vitastor-n
|
|||||||
install_symlink(vitastor-disk ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/vitastor-dump-journal)
|
install_symlink(vitastor-disk ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/vitastor-dump-journal)
|
||||||
install_symlink(vitastor-cli ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/vitastor-rm)
|
install_symlink(vitastor-cli ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/vitastor-rm)
|
||||||
install_symlink(vitastor-cli ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/vita)
|
install_symlink(vitastor-cli ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/vita)
|
||||||
|
install_symlink(../${CMAKE_INSTALL_BINDIR}/vitastor-nfs ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_SBINDIR}/mount.vitastorfs)
|
||||||
|
install_symlink(../${CMAKE_INSTALL_BINDIR}/vitastor-nfs ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_SBINDIR}/mount.vitastorblk)
|
||||||
install(
|
install(
|
||||||
TARGETS vitastor_client vitastor_kv
|
TARGETS vitastor_client vitastor_kv
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ project(vitastor)
|
|||||||
|
|
||||||
# libvitastor_blk.a
|
# libvitastor_blk.a
|
||||||
add_library(vitastor_blk STATIC
|
add_library(vitastor_blk STATIC
|
||||||
../util/allocator.cpp ../util/crc32c.c ../util/ringloop.cpp
|
|
||||||
multilist.cpp blockstore_heap.cpp blockstore_disk.cpp
|
multilist.cpp blockstore_heap.cpp blockstore_disk.cpp
|
||||||
blockstore.cpp blockstore_impl.cpp blockstore_init.cpp blockstore_open.cpp
|
blockstore.cpp blockstore_impl.cpp blockstore_init.cpp blockstore_open.cpp
|
||||||
blockstore_flush.cpp blockstore_read.cpp blockstore_stable.cpp blockstore_sync.cpp blockstore_write.cpp
|
blockstore_flush.cpp blockstore_read.cpp blockstore_stable.cpp blockstore_sync.cpp blockstore_write.cpp
|
||||||
@@ -12,10 +11,8 @@ add_library(vitastor_blk STATIC
|
|||||||
)
|
)
|
||||||
target_compile_options(vitastor_blk PUBLIC -fPIC)
|
target_compile_options(vitastor_blk PUBLIC -fPIC)
|
||||||
target_link_libraries(vitastor_blk
|
target_link_libraries(vitastor_blk
|
||||||
${LIBURING_LIBRARIES}
|
util
|
||||||
${ISAL_LIBRARIES}
|
crc32c
|
||||||
# for timerfd_manager
|
|
||||||
vitastor_common
|
|
||||||
)
|
)
|
||||||
set_target_properties(vitastor_blk PROPERTIES VERSION ${VITASTOR_VERSION} SOVERSION 0)
|
set_target_properties(vitastor_blk PROPERTIES VERSION ${VITASTOR_VERSION} SOVERSION 0)
|
||||||
|
|
||||||
@@ -23,9 +20,9 @@ if (${WITH_FIO})
|
|||||||
# libfio_vitastor_blk.so
|
# libfio_vitastor_blk.so
|
||||||
add_library(fio_vitastor_blk SHARED
|
add_library(fio_vitastor_blk SHARED
|
||||||
fio_engine.cpp
|
fio_engine.cpp
|
||||||
../../json11/json11.cpp
|
|
||||||
)
|
)
|
||||||
target_link_libraries(fio_vitastor_blk
|
target_link_libraries(fio_vitastor_blk
|
||||||
vitastor_blk
|
vitastor_blk
|
||||||
|
util
|
||||||
)
|
)
|
||||||
endif (${WITH_FIO})
|
endif (${WITH_FIO})
|
||||||
|
|||||||
@@ -77,8 +77,11 @@ Output:
|
|||||||
- retval = number of bytes actually read/written or negative error number
|
- retval = number of bytes actually read/written or negative error number
|
||||||
-EINVAL = invalid input parameters
|
-EINVAL = invalid input parameters
|
||||||
-ENOENT = requested object/version does not exist for reads
|
-ENOENT = requested object/version does not exist for reads
|
||||||
|
-EEXIST = requested version to write already exists
|
||||||
-ENOSPC = no space left in the store for writes
|
-ENOSPC = no space left in the store for writes
|
||||||
-EDOM = checksum error.
|
-EAGAIN = temporary failure, operation should be retried (notably, writes may be retried
|
||||||
|
after stabilizing some journaled entries and freeing up journal space)
|
||||||
|
-EDOM = checksum error
|
||||||
- version = the version actually read or written
|
- version = the version actually read or written
|
||||||
|
|
||||||
## BS_OP_DELETE
|
## BS_OP_DELETE
|
||||||
@@ -90,7 +93,7 @@ Input:
|
|||||||
- version = requested version. Treated the same as with BS_OP_WRITE
|
- version = requested version. Treated the same as with BS_OP_WRITE
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
- retval = 0 or negative error number (-EINVAL)
|
- retval = 0 or negative error number (-EINVAL, -EAGAIN)
|
||||||
- version = the version actually written (delete is initially written as an object version)
|
- version = the version actually written (delete is initially written as an object version)
|
||||||
|
|
||||||
## BS_OP_SYNC
|
## BS_OP_SYNC
|
||||||
@@ -99,7 +102,7 @@ Make sure all previously issued modifications reach physical media.
|
|||||||
|
|
||||||
Input: Nothing except opcode
|
Input: Nothing except opcode
|
||||||
Output:
|
Output:
|
||||||
- retval = 0 or negative error number (-EINVAL)
|
- retval = 0 or negative error number (-EINVAL, -EAGAIN)
|
||||||
|
|
||||||
## BS_OP_STABLE / BS_OP_ROLLBACK
|
## BS_OP_STABLE / BS_OP_ROLLBACK
|
||||||
|
|
||||||
@@ -129,7 +132,9 @@ Input:
|
|||||||
- max_oid = max inode/stripe or 0 to list all objects
|
- max_oid = max inode/stripe or 0 to list all objects
|
||||||
|
|
||||||
Output:
|
Output:
|
||||||
- retval = total obj_ver_id count
|
- retval = total obj_ver_id count or negative error number
|
||||||
|
-ENOMEM = could not allocate memory for the listing
|
||||||
|
-EAGAIN = pool sharding settings are incorrect, please reshard and retry
|
||||||
- version = stable obj_ver_id count
|
- version = stable obj_ver_id count
|
||||||
- buf = obj_ver_id array allocated by the blockstore. Stable versions come first.
|
- buf = obj_ver_id array allocated by the blockstore. Stable versions come first.
|
||||||
You must free it yourself after usage with free().
|
You must free it yourself after usage with free().
|
||||||
@@ -166,7 +171,7 @@ struct __attribute__ ((visibility("default"))) blockstore_op_t
|
|||||||
};
|
};
|
||||||
uint8_t *buf = NULL;
|
uint8_t *buf = NULL;
|
||||||
uint8_t *bitmap = NULL;
|
uint8_t *bitmap = NULL;
|
||||||
int retval = 0;
|
int64_t retval = 0;
|
||||||
|
|
||||||
uint8_t private_data[BS_OP_PRIVATE_DATA_SIZE];
|
uint8_t private_data[BS_OP_PRIVATE_DATA_SIZE];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,23 +12,9 @@
|
|||||||
#include "blockstore_disk.h"
|
#include "blockstore_disk.h"
|
||||||
#include "blockstore_heap.h"
|
#include "blockstore_heap.h"
|
||||||
#include "str_util.h"
|
#include "str_util.h"
|
||||||
|
#include "disk_util.h"
|
||||||
#include "allocator.h"
|
#include "allocator.h"
|
||||||
|
|
||||||
static uint32_t is_power_of_two(uint64_t value)
|
|
||||||
{
|
|
||||||
uint32_t l = 0;
|
|
||||||
while (value > 1)
|
|
||||||
{
|
|
||||||
if (value & 1)
|
|
||||||
{
|
|
||||||
return 64;
|
|
||||||
}
|
|
||||||
value = value >> 1;
|
|
||||||
l++;
|
|
||||||
}
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
|
|
||||||
void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config)
|
void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config)
|
||||||
{
|
{
|
||||||
// Parse
|
// Parse
|
||||||
@@ -100,6 +86,7 @@ void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config
|
|||||||
(config["discard_on_start"] == "true" || config["discard_on_start"] == "1" || config["discard_on_start"] == "yes");
|
(config["discard_on_start"] == "true" || config["discard_on_start"] == "1" || config["discard_on_start"] == "yes");
|
||||||
gc_on_start = config.find("gc_on_start") == config.end() ||
|
gc_on_start = config.find("gc_on_start") == config.end() ||
|
||||||
(config["gc_on_start"] == "true" || config["gc_on_start"] == "1" || config["gc_on_start"] == "yes");
|
(config["gc_on_start"] == "true" || config["gc_on_start"] == "1" || config["gc_on_start"] == "yes");
|
||||||
|
skip_double_claim = (config["skip_double_claim"] == "true" || config["skip_double_claim"] == "1" || config["skip_double_claim"] == "yes");
|
||||||
min_discard_size = parse_size(config["min_discard_size"]);
|
min_discard_size = parse_size(config["min_discard_size"]);
|
||||||
if (!min_discard_size)
|
if (!min_discard_size)
|
||||||
min_discard_size = 1024*1024;
|
min_discard_size = 1024*1024;
|
||||||
@@ -125,7 +112,7 @@ void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config
|
|||||||
{
|
{
|
||||||
data_block_size = (1 << DEFAULT_DATA_BLOCK_ORDER);
|
data_block_size = (1 << DEFAULT_DATA_BLOCK_ORDER);
|
||||||
}
|
}
|
||||||
if (is_power_of_two(data_block_size) >= 64 || data_block_size < MIN_DATA_BLOCK_SIZE || data_block_size >= MAX_DATA_BLOCK_SIZE)
|
if ((data_block_size & (data_block_size-1)) || data_block_size < MIN_DATA_BLOCK_SIZE || data_block_size >= MAX_DATA_BLOCK_SIZE)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Bad block size");
|
throw std::runtime_error("Bad block size");
|
||||||
}
|
}
|
||||||
@@ -223,6 +210,32 @@ void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A metadata entry must fit into one metadata block; a large block_size with a small
|
||||||
|
// csum_block_size otherwise inflates the entry past it and makes the callers divide by zero.
|
||||||
|
static uint64_t entries_per_meta_block(uint64_t entry_size, uint64_t meta_block_size,
|
||||||
|
uint64_t data_block_size, uint32_t bitmap_granularity, uint32_t csum_block_size)
|
||||||
|
{
|
||||||
|
if (entry_size && entry_size <= meta_block_size)
|
||||||
|
{
|
||||||
|
return meta_block_size / entry_size;
|
||||||
|
}
|
||||||
|
std::string msg = "Metadata entry size ("+std::to_string(entry_size)+" bytes) exceeds meta_block_size ("+
|
||||||
|
std::to_string(meta_block_size)+" bytes) with block_size="+std::to_string(data_block_size)+
|
||||||
|
", bitmap_granularity="+std::to_string(bitmap_granularity);
|
||||||
|
if (csum_block_size)
|
||||||
|
{
|
||||||
|
msg += ", csum_block_size="+std::to_string(csum_block_size)+" ("+
|
||||||
|
std::to_string(data_block_size/csum_block_size)+" checksums per block)";
|
||||||
|
}
|
||||||
|
msg += ". Decrease block_size";
|
||||||
|
if (csum_block_size)
|
||||||
|
{
|
||||||
|
msg += ", increase csum_block_size";
|
||||||
|
}
|
||||||
|
msg += " or increase meta_block_size";
|
||||||
|
throw std::runtime_error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
void blockstore_disk_t::calc_lengths(bool skip_meta_check)
|
void blockstore_disk_t::calc_lengths(bool skip_meta_check)
|
||||||
{
|
{
|
||||||
// data
|
// data
|
||||||
@@ -272,17 +285,20 @@ void blockstore_disk_t::calc_lengths(bool skip_meta_check)
|
|||||||
clean_entry_bitmap_size = (data_block_size / bitmap_granularity + 7) / 8;
|
clean_entry_bitmap_size = (data_block_size / bitmap_granularity + 7) / 8;
|
||||||
clean_dyn_size = clean_entry_bitmap_size*2 + (csum_block_size
|
clean_dyn_size = clean_entry_bitmap_size*2 + (csum_block_size
|
||||||
? data_block_size/csum_block_size*(data_csum_type & 0xFF) : 0);
|
? data_block_size/csum_block_size*(data_csum_type & 0xFF) : 0);
|
||||||
|
uint64_t entries_per_block = 0;
|
||||||
recalc:
|
recalc:
|
||||||
if (meta_format == BLOCKSTORE_META_FORMAT_HEAP)
|
if (meta_format == BLOCKSTORE_META_FORMAT_HEAP)
|
||||||
{
|
{
|
||||||
uint32_t entries_per_block = meta_block_size / (sizeof(heap_big_write_t) + clean_dyn_size);
|
entries_per_block = entries_per_meta_block(sizeof(heap_big_write_t) + clean_dyn_size,
|
||||||
|
meta_block_size, data_block_size, bitmap_granularity, csum_block_size);
|
||||||
min_meta_len = (block_count+entries_per_block-1) / entries_per_block * meta_block_size;
|
min_meta_len = (block_count+entries_per_block-1) / entries_per_block * meta_block_size;
|
||||||
}
|
}
|
||||||
else if (meta_format == BLOCKSTORE_META_FORMAT_V1)
|
else if (meta_format == BLOCKSTORE_META_FORMAT_V1)
|
||||||
{
|
{
|
||||||
clean_entry_size = 24 /*sizeof(clean_disk_entry)*/ + 2*clean_entry_bitmap_size;
|
clean_entry_size = 24 /*sizeof(clean_disk_entry)*/ + 2*clean_entry_bitmap_size;
|
||||||
min_meta_len = (1 + (block_count - 1 + meta_block_size / clean_entry_size)
|
entries_per_block = entries_per_meta_block(clean_entry_size, meta_block_size,
|
||||||
/ (meta_block_size / clean_entry_size)) * meta_block_size;
|
data_block_size, bitmap_granularity, 0);
|
||||||
|
min_meta_len = (1 + (block_count - 1 + entries_per_block) / entries_per_block) * meta_block_size;
|
||||||
if (!skip_meta_check && meta_area_size < min_meta_len)
|
if (!skip_meta_check && meta_area_size < min_meta_len)
|
||||||
{
|
{
|
||||||
too_small:
|
too_small:
|
||||||
@@ -294,7 +310,9 @@ too_small:
|
|||||||
{
|
{
|
||||||
meta_format = BLOCKSTORE_META_FORMAT_V2;
|
meta_format = BLOCKSTORE_META_FORMAT_V2;
|
||||||
clean_entry_size = 24 /*sizeof(clean_disk_entry)*/ + clean_dyn_size + 4 /*entry_csum*/;
|
clean_entry_size = 24 /*sizeof(clean_disk_entry)*/ + clean_dyn_size + 4 /*entry_csum*/;
|
||||||
min_meta_len = (1 + (block_count - 1 + meta_block_size / clean_entry_size) / (meta_block_size / clean_entry_size)) * meta_block_size;
|
entries_per_block = entries_per_meta_block(clean_entry_size, meta_block_size,
|
||||||
|
data_block_size, bitmap_granularity, csum_block_size);
|
||||||
|
min_meta_len = (1 + (block_count - 1 + entries_per_block) / entries_per_block) * meta_block_size;
|
||||||
if (!skip_meta_check && meta_area_size < min_meta_len)
|
if (!skip_meta_check && meta_area_size < min_meta_len)
|
||||||
{
|
{
|
||||||
if (!data_csum_type)
|
if (!data_csum_type)
|
||||||
@@ -337,41 +355,6 @@ void blockstore_disk_t::check_lengths()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Move to utils
|
|
||||||
static void check_size(int fd, uint64_t *size, uint64_t *sectsize, std::string name)
|
|
||||||
{
|
|
||||||
int sect;
|
|
||||||
struct stat st;
|
|
||||||
if (fstat(fd, &st) < 0)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Failed to stat "+name);
|
|
||||||
}
|
|
||||||
if (S_ISREG(st.st_mode))
|
|
||||||
{
|
|
||||||
*size = st.st_size;
|
|
||||||
if (sectsize)
|
|
||||||
{
|
|
||||||
*sectsize = st.st_blksize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (S_ISBLK(st.st_mode))
|
|
||||||
{
|
|
||||||
if (ioctl(fd, BLKGETSIZE64, size) < 0 ||
|
|
||||||
ioctl(fd, BLKSSZGET, §) < 0)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Failed to get "+name+" size or block size: "+strerror(errno));
|
|
||||||
}
|
|
||||||
if (sectsize)
|
|
||||||
{
|
|
||||||
*sectsize = sect;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw std::runtime_error(name+" is neither a file nor a block device");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bs_openmode(const std::string & mode)
|
static int bs_openmode(const std::string & mode)
|
||||||
{
|
{
|
||||||
if (mode == "directsync")
|
if (mode == "directsync")
|
||||||
@@ -490,7 +473,7 @@ void blockstore_disk_t::open_journal()
|
|||||||
journal_fd = meta_fd;
|
journal_fd = meta_fd;
|
||||||
journal_device_sect = meta_device_sect;
|
journal_device_sect = meta_device_sect;
|
||||||
journal_device_size = 0;
|
journal_device_size = 0;
|
||||||
if (journal_offset >= data_device_size)
|
if (journal_offset >= (meta_device_size > 0 ? meta_device_size : data_device_size))
|
||||||
{
|
{
|
||||||
throw std::runtime_error("journal_offset exceeds device size");
|
throw std::runtime_error("journal_offset exceeds device size");
|
||||||
}
|
}
|
||||||
@@ -520,7 +503,7 @@ void blockstore_disk_t::close_all()
|
|||||||
|
|
||||||
// Sadly DISCARD only works through ioctl(), but it seems to always block the device queue,
|
// Sadly DISCARD only works through ioctl(), but it seems to always block the device queue,
|
||||||
// so it's not a big deal that we can only run it synchronously.
|
// so it's not a big deal that we can only run it synchronously.
|
||||||
int blockstore_disk_t::trim_data(std::function<bool(uint64_t)> is_free)
|
int blockstore_disk_t::trim_data(std::function<bool(uint64_t)> is_used)
|
||||||
{
|
{
|
||||||
if (mock_mode)
|
if (mock_mode)
|
||||||
{
|
{
|
||||||
@@ -531,7 +514,7 @@ int blockstore_disk_t::trim_data(std::function<bool(uint64_t)> is_free)
|
|||||||
uint64_t discarded = 0;
|
uint64_t discarded = 0;
|
||||||
for (; i <= block_count; i++)
|
for (; i <= block_count; i++)
|
||||||
{
|
{
|
||||||
if (i >= block_count || is_free(i))
|
if (i >= block_count || is_used(i))
|
||||||
{
|
{
|
||||||
if (i > j && (i-j)*data_block_size >= min_discard_size)
|
if (i > j && (i-j)*data_block_size >= min_discard_size)
|
||||||
{
|
{
|
||||||
@@ -544,17 +527,21 @@ int blockstore_disk_t::trim_data(std::function<bool(uint64_t)> is_free)
|
|||||||
if (range[0] % discard_granularity)
|
if (range[0] % discard_granularity)
|
||||||
range[0] = range[0] + discard_granularity - (range[0] % discard_granularity);
|
range[0] = range[0] + discard_granularity - (range[0] % discard_granularity);
|
||||||
if (range[0] >= range[1])
|
if (range[0] >= range[1])
|
||||||
continue;
|
range[1] = 0;
|
||||||
range[1] -= range[0];
|
else
|
||||||
|
range[1] -= range[0];
|
||||||
}
|
}
|
||||||
r = ioctl(data_fd, BLKDISCARD, &range);
|
if (range[1] > 0)
|
||||||
if (r != 0)
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Failed to execute BLKDISCARD %ju+%ju on %s: %s (code %d)\n",
|
r = ioctl(data_fd, BLKDISCARD, &range);
|
||||||
range[0], range[1], data_device.c_str(), strerror(-r), r);
|
if (r != 0)
|
||||||
return -errno;
|
{
|
||||||
|
fprintf(stderr, "Failed to execute BLKDISCARD %ju+%ju on %s: %s (code %d)\n",
|
||||||
|
range[0], range[1], data_device.c_str(), strerror(-r), r);
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
discarded += range[1];
|
||||||
}
|
}
|
||||||
discarded += range[1];
|
|
||||||
}
|
}
|
||||||
j = i+1;
|
j = i+1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ struct blockstore_disk_t
|
|||||||
bool discard_on_start = false;
|
bool discard_on_start = false;
|
||||||
// GC on start (new store)
|
// GC on start (new store)
|
||||||
bool gc_on_start = true;
|
bool gc_on_start = true;
|
||||||
|
// Skip double claim conflicts on start (new store, temporary until the bug is found)
|
||||||
|
bool skip_double_claim = false;
|
||||||
uint64_t min_discard_size = 1024*1024;
|
uint64_t min_discard_size = 1024*1024;
|
||||||
uint64_t discard_granularity = 0;
|
uint64_t discard_granularity = 0;
|
||||||
|
|
||||||
@@ -82,7 +84,7 @@ struct blockstore_disk_t
|
|||||||
void calc_lengths(bool skip_meta_check = false);
|
void calc_lengths(bool skip_meta_check = false);
|
||||||
void check_lengths();
|
void check_lengths();
|
||||||
void close_all();
|
void close_all();
|
||||||
int trim_data(std::function<bool(uint64_t)> is_free);
|
int trim_data(std::function<bool(uint64_t)> is_used);
|
||||||
|
|
||||||
inline uint64_t dirty_dyn_size(uint64_t offset, uint64_t len)
|
inline uint64_t dirty_dyn_size(uint64_t offset, uint64_t len)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -80,6 +80,16 @@ int journal_flusher_t::get_syncing_buffer()
|
|||||||
return syncing_buffer;
|
return syncing_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t journal_flusher_t::get_flushing_lsn(object_id oid)
|
||||||
|
{
|
||||||
|
auto it = flushing.find(oid);
|
||||||
|
if (it == flushing.end())
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
bool journal_flusher_t::is_active()
|
bool journal_flusher_t::is_active()
|
||||||
{
|
{
|
||||||
return active_flushers > 0;
|
return active_flushers > 0;
|
||||||
@@ -96,6 +106,20 @@ void journal_flusher_t::release_trim()
|
|||||||
force_start--;
|
force_start--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ask for an fsync. Used when a new entry depends on another not-yet-durable entry
|
||||||
|
void journal_flusher_t::request_fsync()
|
||||||
|
{
|
||||||
|
force_start++;
|
||||||
|
force_fsync++;
|
||||||
|
bs->ringloop->wakeup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void journal_flusher_t::release_fsync()
|
||||||
|
{
|
||||||
|
force_fsync--;
|
||||||
|
force_start--;
|
||||||
|
}
|
||||||
|
|
||||||
void journal_flusher_t::dump_diagnostics()
|
void journal_flusher_t::dump_diagnostics()
|
||||||
{
|
{
|
||||||
printf(
|
printf(
|
||||||
@@ -172,30 +196,47 @@ bool journal_flusher_co::loop()
|
|||||||
else if (wait_state == 17) goto resume_17;
|
else if (wait_state == 17) goto resume_17;
|
||||||
else if (wait_state == 18) goto resume_18;
|
else if (wait_state == 18) goto resume_18;
|
||||||
else if (wait_state == 19) goto resume_19;
|
else if (wait_state == 19) goto resume_19;
|
||||||
else if (wait_state == 20) goto resume_20;
|
else if (wait_state == 22) goto resume_22;
|
||||||
else if (wait_state == 21) goto resume_21;
|
else if (wait_state == 23) goto resume_23;
|
||||||
|
else if (wait_state == 24) goto resume_24;
|
||||||
|
else if (wait_state == 25) goto resume_25;
|
||||||
|
else if (wait_state == 26) goto resume_26;
|
||||||
|
else if (wait_state == 27) goto resume_27;
|
||||||
|
else if (wait_state == 28) goto resume_28;
|
||||||
|
else if (wait_state == 29) goto resume_29;
|
||||||
|
else if (wait_state == 30) goto resume_30;
|
||||||
|
else if (wait_state == 31) goto resume_31;
|
||||||
resume_0:
|
resume_0:
|
||||||
wait_state = 0;
|
wait_state = 0;
|
||||||
wait_count = 0;
|
wait_count = 0;
|
||||||
cur_oid = {};
|
cur_oid = {};
|
||||||
res = bs->heap->get_next_compact(cur_oid);
|
res = bs->heap->get_next_compact(cur_oid);
|
||||||
// Advance fsynced_lsn every <journal_trim_interval> intent writes
|
if ((bs->intent_write_counter >= bs->journal_trim_interval || flusher->force_fsync > 0) && co_id == 0)
|
||||||
if ((bs->intent_write_counter >= bs->journal_trim_interval) && co_id == 0)
|
|
||||||
{
|
{
|
||||||
bs->intent_write_counter = 0;
|
|
||||||
resume_17:
|
resume_17:
|
||||||
resume_18:
|
resume_18:
|
||||||
resume_19:
|
resume_19:
|
||||||
if (!fsync_buffer(17))
|
if (!fsync_buffer(17))
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
resume_20:
|
}
|
||||||
resume_21:
|
if (bs->intent_write_counter >= bs->journal_trim_interval)
|
||||||
if (!trim_lsn(20))
|
{
|
||||||
return false;
|
// Advance fsynced_lsn every <journal_trim_interval> intent writes
|
||||||
|
bs->intent_write_counter = 0;
|
||||||
|
resume_29:
|
||||||
|
resume_30:
|
||||||
|
resume_31:
|
||||||
|
if (!trim_lsn(29))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (res == ENOENT && flusher->force_start > 0 && co_id == 0 &&
|
if (res == ENOENT && flusher->force_start > 0 && co_id == 0 &&
|
||||||
(!bs->dsk.disable_journal_fsync || !bs->dsk.disable_meta_fsync || !bs->dsk.disable_data_fsync))
|
(!bs->dsk.disable_journal_fsync || !bs->dsk.disable_meta_fsync || !bs->dsk.disable_data_fsync))
|
||||||
{
|
{
|
||||||
|
// When under pressure, do an additional fsync to force entries to be marked compactable
|
||||||
flusher->active_flushers++;
|
flusher->active_flushers++;
|
||||||
resume_14:
|
resume_14:
|
||||||
resume_15:
|
resume_15:
|
||||||
@@ -226,7 +267,6 @@ resume_16:
|
|||||||
}
|
}
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
flusher->flushing.insert(cur_oid);
|
|
||||||
resume_1:
|
resume_1:
|
||||||
wait_state = 1;
|
wait_state = 1;
|
||||||
should_repeat = false;
|
should_repeat = false;
|
||||||
@@ -234,7 +274,6 @@ resume_1:
|
|||||||
if (!cur_obj)
|
if (!cur_obj)
|
||||||
{
|
{
|
||||||
// Object does not exist
|
// Object does not exist
|
||||||
flusher->flushing.erase(cur_oid);
|
|
||||||
goto resume_0;
|
goto resume_0;
|
||||||
}
|
}
|
||||||
// Scan versions to flush
|
// Scan versions to flush
|
||||||
@@ -259,44 +298,83 @@ resume_1:
|
|||||||
if (wr->type() == BS_HEAP_SMALL_WRITE ||
|
if (wr->type() == BS_HEAP_SMALL_WRITE ||
|
||||||
wr->type() == BS_HEAP_INTENT_WRITE && bs->dsk.csum_block_size > bs->dsk.bitmap_granularity)
|
wr->type() == BS_HEAP_INTENT_WRITE && bs->dsk.csum_block_size > bs->dsk.bitmap_granularity)
|
||||||
{
|
{
|
||||||
auto res = bs->prepare_read(read_vec, cur_obj, wr, 0, bs->dsk.data_block_size,
|
bs->prepare_read(read_vec, cur_obj, wr, 0, bs->dsk.data_block_size,
|
||||||
wr->type() == BS_HEAP_INTENT_WRITE && bs->dsk.csum_block_size > bs->dsk.bitmap_granularity && !bs->perfect_csum_update
|
wr->type() == BS_HEAP_INTENT_WRITE && bs->dsk.csum_block_size > bs->dsk.bitmap_granularity && !bs->perfect_csum_update
|
||||||
? COPY_BUF_SKIP_CSUM : 0);
|
? COPY_BUF_SKIP_CSUM : 0);
|
||||||
if (res > 0)
|
|
||||||
copy_count++;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!compact_info.compact_lsn)
|
if (!compact_info.compact_lsn)
|
||||||
{
|
{
|
||||||
// Flushing is aborted
|
// Flushing is aborted
|
||||||
flusher->flushing.erase(cur_oid);
|
|
||||||
bs->heap->unlock_entry(cur_oid);
|
bs->heap->unlock_entry(cur_oid);
|
||||||
goto resume_0;
|
goto resume_0;
|
||||||
}
|
}
|
||||||
mem_or(new_bmp, compact_info.clean_wr->get_int_bitmap(bs->heap), bs->dsk.clean_entry_bitmap_size);
|
flusher->flushing[cur_oid] = compact_info.compact_lsn;
|
||||||
if (!bitmap_copied)
|
|
||||||
{
|
|
||||||
memcpy(new_ext_bmp, compact_info.clean_wr->get_ext_bitmap(bs->heap), bs->dsk.clean_entry_bitmap_size);
|
|
||||||
bitmap_copied = true;
|
|
||||||
}
|
|
||||||
if (bs->dsk.csum_block_size && bs->dsk.csum_block_size <= bs->dsk.bitmap_granularity)
|
|
||||||
{
|
|
||||||
memcpy(new_csums, compact_info.clean_wr->get_checksums(bs->heap), bs->dsk.data_block_size/bs->dsk.csum_block_size * (bs->dsk.data_csum_type & 0xFF));
|
|
||||||
for (size_t i = csum_copy.size(); i > 0; i--)
|
|
||||||
{
|
|
||||||
auto wr = csum_copy[i-1];
|
|
||||||
memcpy(new_csums + wr->small().offset/bs->dsk.csum_block_size*(bs->dsk.data_csum_type & 0xFF),
|
|
||||||
wr->get_checksums(bs->heap), wr->small().len/bs->dsk.csum_block_size*(bs->dsk.data_csum_type & 0xFF));
|
|
||||||
}
|
|
||||||
csum_copy.clear();
|
|
||||||
}
|
|
||||||
clean_loc = compact_info.clean_wr->big_location(bs->heap);
|
|
||||||
flusher->active_flushers++;
|
flusher->active_flushers++;
|
||||||
if (bs->log_level > 10)
|
if (!compact_info.do_delete && compact_info.clean_wr->type() == BS_HEAP_BIG_INTENT &&
|
||||||
|
(!bs->dsk.csum_block_size || bs->dsk.csum_block_size > bs->dsk.bitmap_granularity))
|
||||||
{
|
{
|
||||||
printf("Compacting %jx:%jx v%ju..v%ju / l%ju..l%ju (%d writes)\n", cur_oid.inode, cur_oid.stripe,
|
// Compaction merges newer writes into the data block in place, which changes bytes that
|
||||||
compact_info.clean_wr->version, compact_info.compact_version,
|
// <clean_wr>'s checksums still describe. Reads never take those bytes from it, but the
|
||||||
compact_info.clean_wr->lsn, compact_info.compact_lsn, copy_count);
|
// startup recheck does, and it can only skip the overwritten parts when a checksum block
|
||||||
|
// covers exactly one write granule. With a larger block an overwritten part poisons the
|
||||||
|
// checksum of the whole block, and with checksums disabled a big_intent still carries a
|
||||||
|
// single checksum of its whole extent - it's the only way to tell an unfinished in-place
|
||||||
|
// write from a finished one. Either way see blockstore_heap_t::recheck_verify().
|
||||||
|
// So make sure the entry is out of the recheck's scope by persisting completed_lsn first.
|
||||||
|
// It is below fsynced_lsn already - iterate_compaction() doesn't return anything above it
|
||||||
|
resume_26:
|
||||||
|
resume_27:
|
||||||
|
resume_28:
|
||||||
|
if (!trim_lsn(26))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (i = 0; i < read_vec.size(); i++)
|
||||||
|
{
|
||||||
|
if ((read_vec[i].copy_flags & COPY_BUF_JOURNAL) &&
|
||||||
|
!(read_vec[i].copy_flags & COPY_BUF_COALESCED))
|
||||||
|
{
|
||||||
|
copy_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (copy_count > 0 && !bs->dsk.disable_data_fsync)
|
||||||
|
{
|
||||||
|
init_fsync_data();
|
||||||
|
}
|
||||||
|
if (compact_info.do_delete)
|
||||||
|
{
|
||||||
|
if (bs->log_level > 10)
|
||||||
|
{
|
||||||
|
printf("Compacting %jx:%jx up to l%ju (delete)\n", cur_oid.inode, cur_oid.stripe, compact_info.compact_lsn);
|
||||||
|
}
|
||||||
|
clean_loc = UINT64_MAX;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (bs->log_level > 10)
|
||||||
|
{
|
||||||
|
printf("Compacting %jx:%jx v%ju..v%ju / l%ju..l%ju (%d writes)\n", cur_oid.inode, cur_oid.stripe,
|
||||||
|
compact_info.clean_wr->version, compact_info.compact_version,
|
||||||
|
compact_info.clean_wr->lsn, compact_info.compact_lsn, copy_count);
|
||||||
|
}
|
||||||
|
mem_or(new_bmp, compact_info.clean_wr->get_int_bitmap(bs->heap), bs->dsk.clean_entry_bitmap_size);
|
||||||
|
if (!bitmap_copied)
|
||||||
|
{
|
||||||
|
memcpy(new_ext_bmp, compact_info.clean_wr->get_ext_bitmap(bs->heap), bs->dsk.clean_entry_bitmap_size);
|
||||||
|
bitmap_copied = true;
|
||||||
|
}
|
||||||
|
if (bs->dsk.csum_block_size && bs->dsk.csum_block_size <= bs->dsk.bitmap_granularity)
|
||||||
|
{
|
||||||
|
memcpy(new_csums, compact_info.clean_wr->get_checksums(bs->heap), bs->dsk.data_block_size/bs->dsk.csum_block_size * (bs->dsk.data_csum_type & 0xFF));
|
||||||
|
for (size_t i = csum_copy.size(); i > 0; i--)
|
||||||
|
{
|
||||||
|
auto wr = csum_copy[i-1];
|
||||||
|
memcpy(new_csums + wr->small().offset/bs->dsk.csum_block_size*(bs->dsk.data_csum_type & 0xFF),
|
||||||
|
wr->get_checksums(bs->heap), wr->small().len/bs->dsk.csum_block_size*(bs->dsk.data_csum_type & 0xFF));
|
||||||
|
}
|
||||||
|
csum_copy.clear();
|
||||||
|
}
|
||||||
|
clean_loc = compact_info.clean_wr->big_location(bs->heap);
|
||||||
}
|
}
|
||||||
overwrite_start = overwrite_end = 0;
|
overwrite_start = overwrite_end = 0;
|
||||||
if (read_vec.size() > 0)
|
if (read_vec.size() > 0)
|
||||||
@@ -336,6 +414,13 @@ resume_3:
|
|||||||
if (res == ENOENT || res == EDOM)
|
if (res == ENOENT || res == EDOM)
|
||||||
{
|
{
|
||||||
// Abort compaction
|
// Abort compaction
|
||||||
|
abort_compact:
|
||||||
|
if (copy_count > 0 && !bs->dsk.disable_data_fsync)
|
||||||
|
{
|
||||||
|
cur_sync->member_count--;
|
||||||
|
if (cur_sync->member_count > 0)
|
||||||
|
bs->ringloop->wakeup();
|
||||||
|
}
|
||||||
flusher->flushing.erase(cur_oid);
|
flusher->flushing.erase(cur_oid);
|
||||||
bs->heap->unlock_entry(cur_oid);
|
bs->heap->unlock_entry(cur_oid);
|
||||||
flusher->active_flushers--;
|
flusher->active_flushers--;
|
||||||
@@ -349,10 +434,7 @@ resume_4:
|
|||||||
if (res == ENOENT)
|
if (res == ENOENT)
|
||||||
{
|
{
|
||||||
// Abort compaction
|
// Abort compaction
|
||||||
flusher->flushing.erase(cur_oid);
|
goto abort_compact;
|
||||||
bs->heap->unlock_entry(cur_oid);
|
|
||||||
flusher->active_flushers--;
|
|
||||||
goto resume_0;
|
|
||||||
}
|
}
|
||||||
if (res == EAGAIN)
|
if (res == EAGAIN)
|
||||||
{
|
{
|
||||||
@@ -381,14 +463,14 @@ resume_9:
|
|||||||
for (i = 0; i < read_vec.size(); i++)
|
for (i = 0; i < read_vec.size(); i++)
|
||||||
{
|
{
|
||||||
if ((read_vec[i].copy_flags & COPY_BUF_JOURNAL) &&
|
if ((read_vec[i].copy_flags & COPY_BUF_JOURNAL) &&
|
||||||
!(read_vec[i].copy_flags & COPY_BUF_COALESCED) ||
|
!(read_vec[i].copy_flags & COPY_BUF_COALESCED))
|
||||||
(read_vec[i].copy_flags & COPY_BUF_PADDED)) // FIXME Shit, simplify these flags
|
|
||||||
{
|
{
|
||||||
assert(read_vec[i].buf);
|
assert(read_vec[i].buf);
|
||||||
await_sqe(10);
|
await_sqe(10);
|
||||||
data->iov = (struct iovec){ read_vec[i].buf + (read_vec[i].copy_flags & COPY_BUF_PADDED
|
data->iov = (struct iovec){ read_vec[i].buf + (read_vec[i].copy_flags & COPY_BUF_PADDED
|
||||||
? read_vec[i].offset - read_vec[i].disk_offset : 0), (size_t)read_vec[i].len };
|
? read_vec[i].offset - read_vec[i].disk_offset : 0), (size_t)read_vec[i].len };
|
||||||
data->callback = simple_callback_w;
|
data->callback = simple_callback_w;
|
||||||
|
assert(clean_loc + read_vec[i].offset + data->iov.iov_len <= bs->dsk.block_count*bs->dsk.data_block_size);
|
||||||
io_uring_prep_writev(sqe, bs->dsk.data_fd, &data->iov, 1, bs->dsk.data_offset + clean_loc + read_vec[i].offset);
|
io_uring_prep_writev(sqe, bs->dsk.data_fd, &data->iov, 1, bs->dsk.data_offset + clean_loc + read_vec[i].offset);
|
||||||
wait_count++;
|
wait_count++;
|
||||||
}
|
}
|
||||||
@@ -399,6 +481,17 @@ resume_11:
|
|||||||
wait_state = 11;
|
wait_state = 11;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (copy_count > 0 && !bs->dsk.disable_data_fsync)
|
||||||
|
{
|
||||||
|
resume_22:
|
||||||
|
resume_23:
|
||||||
|
resume_24:
|
||||||
|
resume_25:
|
||||||
|
if (!fsync_data(22))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
// Lock is only needed to prevent freeing the big_write because we overwrite it...
|
// Lock is only needed to prevent freeing the big_write because we overwrite it...
|
||||||
bs->heap->unlock_entry(cur_oid);
|
bs->heap->unlock_entry(cur_oid);
|
||||||
// Mark the object compacted, but don't free and remove small_writes
|
// Mark the object compacted, but don't free and remove small_writes
|
||||||
@@ -408,12 +501,14 @@ resume_11:
|
|||||||
if (!cur_obj)
|
if (!cur_obj)
|
||||||
{
|
{
|
||||||
// Abort compaction
|
// Abort compaction
|
||||||
|
flusher->active_flushers--;
|
||||||
flusher->flushing.erase(cur_oid);
|
flusher->flushing.erase(cur_oid);
|
||||||
goto resume_0;
|
goto resume_0;
|
||||||
}
|
}
|
||||||
if (!calc_block_checksums())
|
if (!calc_block_checksums())
|
||||||
{
|
{
|
||||||
// Abort compaction
|
// Abort compaction
|
||||||
|
flusher->active_flushers--;
|
||||||
flusher->flushing.erase(cur_oid);
|
flusher->flushing.erase(cur_oid);
|
||||||
goto resume_0;
|
goto resume_0;
|
||||||
}
|
}
|
||||||
@@ -422,6 +517,7 @@ resume_11:
|
|||||||
if (res == EBUSY)
|
if (res == EBUSY)
|
||||||
{
|
{
|
||||||
// Abort compaction, object is already overwritten by something else
|
// Abort compaction, object is already overwritten by something else
|
||||||
|
flusher->active_flushers--;
|
||||||
flusher->flushing.erase(cur_oid);
|
flusher->flushing.erase(cur_oid);
|
||||||
goto resume_0;
|
goto resume_0;
|
||||||
}
|
}
|
||||||
@@ -438,12 +534,12 @@ resume_13:
|
|||||||
printf("Compacted %jx:%jx l%ju (%d writes)\n", cur_oid.inode, cur_oid.stripe, compact_info.compact_lsn, copy_count);
|
printf("Compacted %jx:%jx l%ju (%d writes)\n", cur_oid.inode, cur_oid.stripe, compact_info.compact_lsn, copy_count);
|
||||||
}
|
}
|
||||||
flusher->active_flushers--;
|
flusher->active_flushers--;
|
||||||
|
flusher->flushing.erase(cur_oid);
|
||||||
if (should_repeat)
|
if (should_repeat)
|
||||||
{
|
{
|
||||||
// Flush the same object again
|
// Flush the same object again
|
||||||
goto resume_1;
|
goto resume_1;
|
||||||
}
|
}
|
||||||
flusher->flushing.erase(cur_oid);
|
|
||||||
// All done
|
// All done
|
||||||
goto resume_0;
|
goto resume_0;
|
||||||
}
|
}
|
||||||
@@ -489,7 +585,7 @@ void journal_flusher_co::fill_partial_checksum_blocks()
|
|||||||
}
|
}
|
||||||
auto & vec = read_vec[read_vec.size()-1];
|
auto & vec = read_vec[read_vec.size()-1];
|
||||||
read_vec.insert(read_vec.begin()+vec_pos, (copy_buffer_t){
|
read_vec.insert(read_vec.begin()+vec_pos, (copy_buffer_t){
|
||||||
.copy_flags = COPY_BUF_JOURNAL|COPY_BUF_COALESCED,
|
.copy_flags = COPY_BUF_DATA|COPY_BUF_COALESCED,
|
||||||
.offset = hole_start,
|
.offset = hole_start,
|
||||||
.len = hole_end - hole_start,
|
.len = hole_end - hole_start,
|
||||||
.disk_offset = hole_start,
|
.disk_offset = hole_start,
|
||||||
@@ -586,13 +682,13 @@ int journal_flusher_co::check_and_punch_checksums()
|
|||||||
bs->heap->calc_block_checksums((uint32_t*)(new_csums+csum_off), vec.buf, punch_bmp, vec.offset, vec.offset+vec.len, true, NULL);
|
bs->heap->calc_block_checksums((uint32_t*)(new_csums+csum_off), vec.buf, punch_bmp, vec.offset, vec.offset+vec.len, true, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Modified, we should add_punch_holes and then write the block to disk
|
// Modified, we should punch_holes and then write the block to disk
|
||||||
return EBUSY;
|
return EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool journal_flusher_co::calc_block_checksums()
|
bool journal_flusher_co::calc_block_checksums()
|
||||||
{
|
{
|
||||||
if (bs->dsk.csum_block_size <= bs->dsk.bitmap_granularity)
|
if (bs->dsk.csum_block_size <= bs->dsk.bitmap_granularity || compact_info.do_delete)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -664,9 +760,11 @@ bool journal_flusher_co::read_buffered(int wait_base)
|
|||||||
else if (wait_state == wait_base+1)
|
else if (wait_state == wait_base+1)
|
||||||
goto resume_1;
|
goto resume_1;
|
||||||
wait_count = 0;
|
wait_count = 0;
|
||||||
if (bs->dsk.inmemory_journal && !read_to_fill_incomplete)
|
if (bs->dsk.inmemory_journal && bs->dsk.csum_block_size <= bs->dsk.bitmap_granularity)
|
||||||
{
|
{
|
||||||
// Happy path: nothing to read :)
|
// Happy path: nothing to read :)
|
||||||
|
// We may have to read intent writes to recalculate block checksums, so proceed
|
||||||
|
// even if read_to_fill_incomplete is false, but csum_block_size is > bitmap_granularity
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for (i = 0; i < read_vec.size(); i++)
|
for (i = 0; i < read_vec.size(); i++)
|
||||||
@@ -699,6 +797,67 @@ resume_1:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void journal_flusher_co::init_fsync_data()
|
||||||
|
{
|
||||||
|
cur_sync = flusher->data_syncs.begin();
|
||||||
|
if (cur_sync == flusher->data_syncs.end() || cur_sync->ready_count > 0)
|
||||||
|
{
|
||||||
|
cur_sync = flusher->data_syncs.emplace(cur_sync);
|
||||||
|
}
|
||||||
|
cur_sync->member_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool journal_flusher_co::fsync_data(int wait_base)
|
||||||
|
{
|
||||||
|
if (wait_state == wait_base)
|
||||||
|
goto resume_0;
|
||||||
|
else if (wait_state == wait_base+1)
|
||||||
|
goto resume_1;
|
||||||
|
else if (wait_state == wait_base+2)
|
||||||
|
goto resume_2;
|
||||||
|
else if (wait_state == wait_base+3)
|
||||||
|
goto resume_3;
|
||||||
|
cur_sync->ready_count++;
|
||||||
|
resume_0:
|
||||||
|
if (cur_sync->ready_count < cur_sync->member_count)
|
||||||
|
{
|
||||||
|
wait_state = wait_base;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!cur_sync->sent)
|
||||||
|
{
|
||||||
|
// Sync batch is ready. Do it.
|
||||||
|
await_sqe(1);
|
||||||
|
data->iov = { 0 };
|
||||||
|
data->callback = simple_callback_w;
|
||||||
|
io_uring_prep_fsync(sqe, bs->dsk.data_fd, IORING_FSYNC_DATASYNC);
|
||||||
|
cur_sync->sent = true;
|
||||||
|
wait_count++;
|
||||||
|
resume_2:
|
||||||
|
if (wait_count > 0)
|
||||||
|
{
|
||||||
|
wait_state = wait_base+2;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cur_sync->done = true;
|
||||||
|
// Wake up other flushers
|
||||||
|
bs->ringloop->wakeup();
|
||||||
|
}
|
||||||
|
resume_3:
|
||||||
|
if (!cur_sync->done)
|
||||||
|
{
|
||||||
|
wait_state = wait_base+3;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cur_sync->done_count++;
|
||||||
|
if (cur_sync->done_count >= cur_sync->member_count)
|
||||||
|
{
|
||||||
|
flusher->data_syncs.erase(cur_sync);
|
||||||
|
cur_sync = flusher->data_syncs.end();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool journal_flusher_co::fsync_meta(int wait_base)
|
bool journal_flusher_co::fsync_meta(int wait_base)
|
||||||
{
|
{
|
||||||
if (wait_state == wait_base) goto resume_0;
|
if (wait_state == wait_base) goto resume_0;
|
||||||
@@ -774,27 +933,40 @@ bool journal_flusher_co::trim_lsn(int wait_base)
|
|||||||
{
|
{
|
||||||
if (wait_state == wait_base) goto resume_0;
|
if (wait_state == wait_base) goto resume_0;
|
||||||
else if (wait_state == wait_base+1) goto resume_1;
|
else if (wait_state == wait_base+1) goto resume_1;
|
||||||
|
else if (wait_state == wait_base+2) goto resume_2;
|
||||||
|
resume_0:
|
||||||
|
if (flusher->trimming_lsn)
|
||||||
|
{
|
||||||
|
// Allow only one coroutine to update the superblock at a time to prevent parallel
|
||||||
|
// block updates or the device may in theory apply them in any order.
|
||||||
|
wait_state = wait_base;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
fsynced_lsn = bs->heap->get_fsynced_lsn();
|
fsynced_lsn = bs->heap->get_fsynced_lsn();
|
||||||
if (((blockstore_meta_header_v3_t*)bs->meta_superblock)->completed_lsn == fsynced_lsn)
|
if (((blockstore_meta_header_v3_t*)bs->meta_superblock)->completed_lsn == fsynced_lsn)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
flusher->trimming_lsn = true;
|
||||||
flusher->active_flushers++;
|
flusher->active_flushers++;
|
||||||
((blockstore_meta_header_v3_t*)bs->meta_superblock)->completed_lsn = fsynced_lsn;
|
((blockstore_meta_header_v3_t*)bs->meta_superblock)->completed_lsn = fsynced_lsn;
|
||||||
((blockstore_meta_header_v3_t*)bs->meta_superblock)->set_crc32c();
|
((blockstore_meta_header_v3_t*)bs->meta_superblock)->set_crc32c();
|
||||||
await_sqe(0);
|
await_sqe(1);
|
||||||
data->iov = (struct iovec){ bs->meta_superblock, (size_t)bs->dsk.meta_block_size };
|
data->iov = (struct iovec){ bs->meta_superblock, (size_t)bs->dsk.meta_block_size };
|
||||||
data->callback = simple_callback_w;
|
data->callback = simple_callback_w;
|
||||||
io_uring_prep_writev(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset);
|
io_uring_prep_writev(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset);
|
||||||
// Update superblock with datasync
|
// Update superblock with datasync
|
||||||
sqe->rw_flags = RWF_DSYNC;
|
sqe->rw_flags = RWF_DSYNC;
|
||||||
wait_count++;
|
wait_count++;
|
||||||
resume_1:
|
resume_2:
|
||||||
if (wait_count > 0)
|
if (wait_count > 0)
|
||||||
{
|
{
|
||||||
wait_state = wait_base+1;
|
wait_state = wait_base+2;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// Superblock is updated, resume everyone who was waiting for it
|
||||||
|
flusher->trimming_lsn = false;
|
||||||
|
bs->ringloop->wakeup();
|
||||||
flusher->active_flushers--;
|
flusher->active_flushers--;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,15 @@ struct flusher_meta_write_t
|
|||||||
std::map<uint64_t, meta_sector_t>::iterator it;
|
std::map<uint64_t, meta_sector_t>::iterator it;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct flusher_data_sync_t
|
||||||
|
{
|
||||||
|
int member_count = 0;
|
||||||
|
int ready_count = 0;
|
||||||
|
int done_count = 0;
|
||||||
|
bool sent = false;
|
||||||
|
bool done = false;
|
||||||
|
};
|
||||||
|
|
||||||
class journal_flusher_t;
|
class journal_flusher_t;
|
||||||
|
|
||||||
// Journal flusher coroutine
|
// Journal flusher coroutine
|
||||||
@@ -58,6 +67,7 @@ class journal_flusher_co
|
|||||||
int i, res;
|
int i, res;
|
||||||
bool read_to_fill_incomplete;
|
bool read_to_fill_incomplete;
|
||||||
int copy_count;
|
int copy_count;
|
||||||
|
std::list<flusher_data_sync_t>::iterator cur_sync;
|
||||||
|
|
||||||
friend class journal_flusher_t;
|
friend class journal_flusher_t;
|
||||||
|
|
||||||
@@ -68,6 +78,8 @@ class journal_flusher_co
|
|||||||
bool calc_block_checksums();
|
bool calc_block_checksums();
|
||||||
bool write_meta_block(int wait_base);
|
bool write_meta_block(int wait_base);
|
||||||
bool read_buffered(int wait_base);
|
bool read_buffered(int wait_base);
|
||||||
|
void init_fsync_data();
|
||||||
|
bool fsync_data(int wait_base);
|
||||||
bool fsync_meta(int wait_base);
|
bool fsync_meta(int wait_base);
|
||||||
bool fsync_buffer(int wait_base);
|
bool fsync_buffer(int wait_base);
|
||||||
bool trim_lsn(int wait_base);
|
bool trim_lsn(int wait_base);
|
||||||
@@ -81,24 +93,30 @@ public:
|
|||||||
class journal_flusher_t
|
class journal_flusher_t
|
||||||
{
|
{
|
||||||
int force_start = 0;
|
int force_start = 0;
|
||||||
|
int force_fsync = 0;
|
||||||
int min_flusher_count = 0, max_flusher_count = 0, cur_flusher_count = 0, target_flusher_count = 0;
|
int min_flusher_count = 0, max_flusher_count = 0, cur_flusher_count = 0, target_flusher_count = 0;
|
||||||
journal_flusher_co *co;
|
journal_flusher_co *co;
|
||||||
blockstore_impl_t *bs;
|
blockstore_impl_t *bs;
|
||||||
friend class journal_flusher_co;
|
friend class journal_flusher_co;
|
||||||
|
|
||||||
robin_hood::unordered_flat_set<object_id> flushing;
|
robin_hood::unordered_flat_map<object_id, uint64_t> flushing;
|
||||||
int active_flushers = 0;
|
int active_flushers = 0;
|
||||||
|
std::list<flusher_data_sync_t> data_syncs;
|
||||||
int wanting_meta_fsync = 0;
|
int wanting_meta_fsync = 0;
|
||||||
bool fsyncing_meta = false;
|
bool fsyncing_meta = false;
|
||||||
int syncing_buffer = 0;
|
int syncing_buffer = 0;
|
||||||
|
bool trimming_lsn = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
journal_flusher_t(blockstore_impl_t *bs);
|
journal_flusher_t(blockstore_impl_t *bs);
|
||||||
~journal_flusher_t();
|
~journal_flusher_t();
|
||||||
void loop();
|
void loop();
|
||||||
int get_syncing_buffer();
|
int get_syncing_buffer();
|
||||||
|
uint64_t get_flushing_lsn(object_id oid);
|
||||||
bool is_active();
|
bool is_active();
|
||||||
void request_trim();
|
void request_trim();
|
||||||
void release_trim();
|
void release_trim();
|
||||||
|
void request_fsync();
|
||||||
|
void release_fsync();
|
||||||
void dump_diagnostics();
|
void dump_diagnostics();
|
||||||
};
|
};
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -57,11 +57,11 @@ struct __attribute__((__packed__)) heap_entry_t
|
|||||||
inline heap_small_write_t& small() { return *(heap_small_write_t*)this; }
|
inline heap_small_write_t& small() { return *(heap_small_write_t*)this; }
|
||||||
inline heap_big_write_t& big() { return *(heap_big_write_t*)this; }
|
inline heap_big_write_t& big() { return *(heap_big_write_t*)this; }
|
||||||
inline heap_big_intent_t& big_intent() { return *(heap_big_intent_t*)this; }
|
inline heap_big_intent_t& big_intent() { return *(heap_big_intent_t*)this; }
|
||||||
bool is_garbage();
|
bool is_garbage() const;
|
||||||
void set_garbage();
|
void set_garbage();
|
||||||
bool is_overwrite();
|
bool is_overwrite() const;
|
||||||
bool is_compactable();
|
bool is_compactable() const;
|
||||||
bool is_before(heap_entry_t *other);
|
bool is_before(const heap_entry_t *other) const;
|
||||||
uint32_t get_size(blockstore_heap_t *heap);
|
uint32_t get_size(blockstore_heap_t *heap);
|
||||||
uint8_t *get_ext_bitmap(blockstore_heap_t *heap);
|
uint8_t *get_ext_bitmap(blockstore_heap_t *heap);
|
||||||
uint8_t *get_int_bitmap(blockstore_heap_t *heap);
|
uint8_t *get_int_bitmap(blockstore_heap_t *heap);
|
||||||
@@ -159,6 +159,19 @@ struct heap_li_equal
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct heap_recheck_state_t
|
||||||
|
{
|
||||||
|
heap_entry_t *obj = NULL;
|
||||||
|
heap_entry_t *next_wr = NULL;
|
||||||
|
size_t total_reads = 0;
|
||||||
|
size_t sent_reads = 0;
|
||||||
|
size_t checked_reads = 0;
|
||||||
|
heap_entry_t *bad_wr = NULL;
|
||||||
|
// The oldest entry a read was issued for. Everything below it is unverified, so if it
|
||||||
|
// turns out to be bad the object has to be looked at again after dropping it
|
||||||
|
heap_entry_t *oldest_wr = NULL;
|
||||||
|
};
|
||||||
|
|
||||||
using i64hash_t = robin_hood::hash<uint64_t>;
|
using i64hash_t = robin_hood::hash<uint64_t>;
|
||||||
using heap_inode_map_t = robin_hood::unordered_flat_set<heap_list_item_t*, heap_li_hash, heap_li_equal, 88>;
|
using heap_inode_map_t = robin_hood::unordered_flat_set<heap_list_item_t*, heap_li_hash, heap_li_equal, 88>;
|
||||||
using heap_block_index_t = robin_hood::unordered_flat_map<uint64_t,
|
using heap_block_index_t = robin_hood::unordered_flat_map<uint64_t,
|
||||||
@@ -209,36 +222,48 @@ class blockstore_heap_t
|
|||||||
|
|
||||||
bool marked_used_blocks = false;
|
bool marked_used_blocks = false;
|
||||||
bool recheck_queue_filled = false;
|
bool recheck_queue_filled = false;
|
||||||
std::vector<heap_list_item_t*> loaded_list_items;
|
std::vector<heap_list_item_t*> postponed_items;
|
||||||
|
std::vector<heap_list_item_t*> init_erase_items;
|
||||||
std::set<uint32_t> recheck_modified_blocks;
|
std::set<uint32_t> recheck_modified_blocks;
|
||||||
std::deque<heap_entry_t*> recheck_queue;
|
std::deque<heap_entry_t*> recheck_queue;
|
||||||
|
std::map<heap_entry_t*, heap_recheck_state_t> recheck_states;
|
||||||
|
size_t recheck_pending_reads = 0;
|
||||||
int recheck_in_progress = 0;
|
int recheck_in_progress = 0;
|
||||||
bool in_recheck = false;
|
bool in_recheck = false;
|
||||||
std::function<void(bool is_data, uint64_t offset, uint64_t len, uint8_t* buf, std::function<void()>)> recheck_cb;
|
std::function<void(bool is_data, uint64_t offset, uint64_t len, std::function<void(uint8_t*)>)> recheck_cb;
|
||||||
int recheck_queue_depth = 0;
|
int recheck_queue_depth = 0;
|
||||||
|
|
||||||
uint64_t get_pg_id(inode_t inode, uint64_t stripe);
|
uint64_t get_pg_id(inode_t inode, uint64_t stripe);
|
||||||
bool validate_object(heap_entry_t *obj);
|
bool validate_object(heap_entry_t *obj);
|
||||||
void fill_recheck_queue();
|
void fill_recheck_queue();
|
||||||
|
void recheck_requeue(object_id oid);
|
||||||
|
void recheck_drop_entries(heap_entry_t *obj, heap_entry_t *bad_wr);
|
||||||
|
int recheck_start_reads(heap_recheck_state_t *st);
|
||||||
int mark_used_blocks();
|
int mark_used_blocks();
|
||||||
|
void init_free_bad_entry(heap_entry_t *wr);
|
||||||
|
void init_erase_bad_entry(heap_list_item_t *li);
|
||||||
|
bool init_erase_double_claim(heap_list_item_t *prev_li, heap_list_item_t *cur_li);
|
||||||
void recheck_full_gc();
|
void recheck_full_gc();
|
||||||
void recheck_buffer(heap_entry_t *cwr, uint8_t *buf);
|
void recheck_buffer(heap_entry_t *cwr, uint8_t *buf);
|
||||||
void defragment_block(uint32_t block_num);
|
void defragment_block(uint32_t block_num);
|
||||||
void reshard_add(heap_reshard_state_t *st, heap_list_item_t *li);
|
void reshard_add(heap_reshard_state_t *st, heap_list_item_t *li);
|
||||||
|
|
||||||
void gc_block(heap_block_info_t & inf);
|
void gc_block(heap_block_info_t & inf);
|
||||||
int allocate_entry(uint32_t entry_size, uint32_t *block_num, bool allow_last_free);
|
int allocate_entry(uint32_t entry_size, uint32_t *block_num, bool allow_last_free, uint32_t prefer_block);
|
||||||
void insert_list_item(heap_list_item_t *li);
|
void insert_list_items(heap_list_item_t** v, size_t count, bool postpone);
|
||||||
void remove_list_item(heap_list_item_t *li);
|
void unlink_list_item(heap_list_item_t *li);
|
||||||
int add_entry(uint32_t wr_size, uint32_t *modified_block, bool allow_last_free,
|
int add_entry(uint32_t wr_size, heap_entry_t *prev_obj, uint32_t *modified_block, bool prefer_same,
|
||||||
bool explicit_complete, std::function<void(heap_entry_t *wr)> fill_entry);
|
bool allow_last_free, bool explicit_complete, std::function<void(heap_entry_t *wr)> fill_entry);
|
||||||
int add_simple(heap_entry_t *obj, uint64_t version, uint32_t *modified_block, uint32_t entry_type);
|
int add_simple(heap_entry_t *obj, uint64_t version, uint32_t *modified_block, uint32_t entry_type, bool prefer_same);
|
||||||
uint32_t meta_alloc_pos(const heap_block_info_t & inf);
|
uint32_t meta_alloc_pos(const heap_block_info_t & inf);
|
||||||
void modify_alloc(uint32_t block_num, std::function<void(heap_block_info_t &)> change_cb);
|
void modify_alloc(uint32_t block_num, std::function<void(heap_block_info_t &)> change_cb);
|
||||||
void mark_garbage_up_to(heap_entry_t *wr);
|
void mark_garbage_up_to(heap_entry_t *wr);
|
||||||
void mark_garbage(uint32_t block_num, heap_entry_t *prev_wr, uint32_t used_big);
|
void mark_garbage(uint32_t block_num, heap_entry_t *prev_wr, uint32_t used_big, uint32_t keep_big);
|
||||||
void push_inflight_lsn(uint64_t lsn, heap_entry_t *wr, uint64_t flags);
|
void push_inflight_lsn(uint64_t lsn, heap_entry_t *wr, uint64_t flags);
|
||||||
void mark_completed_lsns(uint64_t mod_lsn);
|
void mark_completed_lsns(uint64_t mod_lsn);
|
||||||
|
heap_entry_t *skip_commits(heap_entry_t *wr);
|
||||||
|
int count_recheck_entries(heap_entry_t *obj, bool & need_data);
|
||||||
|
bool recheck_verify(heap_recheck_state_t *st, heap_entry_t *wr, uint8_t *buf);
|
||||||
void apply_inflight(heap_inflight_lsn_t & inflight);
|
void apply_inflight(heap_inflight_lsn_t & inflight);
|
||||||
public:
|
public:
|
||||||
blockstore_heap_t(blockstore_disk_t *dsk, uint8_t *buffer_area, int log_level = 0);
|
blockstore_heap_t(blockstore_disk_t *dsk, uint8_t *buffer_area, int log_level = 0);
|
||||||
@@ -256,7 +281,7 @@ public:
|
|||||||
// before finishing initialization if not R/O
|
// before finishing initialization if not R/O
|
||||||
std::vector<uint32_t> get_recheck_modified_blocks();
|
std::vector<uint32_t> get_recheck_modified_blocks();
|
||||||
// recheck small write data after reading the database from disk
|
// recheck small write data after reading the database from disk
|
||||||
bool recheck_small_writes(std::function<void(bool is_data, uint64_t offset, uint64_t len, uint8_t* buf, std::function<void()>)> read_buffer, int queue_depth);
|
bool recheck_small_writes(std::function<void(bool is_data, uint64_t offset, uint64_t len, std::function<void(uint8_t* buf)>)> read_buffer, int queue_depth);
|
||||||
int finish_recheck();
|
int finish_recheck();
|
||||||
// reshard database according to the pool's PG count
|
// reshard database according to the pool's PG count
|
||||||
void* reshard_start(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size, uint64_t chunk_limit);
|
void* reshard_start(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size, uint64_t chunk_limit);
|
||||||
@@ -344,7 +369,7 @@ public:
|
|||||||
|
|
||||||
// get metadata block data buffer and used space
|
// get metadata block data buffer and used space
|
||||||
void get_meta_block(uint32_t block_num, uint8_t *buffer);
|
void get_meta_block(uint32_t block_num, uint8_t *buffer);
|
||||||
void fill_block_empty_space(uint8_t *buffer, uint32_t pos);
|
void fill_block_empty_space(uint8_t *buffer, uint64_t pos);
|
||||||
uint32_t get_meta_block_used_space(uint32_t block_num);
|
uint32_t get_meta_block_used_space(uint32_t block_num);
|
||||||
|
|
||||||
// get space usage statistics
|
// get space usage statistics
|
||||||
|
|||||||
@@ -43,6 +43,24 @@ blockstore_impl_t::blockstore_impl_t(blockstore_config_t & config, ring_loop_i *
|
|||||||
|
|
||||||
blockstore_impl_t::~blockstore_impl_t()
|
blockstore_impl_t::~blockstore_impl_t()
|
||||||
{
|
{
|
||||||
|
// Operations which were queued or in flight when we were destroyed still have their
|
||||||
|
// private state constructed, and a read also owns its read vector. Nobody is going to
|
||||||
|
// complete them now, so release it here
|
||||||
|
for (auto op: submit_queue)
|
||||||
|
if (op)
|
||||||
|
PRIV(op)->~blockstore_op_private_t();
|
||||||
|
submit_queue.clear();
|
||||||
|
for (auto op: reads_in_flight)
|
||||||
|
{
|
||||||
|
free_read_buffers(PRIV(op)->read_vec);
|
||||||
|
PRIV(op)->~blockstore_op_private_t();
|
||||||
|
}
|
||||||
|
reads_in_flight.clear();
|
||||||
|
// Metadata block buffers are normally freed by the write completion callback,
|
||||||
|
// so anything still here was queued or in flight when we were destroyed
|
||||||
|
for (auto & mb: modified_blocks)
|
||||||
|
free(mb.second.buf);
|
||||||
|
modified_blocks.clear();
|
||||||
if (flusher)
|
if (flusher)
|
||||||
delete flusher;
|
delete flusher;
|
||||||
if (heap)
|
if (heap)
|
||||||
@@ -101,6 +119,7 @@ void blockstore_impl_t::loop()
|
|||||||
unsigned initial_ring_space = ringloop->space_left();
|
unsigned initial_ring_space = ringloop->space_left();
|
||||||
int op_idx = 0, new_idx = 0;
|
int op_idx = 0, new_idx = 0;
|
||||||
bool has_unfinished_writes = false;
|
bool has_unfinished_writes = false;
|
||||||
|
bool has_unfinished_sync = false;
|
||||||
for (; op_idx < submit_queue.size(); op_idx++, new_idx++)
|
for (; op_idx < submit_queue.size(); op_idx++, new_idx++)
|
||||||
{
|
{
|
||||||
auto op = submit_queue[op_idx];
|
auto op = submit_queue[op_idx];
|
||||||
@@ -138,7 +157,13 @@ void blockstore_impl_t::loop()
|
|||||||
else if (op->opcode == BS_OP_SYNC)
|
else if (op->opcode == BS_OP_SYNC)
|
||||||
{
|
{
|
||||||
// syncs only completed writes, so doesn't have to be blocked by anything
|
// syncs only completed writes, so doesn't have to be blocked by anything
|
||||||
wr_st = continue_sync(op);
|
if (!has_unfinished_sync)
|
||||||
|
{
|
||||||
|
wr_st = continue_sync(op);
|
||||||
|
has_unfinished_sync = (wr_st != 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
wr_st = 0;
|
||||||
}
|
}
|
||||||
else if (op->opcode == BS_OP_STABLE || op->opcode == BS_OP_ROLLBACK)
|
else if (op->opcode == BS_OP_STABLE || op->opcode == BS_OP_ROLLBACK)
|
||||||
{
|
{
|
||||||
@@ -154,9 +179,7 @@ void blockstore_impl_t::loop()
|
|||||||
wr_st = 2;
|
wr_st = 2;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
wr_st = 0;
|
wr_st = 0;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (wr_st == 2)
|
if (wr_st == 2)
|
||||||
{
|
{
|
||||||
@@ -263,6 +286,19 @@ void blockstore_impl_t::check_wait(blockstore_op_t *op)
|
|||||||
flusher->release_trim();
|
flusher->release_trim();
|
||||||
PRIV(op)->wait_for = 0;
|
PRIV(op)->wait_for = 0;
|
||||||
}
|
}
|
||||||
|
else if (PRIV(op)->wait_for == WAIT_META_FSYNC)
|
||||||
|
{
|
||||||
|
if (heap->get_fsynced_lsn() < PRIV(op)->wait_detail)
|
||||||
|
{
|
||||||
|
// do not submit
|
||||||
|
#ifdef BLOCKSTORE_DEBUG
|
||||||
|
printf("Still waiting for LSN %ju to become durable\n", PRIV(op)->wait_detail);
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
flusher->release_fsync();
|
||||||
|
PRIV(op)->wait_for = 0;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw std::runtime_error("BUG: op->wait_for value is unexpected");
|
throw std::runtime_error("BUG: op->wait_for value is unexpected");
|
||||||
@@ -340,7 +376,7 @@ void blockstore_impl_t::process_list(blockstore_op_t *op)
|
|||||||
std::sort(result, result + stable_count);
|
std::sort(result, result + stable_count);
|
||||||
if (stable_count > op->list_stable_limit)
|
if (stable_count > op->list_stable_limit)
|
||||||
{
|
{
|
||||||
memmove(result + op->list_stable_limit, result + stable_count, unstable_count);
|
memmove(result + op->list_stable_limit, result + stable_count, unstable_count*sizeof(obj_ver_id));
|
||||||
stable_count = op->list_stable_limit;
|
stable_count = op->list_stable_limit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,12 +9,9 @@
|
|||||||
#include "ondisk_formats.h"
|
#include "ondisk_formats.h"
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <linux/fs.h>
|
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <list>
|
#include <list>
|
||||||
@@ -42,7 +39,7 @@ struct blockstore_op_private_t
|
|||||||
int op_state;
|
int op_state;
|
||||||
|
|
||||||
// Write, sync, stabilize
|
// Write, sync, stabilize
|
||||||
uint32_t modified_block, modified_block2;
|
uint32_t modified_block;
|
||||||
|
|
||||||
// Read
|
// Read
|
||||||
std::vector<copy_buffer_t> read_vec;
|
std::vector<copy_buffer_t> read_vec;
|
||||||
@@ -55,7 +52,9 @@ struct blockstore_op_private_t
|
|||||||
uint32_t write_type;
|
uint32_t write_type;
|
||||||
|
|
||||||
// Stabilize, rollback
|
// Stabilize, rollback
|
||||||
int stab_pos;
|
heap_entry_t *obj;
|
||||||
|
int stab_pos, res;
|
||||||
|
uint32_t modified_block2;
|
||||||
|
|
||||||
// Write
|
// Write
|
||||||
timespec tv_begin;
|
timespec tv_begin;
|
||||||
@@ -109,6 +108,9 @@ public:
|
|||||||
uint8_t* meta_superblock = NULL;
|
uint8_t* meta_superblock = NULL;
|
||||||
uint8_t *buffer_area = NULL;
|
uint8_t *buffer_area = NULL;
|
||||||
std::vector<blockstore_op_t*> submit_queue;
|
std::vector<blockstore_op_t*> submit_queue;
|
||||||
|
// Reads waiting for their disk requests. They're out of submit_queue already, but we
|
||||||
|
// still own their private state, including the read vector - see the destructor
|
||||||
|
robin_hood::unordered_flat_set<blockstore_op_t*> reads_in_flight;
|
||||||
int unsynced_data_write_count = 0, unsynced_buffer_write_count = 0, unsynced_meta_write_count = 0;
|
int unsynced_data_write_count = 0, unsynced_buffer_write_count = 0, unsynced_meta_write_count = 0;
|
||||||
int unsynced_queued_ops = 0;
|
int unsynced_queued_ops = 0;
|
||||||
|
|
||||||
@@ -117,9 +119,12 @@ public:
|
|||||||
|
|
||||||
journal_flusher_t *flusher;
|
journal_flusher_t *flusher;
|
||||||
int write_iodepth = 0;
|
int write_iodepth = 0;
|
||||||
int inflight_big = 0;
|
|
||||||
int intent_write_counter = 0;
|
int intent_write_counter = 0;
|
||||||
bool fsyncing_data = false;
|
uint64_t data_fsync_next = 0;
|
||||||
|
uint64_t data_fsync_cur = 0;
|
||||||
|
uint64_t data_fsync_sent = 0;
|
||||||
|
uint64_t data_fsync_done = 0;
|
||||||
|
std::deque<bool> data_fsyncs;
|
||||||
|
|
||||||
bool live = false, queue_stall = false;
|
bool live = false, queue_stall = false;
|
||||||
ring_loop_i *ringloop = NULL;
|
ring_loop_i *ringloop = NULL;
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
#define INIT_META_EMPTY 0
|
#define INIT_META_EMPTY 0
|
||||||
#define INIT_META_READING 1
|
#define INIT_META_READING 1
|
||||||
#define INIT_META_READ_DONE 2
|
#define INIT_META_READ_DONE 2
|
||||||
#define INIT_META_WRITING 3
|
|
||||||
|
|
||||||
#define GET_SQE() \
|
#define GET_SQE() \
|
||||||
sqe = bs->get_sqe();\
|
sqe = bs->get_sqe();\
|
||||||
@@ -18,19 +17,34 @@
|
|||||||
throw std::runtime_error("io_uring is full during initialization");\
|
throw std::runtime_error("io_uring is full during initialization");\
|
||||||
data = ((ring_data_t*)sqe->user_data)
|
data = ((ring_data_t*)sqe->user_data)
|
||||||
|
|
||||||
|
struct blockstore_recheck_io_t
|
||||||
|
{
|
||||||
|
bool is_data;
|
||||||
|
uint64_t offset;
|
||||||
|
uint64_t len;
|
||||||
|
uint8_t *buf;
|
||||||
|
std::function<void(uint8_t *buf)> cb;
|
||||||
|
|
||||||
|
blockstore_recheck_io_t(bool is_data, uint64_t offset, uint64_t len, std::function<void(uint8_t *buf)>&& cb):
|
||||||
|
is_data(is_data), offset(offset), len(len), buf(NULL), cb(std::move(cb))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
blockstore_init_meta::blockstore_init_meta(blockstore_impl_t *bs)
|
blockstore_init_meta::blockstore_init_meta(blockstore_impl_t *bs)
|
||||||
{
|
{
|
||||||
this->bs = bs;
|
this->bs = bs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void blockstore_init_meta::handle_event(ring_data_t *data, int buf_num)
|
void blockstore_init_meta::handle_event(ring_data_t *data, int buf_num, const char *op)
|
||||||
{
|
{
|
||||||
if (data->res < 0)
|
if (data->res != data->iov.iov_len)
|
||||||
{
|
{
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(strprintf(
|
||||||
std::string("read metadata failed at offset ") + std::to_string(buf_num >= 0 ? bufs[buf_num].offset : last_read_offset) +
|
"%s failed at offset %ju: got %s (code %d), but expected %zu",
|
||||||
std::string(": ") + strerror(-data->res)
|
op, (buf_num >= 0 ? bufs[buf_num].offset : last_read_offset), strerror(-data->res),
|
||||||
);
|
data->res, data->iov.iov_len
|
||||||
|
));
|
||||||
}
|
}
|
||||||
if (buf_num >= 0)
|
if (buf_num >= 0)
|
||||||
{
|
{
|
||||||
@@ -60,7 +74,7 @@ int blockstore_init_meta::loop()
|
|||||||
GET_SQE();
|
GET_SQE();
|
||||||
last_read_offset = 0;
|
last_read_offset = 0;
|
||||||
data->iov = { bs->meta_superblock, (size_t)bs->dsk.meta_block_size };
|
data->iov = { bs->meta_superblock, (size_t)bs->dsk.meta_block_size };
|
||||||
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
|
data->callback = [this](ring_data_t *data) { handle_event(data, -1, "read metadata header"); };
|
||||||
io_uring_prep_readv(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset);
|
io_uring_prep_readv(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset);
|
||||||
bs->ringloop->submit();
|
bs->ringloop->submit();
|
||||||
submitted++;
|
submitted++;
|
||||||
@@ -72,25 +86,19 @@ resume_1:
|
|||||||
}
|
}
|
||||||
if (is_zero((uint64_t*)bs->meta_superblock, bs->dsk.meta_block_size))
|
if (is_zero((uint64_t*)bs->meta_superblock, bs->dsk.meta_block_size))
|
||||||
{
|
{
|
||||||
{
|
assert(bs->dsk.meta_format == BLOCKSTORE_META_FORMAT_HEAP);
|
||||||
blockstore_meta_header_v3_t *hdr = (blockstore_meta_header_v3_t *)bs->meta_superblock;
|
blockstore_meta_header_v3_t *hdr = (blockstore_meta_header_v3_t *)bs->meta_superblock;
|
||||||
hdr->zero = 0;
|
hdr->zero = 0;
|
||||||
hdr->magic = BLOCKSTORE_META_MAGIC_V1;
|
hdr->magic = BLOCKSTORE_META_MAGIC_V1;
|
||||||
hdr->version = bs->dsk.meta_format;
|
hdr->version = bs->dsk.meta_format;
|
||||||
hdr->meta_block_size = bs->dsk.meta_block_size;
|
hdr->meta_block_size = bs->dsk.meta_block_size;
|
||||||
hdr->data_block_size = bs->dsk.data_block_size;
|
hdr->data_block_size = bs->dsk.data_block_size;
|
||||||
hdr->bitmap_granularity = bs->dsk.bitmap_granularity;
|
hdr->bitmap_granularity = bs->dsk.bitmap_granularity;
|
||||||
if (bs->dsk.meta_format >= BLOCKSTORE_META_FORMAT_V2)
|
hdr->completed_lsn = 0;
|
||||||
{
|
hdr->data_csum_type = bs->dsk.data_csum_type;
|
||||||
hdr->data_csum_type = bs->dsk.data_csum_type;
|
hdr->csum_block_size = bs->dsk.csum_block_size;
|
||||||
hdr->csum_block_size = bs->dsk.csum_block_size;
|
hdr->meta_area_size = bs->dsk.meta_area_size;
|
||||||
}
|
hdr->set_crc32c();
|
||||||
if (bs->dsk.meta_format >= BLOCKSTORE_META_FORMAT_HEAP)
|
|
||||||
{
|
|
||||||
hdr->meta_area_size = bs->dsk.meta_area_size;
|
|
||||||
}
|
|
||||||
hdr->set_crc32c();
|
|
||||||
}
|
|
||||||
if (bs->readonly)
|
if (bs->readonly)
|
||||||
{
|
{
|
||||||
printf("Skipping metadata initialization because blockstore is readonly\n");
|
printf("Skipping metadata initialization because blockstore is readonly\n");
|
||||||
@@ -98,21 +106,8 @@ resume_1:
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
printf("Initializing metadata area\n");
|
printf("Initializing metadata area\n");
|
||||||
GET_SQE();
|
|
||||||
last_read_offset = 0;
|
|
||||||
data->iov = (struct iovec){ bs->meta_superblock, (size_t)bs->dsk.meta_block_size };
|
|
||||||
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
|
|
||||||
io_uring_prep_writev(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset);
|
|
||||||
bs->ringloop->submit();
|
|
||||||
submitted++;
|
|
||||||
resume_2:
|
|
||||||
if (submitted > 0)
|
|
||||||
{
|
|
||||||
wait_state = 2;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
zero_on_init = true;
|
|
||||||
}
|
}
|
||||||
|
zero_on_init = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -153,9 +148,17 @@ resume_1:
|
|||||||
);
|
);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
uint32_t csum = hdr->header_csum;
|
||||||
|
hdr->header_csum = 0;
|
||||||
|
if (crc32c(0, hdr, sizeof(*hdr)) != csum)
|
||||||
|
{
|
||||||
|
printf("Metadata header is corrupt (checksum mismatch).\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
hdr->header_csum = csum;
|
||||||
}
|
}
|
||||||
bs->heap->start_load(((blockstore_meta_header_v3_t *)bs->meta_superblock)->completed_lsn);
|
bs->heap->start_load(((blockstore_meta_header_v3_t *)bs->meta_superblock)->completed_lsn);
|
||||||
if (bs->dsk.inmemory_journal)
|
if (bs->dsk.inmemory_journal && !zero_on_init)
|
||||||
{
|
{
|
||||||
// Read buffer area
|
// Read buffer area
|
||||||
printf("Reading buffered data\n");
|
printf("Reading buffered data\n");
|
||||||
@@ -167,7 +170,7 @@ resume_1:
|
|||||||
bs->buffer_area + md_offset,
|
bs->buffer_area + md_offset,
|
||||||
(size_t)(bs->dsk.journal_len - md_offset < bs->metadata_buf_size ? bs->dsk.journal_len - md_offset : bs->metadata_buf_size),
|
(size_t)(bs->dsk.journal_len - md_offset < bs->metadata_buf_size ? bs->dsk.journal_len - md_offset : bs->metadata_buf_size),
|
||||||
};
|
};
|
||||||
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
|
data->callback = [this](ring_data_t *data) { handle_event(data, -1, "read buffer area"); };
|
||||||
io_uring_prep_readv(sqe, bs->dsk.journal_fd, &data->iov, 1, bs->dsk.journal_offset + md_offset);
|
io_uring_prep_readv(sqe, bs->dsk.journal_fd, &data->iov, 1, bs->dsk.journal_offset + md_offset);
|
||||||
md_offset += data->iov.iov_len;
|
md_offset += data->iov.iov_len;
|
||||||
submitted++;
|
submitted++;
|
||||||
@@ -186,7 +189,7 @@ resume_3:
|
|||||||
next_offset = md_offset;
|
next_offset = md_offset;
|
||||||
// Read the rest of the metadata
|
// Read the rest of the metadata
|
||||||
resume_4:
|
resume_4:
|
||||||
if (next_offset < bs->dsk.meta_area_size && submitted == 0)
|
if (next_offset < bs->dsk.meta_area_size && submitted == 0 && (!zero_on_init || !bs->readonly))
|
||||||
{
|
{
|
||||||
// Submit one read
|
// Submit one read
|
||||||
for (int i = 0; i < 2; i++)
|
for (int i = 0; i < 2; i++)
|
||||||
@@ -203,12 +206,15 @@ resume_4:
|
|||||||
GET_SQE();
|
GET_SQE();
|
||||||
assert(bufs[i].size <= 0x7fffffff);
|
assert(bufs[i].size <= 0x7fffffff);
|
||||||
data->iov = { bufs[i].buf, (size_t)bufs[i].size };
|
data->iov = { bufs[i].buf, (size_t)bufs[i].size };
|
||||||
data->callback = [this, i](ring_data_t *data) { handle_event(data, i); };
|
|
||||||
if (!zero_on_init)
|
if (!zero_on_init)
|
||||||
|
{
|
||||||
|
data->callback = [this, i](ring_data_t *data) { handle_event(data, i, "read metadata"); };
|
||||||
io_uring_prep_readv(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset + bufs[i].offset);
|
io_uring_prep_readv(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset + bufs[i].offset);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Fill metadata with empty block pattern
|
// Fill metadata with empty block pattern
|
||||||
|
data->callback = [this, i](ring_data_t *data) { handle_event(data, i, "clear metadata"); };
|
||||||
memset(bufs[i].buf, 0, bufs[i].size);
|
memset(bufs[i].buf, 0, bufs[i].size);
|
||||||
for (uint64_t o = 0; o < bufs[i].size; o += bs->dsk.meta_block_size)
|
for (uint64_t o = 0; o < bufs[i].size; o += bs->dsk.meta_block_size)
|
||||||
bs->heap->fill_block_empty_space(bufs[i].buf + o, 0);
|
bs->heap->fill_block_empty_space(bufs[i].buf + o, 0);
|
||||||
@@ -224,11 +230,14 @@ resume_4:
|
|||||||
if (bufs[i].state == INIT_META_READ_DONE)
|
if (bufs[i].state == INIT_META_READ_DONE)
|
||||||
{
|
{
|
||||||
// Handle result
|
// Handle result
|
||||||
uint64_t loaded = 0;
|
if (!zero_on_init)
|
||||||
int r = bs->heap->load_blocks(bufs[i].offset-bs->dsk.meta_block_size, bufs[i].size, bufs[i].buf, bs->skip_corrupted_meta_entries, loaded);
|
{
|
||||||
if (r != 0)
|
uint64_t loaded = 0;
|
||||||
exit(1);
|
int r = bs->heap->load_blocks(bufs[i].offset-bs->dsk.meta_block_size, bufs[i].size, bufs[i].buf, bs->skip_corrupted_meta_entries, loaded);
|
||||||
entries_loaded += loaded;
|
if (r != 0)
|
||||||
|
exit(1);
|
||||||
|
entries_loaded += loaded;
|
||||||
|
}
|
||||||
bufs[i].state = 0;
|
bufs[i].state = 0;
|
||||||
bs->ringloop->wakeup();
|
bs->ringloop->wakeup();
|
||||||
}
|
}
|
||||||
@@ -238,51 +247,47 @@ resume_4:
|
|||||||
wait_state = 4;
|
wait_state = 4;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// metadata read finished
|
// metadata read/clear finished
|
||||||
bs->heap->finish_load();
|
bs->heap->finish_load();
|
||||||
printf("Metadata entries loaded: %ju, rechecking unfinished writes and garbage entries\n", entries_loaded);
|
printf("Metadata entries loaded: %ju, rechecking unfinished writes and garbage entries\n", entries_loaded);
|
||||||
if (zero_on_init && !bs->dsk.disable_meta_fsync)
|
|
||||||
{
|
|
||||||
GET_SQE();
|
|
||||||
io_uring_prep_fsync(sqe, bs->dsk.meta_fd, IORING_FSYNC_DATASYNC);
|
|
||||||
last_read_offset = 0;
|
|
||||||
data->iov = { 0 };
|
|
||||||
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
|
|
||||||
submitted++;
|
|
||||||
bs->ringloop->submit();
|
|
||||||
resume_5:
|
|
||||||
if (submitted > 0)
|
|
||||||
{
|
|
||||||
wait_state = 5;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// asynchronous recheck
|
// asynchronous recheck
|
||||||
resume_6:
|
resume_6:
|
||||||
wait_state = 6;
|
wait_state = 6;
|
||||||
bs->heap->recheck_small_writes([this](bool is_data, uint64_t offset, uint64_t len, uint8_t *buf, std::function<void()> cb)
|
bs->heap->recheck_small_writes([this](bool is_data, uint64_t offset, uint64_t len, std::function<void(uint8_t*)> cb)
|
||||||
{
|
{
|
||||||
if (!buf)
|
if (!len)
|
||||||
{
|
{
|
||||||
wait_state = 7;
|
wait_state = 7;
|
||||||
bs->ringloop->wakeup();
|
bs->ringloop->wakeup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
GET_SQE();
|
recheck_ios.push_back(new blockstore_recheck_io_t(is_data, offset, len, std::move(cb)));
|
||||||
data->iov = (iovec){ buf, len };
|
}, bs->meta_write_recheck_parallelism);
|
||||||
data->callback = [offset, cb](ring_data_t *data)
|
while (recheck_ios.size())
|
||||||
|
{
|
||||||
|
blockstore_recheck_io_t* rc = recheck_ios.back();
|
||||||
|
sqe = bs->get_sqe();
|
||||||
|
if (!sqe)
|
||||||
|
break;
|
||||||
|
if (!rc->buf)
|
||||||
|
rc->buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, rc->len);
|
||||||
|
data = ((ring_data_t*)sqe->user_data);
|
||||||
|
data->iov = (iovec){ rc->buf, rc->len };
|
||||||
|
data->callback = [this, rc](ring_data_t *data)
|
||||||
{
|
{
|
||||||
if (data->res < 0)
|
if (data->res < 0)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Buffer area read failed at offset %ju: %d\n", offset, data->res);
|
fprintf(stderr, "Buffer area read failed at offset %ju: %d\n", rc->offset, data->res);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
cb();
|
rc->cb(rc->buf);
|
||||||
|
free(rc->buf);
|
||||||
|
delete rc;
|
||||||
};
|
};
|
||||||
io_uring_prep_readv(sqe, (is_data ? bs->dsk.data_fd : bs->dsk.journal_fd), &data->iov, 1,
|
io_uring_prep_readv(sqe, (rc->is_data ? bs->dsk.data_fd : bs->dsk.journal_fd), &data->iov, 1,
|
||||||
(is_data ? bs->dsk.data_offset : bs->dsk.journal_offset) + offset);
|
(rc->is_data ? bs->dsk.data_offset : bs->dsk.journal_offset) + rc->offset);
|
||||||
bs->ringloop->submit();
|
recheck_ios.pop_back();
|
||||||
}, bs->meta_write_recheck_parallelism);
|
}
|
||||||
return 1;
|
return 1;
|
||||||
resume_7:
|
resume_7:
|
||||||
if (bs->heap->finish_recheck() != 0)
|
if (bs->heap->finish_recheck() != 0)
|
||||||
@@ -311,7 +316,7 @@ resume_8:
|
|||||||
uint32_t block_num = recheck_mod[i];
|
uint32_t block_num = recheck_mod[i];
|
||||||
uint64_t block_offset = bs->dsk.meta_offset + (uint64_t)(block_num+1) * bs->dsk.meta_block_size;
|
uint64_t block_offset = bs->dsk.meta_offset + (uint64_t)(block_num+1) * bs->dsk.meta_block_size;
|
||||||
data = ((ring_data_t*)sqe->user_data);
|
data = ((ring_data_t*)sqe->user_data);
|
||||||
uint8_t *buf = (uint8_t*)malloc_or_die(bs->dsk.meta_block_size);
|
uint8_t *buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, bs->dsk.meta_block_size);
|
||||||
bs->heap->get_meta_block(block_num, buf);
|
bs->heap->get_meta_block(block_num, buf);
|
||||||
data->iov = { buf, bs->dsk.meta_block_size };
|
data->iov = { buf, bs->dsk.meta_block_size };
|
||||||
data->callback = [this, buf, block_offset](ring_data_t *data)
|
data->callback = [this, buf, block_offset](ring_data_t *data)
|
||||||
@@ -337,6 +342,44 @@ resume_9:
|
|||||||
}
|
}
|
||||||
free(metadata_buffer);
|
free(metadata_buffer);
|
||||||
metadata_buffer = NULL;
|
metadata_buffer = NULL;
|
||||||
|
do_fsync:
|
||||||
|
if (!bs->dsk.disable_meta_fsync && !bs->readonly)
|
||||||
|
{
|
||||||
|
GET_SQE();
|
||||||
|
io_uring_prep_fsync(sqe, bs->dsk.meta_fd, IORING_FSYNC_DATASYNC);
|
||||||
|
last_read_offset = 0;
|
||||||
|
data->iov = { 0 };
|
||||||
|
data->callback = [this](ring_data_t *data) { handle_event(data, -1, "fsync metadata"); };
|
||||||
|
submitted++;
|
||||||
|
bs->ringloop->submit();
|
||||||
|
resume_5:
|
||||||
|
if (submitted > 0)
|
||||||
|
{
|
||||||
|
wait_state = 5;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (zero_on_init && !header_written && !bs->readonly)
|
||||||
|
{
|
||||||
|
GET_SQE();
|
||||||
|
header_written = true;
|
||||||
|
last_read_offset = 0;
|
||||||
|
data->iov = (struct iovec){ bs->meta_superblock, (size_t)bs->dsk.meta_block_size };
|
||||||
|
data->callback = [this](ring_data_t *data) { handle_event(data, -1, "write metadata header"); };
|
||||||
|
io_uring_prep_writev(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset);
|
||||||
|
bs->ringloop->submit();
|
||||||
|
submitted++;
|
||||||
|
resume_2:
|
||||||
|
if (submitted > 0)
|
||||||
|
{
|
||||||
|
wait_state = 2;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!bs->dsk.disable_meta_fsync)
|
||||||
|
{
|
||||||
|
goto do_fsync;
|
||||||
|
}
|
||||||
|
}
|
||||||
printf("Loading finished. Data used: %ju / %ju bytes (%s / %s)\n",
|
printf("Loading finished. Data used: %ju / %ju bytes (%s / %s)\n",
|
||||||
bs->heap->get_data_used_space(), bs->dsk.block_count * bs->dsk.data_block_size,
|
bs->heap->get_data_used_space(), bs->dsk.block_count * bs->dsk.data_block_size,
|
||||||
format_size(bs->heap->get_data_used_space()).c_str(),
|
format_size(bs->heap->get_data_used_space()).c_str(),
|
||||||
|
|||||||
@@ -11,12 +11,15 @@ struct blockstore_init_meta_buf
|
|||||||
int state = 0;
|
int state = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct blockstore_recheck_io_t;
|
||||||
|
|
||||||
class blockstore_init_meta
|
class blockstore_init_meta
|
||||||
{
|
{
|
||||||
blockstore_impl_t *bs;
|
blockstore_impl_t *bs;
|
||||||
int wait_state = 0;
|
int wait_state = 0;
|
||||||
int wait_count = 0;
|
int wait_count = 0;
|
||||||
bool zero_on_init = false;
|
bool zero_on_init = false;
|
||||||
|
bool header_written = false;
|
||||||
void *metadata_buffer = NULL;
|
void *metadata_buffer = NULL;
|
||||||
blockstore_init_meta_buf bufs[2] = {};
|
blockstore_init_meta_buf bufs[2] = {};
|
||||||
int submitted = 0;
|
int submitted = 0;
|
||||||
@@ -27,9 +30,10 @@ class blockstore_init_meta
|
|||||||
uint64_t last_read_offset = 0;
|
uint64_t last_read_offset = 0;
|
||||||
uint64_t entries_loaded = 0;
|
uint64_t entries_loaded = 0;
|
||||||
std::vector<uint32_t> recheck_mod;
|
std::vector<uint32_t> recheck_mod;
|
||||||
|
std::vector<blockstore_recheck_io_t*> recheck_ios;
|
||||||
int i = 0, j = 0;
|
int i = 0, j = 0;
|
||||||
bool handle_meta_block(uint8_t *buf, uint64_t count, uint64_t done_cnt);
|
bool handle_meta_block(uint8_t *buf, uint64_t count, uint64_t done_cnt);
|
||||||
void handle_event(ring_data_t *data, int buf_num);
|
void handle_event(ring_data_t *data, int buf_num, const char *op);
|
||||||
public:
|
public:
|
||||||
blockstore_init_meta(blockstore_impl_t *bs);
|
blockstore_init_meta(blockstore_impl_t *bs);
|
||||||
int loop();
|
int loop();
|
||||||
|
|||||||
@@ -43,6 +43,20 @@
|
|||||||
#define WAIT_SQE 1
|
#define WAIT_SQE 1
|
||||||
// Suspend operation until there are <wait_detail> bytes of free space in the journal on disk
|
// Suspend operation until there are <wait_detail> bytes of free space in the journal on disk
|
||||||
#define WAIT_COMPACTION 2
|
#define WAIT_COMPACTION 2
|
||||||
|
// Suspend operation until LSN <wait_detail> becomes durable
|
||||||
|
#define WAIT_META_FSYNC 3
|
||||||
|
|
||||||
|
// In non-immediate_commit setups bs_heap may require us to write into the same block as the
|
||||||
|
// previous object's entry block. EAGAIN is returned if it's impossible, in that case we
|
||||||
|
// request an fsync and fsync lifts the placement restriction entirely.
|
||||||
|
#define BS_SUBMIT_CHECK_PLACEMENT(res, prev_lsn) \
|
||||||
|
if ((res) == EAGAIN)\
|
||||||
|
{\
|
||||||
|
PRIV(op)->wait_for = WAIT_META_FSYNC;\
|
||||||
|
PRIV(op)->wait_detail = (prev_lsn);\
|
||||||
|
flusher->request_fsync();\
|
||||||
|
return 0;\
|
||||||
|
}
|
||||||
|
|
||||||
#define COPY_BUF_JOURNAL 0x01
|
#define COPY_BUF_JOURNAL 0x01
|
||||||
#define COPY_BUF_DATA 0x02
|
#define COPY_BUF_DATA 0x02
|
||||||
|
|||||||
113
src/blockstore/blockstore_mock.cpp
Normal file
113
src/blockstore/blockstore_mock.cpp
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
// Copyright (c) Vitaliy Filippov, 2019+
|
||||||
|
// License: VNPL-1.1 (see README.md for details)
|
||||||
|
|
||||||
|
#include "blockstore_mock.h"
|
||||||
|
|
||||||
|
blockstore_mock_t::blockstore_mock_t(const blockstore_config_t & config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void blockstore_mock_t::parse_config(blockstore_config_t & config)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void* blockstore_mock_t::reshard_start(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size, uint64_t chunk_limit)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blockstore_mock_t::reshard_continue(void *reshard_state, uint64_t chunk_limit)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void blockstore_mock_t::loop()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blockstore_mock_t::is_started()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blockstore_mock_t::is_stalled()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blockstore_mock_t::is_safe_to_stop()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void blockstore_mock_t::enqueue_op(blockstore_op_t *op)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int blockstore_mock_t::read_bitmap(object_id oid, uint64_t target_version, void *bitmap, uint64_t *result_version)
|
||||||
|
{
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::map<uint64_t, uint64_t> & blockstore_mock_t::get_inode_space_stats()
|
||||||
|
{
|
||||||
|
return inode_space;
|
||||||
|
}
|
||||||
|
|
||||||
|
void blockstore_mock_t::set_no_inode_stats(const std::vector<uint64_t> & pool_ids)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void blockstore_mock_t::dump_diagnostics()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string blockstore_mock_t::get_op_diag(blockstore_op_t *op)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t blockstore_mock_t::get_block_size()
|
||||||
|
{
|
||||||
|
return block_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t blockstore_mock_t::get_block_count()
|
||||||
|
{
|
||||||
|
return block_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t blockstore_mock_t::get_free_block_count()
|
||||||
|
{
|
||||||
|
return block_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t blockstore_mock_t::get_journal_size()
|
||||||
|
{
|
||||||
|
return 32*1024*1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t blockstore_mock_t::get_bitmap_granularity()
|
||||||
|
{
|
||||||
|
return bitmap_granularity;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t blockstore_mock_t::get_live_entries()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t blockstore_mock_t::get_live_memory()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t blockstore_mock_t::get_garbage_entries()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t blockstore_mock_t::get_garbage_memory()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
39
src/blockstore/blockstore_mock.h
Normal file
39
src/blockstore/blockstore_mock.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// Copyright (c) Vitaliy Filippov, 2019+
|
||||||
|
// License: VNPL-1.1 (see README.md for details)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "blockstore.h"
|
||||||
|
|
||||||
|
class blockstore_mock_t: public blockstore_i
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint32_t block_size = 128*1024;
|
||||||
|
uint32_t bitmap_granularity = 4096;
|
||||||
|
uint64_t block_count = 100*1024*8;
|
||||||
|
std::map<uint64_t, uint64_t> inode_space;
|
||||||
|
|
||||||
|
blockstore_mock_t(const blockstore_config_t & config);
|
||||||
|
void parse_config(blockstore_config_t & config) override;
|
||||||
|
void* reshard_start(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size, uint64_t chunk_limit) override;
|
||||||
|
bool reshard_continue(void *reshard_state, uint64_t chunk_limit) override;
|
||||||
|
void loop() override;
|
||||||
|
bool is_started() override;
|
||||||
|
bool is_stalled() override;
|
||||||
|
bool is_safe_to_stop() override;
|
||||||
|
void enqueue_op(blockstore_op_t *op) override;
|
||||||
|
int read_bitmap(object_id oid, uint64_t target_version, void *bitmap, uint64_t *result_version = NULL) override;
|
||||||
|
const std::map<uint64_t, uint64_t> & get_inode_space_stats() override;
|
||||||
|
void set_no_inode_stats(const std::vector<uint64_t> & pool_ids) override;
|
||||||
|
void dump_diagnostics() override;
|
||||||
|
std::string get_op_diag(blockstore_op_t *op) override;
|
||||||
|
uint32_t get_block_size() override;
|
||||||
|
uint64_t get_block_count() override;
|
||||||
|
uint64_t get_free_block_count() override;
|
||||||
|
uint64_t get_journal_size() override;
|
||||||
|
uint32_t get_bitmap_granularity() override;
|
||||||
|
uint64_t get_live_entries() override;
|
||||||
|
uint64_t get_live_memory() override;
|
||||||
|
uint64_t get_garbage_entries() override;
|
||||||
|
uint64_t get_garbage_memory() override;
|
||||||
|
};
|
||||||
@@ -105,12 +105,12 @@ void blockstore_impl_t::parse_config(blockstore_config_t & config, bool init)
|
|||||||
{
|
{
|
||||||
meta_write_recheck_parallelism = 16;
|
meta_write_recheck_parallelism = 16;
|
||||||
}
|
}
|
||||||
if (immediate_commit != IMMEDIATE_NONE && !dsk.disable_journal_fsync)
|
if (immediate_commit != IMMEDIATE_NONE && (!dsk.disable_journal_fsync || !dsk.disable_meta_fsync))
|
||||||
{
|
{
|
||||||
throw std::runtime_error("immediate_commit requires disable_journal_fsync");
|
throw std::runtime_error("immediate_commit requires disable_journal_fsync and disable_meta_fsync");
|
||||||
}
|
}
|
||||||
if (immediate_commit == IMMEDIATE_ALL && !dsk.disable_data_fsync)
|
if (immediate_commit == IMMEDIATE_ALL && !dsk.disable_data_fsync)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("immediate_commit=all requires disable_journal_fsync and disable_data_fsync");
|
throw std::runtime_error("immediate_commit=all requires disable_data_fsync");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,10 +75,12 @@ int blockstore_impl_t::dequeue_read(blockstore_op_t *op)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (need_skip && wr->type() == BS_HEAP_SMALL_WRITE &&
|
if (need_skip && (wr->type() == BS_HEAP_SMALL_WRITE || wr->type() == BS_HEAP_INTENT_WRITE) &&
|
||||||
wr->small().offset < blk_end && wr->small().offset+wr->small().len > blk_start)
|
wr->small().offset < blk_end && wr->small().offset+wr->small().len > blk_start)
|
||||||
{
|
{
|
||||||
// Small write may mutate big write checksums during flush
|
// Small write may mutate big write checksums during flush, and an intent write
|
||||||
|
// overwrites part of the data block in place right away. Either way the checksum
|
||||||
|
// of a checksum block they only partially cover no longer matches the disk
|
||||||
skip_csum = COPY_BUF_SKIP_CSUM;
|
skip_csum = COPY_BUF_SKIP_CSUM;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -117,6 +119,7 @@ undo_wait:
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
op->retval = 0;
|
op->retval = 0;
|
||||||
|
reads_in_flight.insert(op);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +137,8 @@ int blockstore_impl_t::fulfill_read(blockstore_op_t *op)
|
|||||||
}
|
}
|
||||||
else if ((vec.copy_flags & COPY_BUF_JOURNAL) && dsk.inmemory_journal)
|
else if ((vec.copy_flags & COPY_BUF_JOURNAL) && dsk.inmemory_journal)
|
||||||
{
|
{
|
||||||
memcpy(op->buf + vec.offset - op->offset, buffer_area + vec.disk_loc + vec.disk_offset, vec.len);
|
// disk_loc may be negative - make ubsan happy
|
||||||
|
memcpy(op->buf + vec.offset - op->offset, buffer_area + (vec.disk_loc + vec.disk_offset), vec.len);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -255,7 +259,7 @@ uint32_t blockstore_impl_t::prepare_read_simple(std::vector<copy_buffer_t> & rea
|
|||||||
blk_end = blk_end > wr->small().offset+wr->small().len ? wr->small().offset+wr->small().len : blk_end;
|
blk_end = blk_end > wr->small().offset+wr->small().len ? wr->small().offset+wr->small().len : blk_end;
|
||||||
}
|
}
|
||||||
if ((blk_end-1)/dsk.csum_block_size == blk_start/dsk.csum_block_size ||
|
if ((blk_end-1)/dsk.csum_block_size == blk_start/dsk.csum_block_size ||
|
||||||
blk_end/dsk.csum_block_size == blk_start/dsk.csum_block_size+1 && blk_end != end && blk_start != start ||
|
(blk_end-1)/dsk.csum_block_size == blk_start/dsk.csum_block_size+1 && blk_end != end && blk_start != start ||
|
||||||
blk_end == end && blk_start == start)
|
blk_end == end && blk_start == start)
|
||||||
{
|
{
|
||||||
// single block, exactly two partial blocks, or any number of full blocks
|
// single block, exactly two partial blocks, or any number of full blocks
|
||||||
@@ -404,6 +408,7 @@ void blockstore_impl_t::handle_read_event(ring_data_t *data, blockstore_op_t *op
|
|||||||
op->retval = op->len;
|
op->retval = op->len;
|
||||||
heap->unlock_entry(op->oid);
|
heap->unlock_entry(op->oid);
|
||||||
free_read_buffers(PRIV(op)->read_vec);
|
free_read_buffers(PRIV(op)->read_vec);
|
||||||
|
reads_in_flight.erase(op);
|
||||||
FINISH_OP(op);
|
FINISH_OP(op);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,29 +14,59 @@ int blockstore_impl_t::dequeue_stable(blockstore_op_t *op)
|
|||||||
else if (priv->op_state == 3) goto resume_3;
|
else if (priv->op_state == 3) goto resume_3;
|
||||||
else if (priv->op_state == 4) goto resume_4;
|
else if (priv->op_state == 4) goto resume_4;
|
||||||
else if (priv->op_state == 5) goto resume_5;
|
else if (priv->op_state == 5) goto resume_5;
|
||||||
|
else if (priv->op_state == 6) goto resume_6;
|
||||||
|
else if (priv->op_state == 7) goto resume_7;
|
||||||
assert(!priv->op_state);
|
assert(!priv->op_state);
|
||||||
op->retval = 0;
|
op->retval = 0;
|
||||||
|
PRIV(op)->lsn = 0;
|
||||||
priv->modified_block = priv->modified_block2 = UINT32_MAX;
|
priv->modified_block = priv->modified_block2 = UINT32_MAX;
|
||||||
for (priv->stab_pos = 0; priv->stab_pos < op->len; priv->stab_pos++)
|
for (priv->stab_pos = 0; priv->stab_pos < op->len; priv->stab_pos++)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
auto obj = heap->read_entry(v[priv->stab_pos].oid);
|
priv->obj = heap->read_entry(v[priv->stab_pos].oid);
|
||||||
if (!obj)
|
if (!priv->obj)
|
||||||
{
|
{
|
||||||
op->retval = -ENOENT;
|
op->retval = -ENOENT;
|
||||||
FINISH_OP(op);
|
FINISH_OP(op);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
int res = op->opcode == BS_OP_STABLE
|
priv->modified_block2 = UINT32_MAX;
|
||||||
? heap->add_commit(obj, v[priv->stab_pos].version, &priv->modified_block2)
|
priv->res = op->opcode == BS_OP_STABLE
|
||||||
: heap->add_rollback(obj, v[priv->stab_pos].version, &priv->modified_block2);
|
? heap->add_commit(priv->obj, v[priv->stab_pos].version, &priv->modified_block2)
|
||||||
if (res == EBUSY)
|
: heap->add_rollback(priv->obj, v[priv->stab_pos].version, &priv->modified_block2);
|
||||||
|
if (priv->res != 0)
|
||||||
|
{
|
||||||
|
// Flush the previous modified block on error or nobody may flush it at all
|
||||||
|
resume_6:
|
||||||
|
if (priv->modified_block != UINT32_MAX)
|
||||||
|
{
|
||||||
|
priv->op_state = 6;
|
||||||
|
BS_SUBMIT_CHECK_SQES(1);
|
||||||
|
prepare_meta_block_write(priv->modified_block);
|
||||||
|
resume_7:
|
||||||
|
if (meta_block_is_pending(priv->modified_block))
|
||||||
|
{
|
||||||
|
priv->op_state = 7;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
priv->modified_block = UINT32_MAX;
|
||||||
|
priv->op_state = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BS_SUBMIT_CHECK_PLACEMENT(priv->res, priv->obj->lsn);
|
||||||
|
if (priv->res == EBUSY)
|
||||||
{
|
{
|
||||||
op->retval = -EBUSY;
|
op->retval = -EBUSY;
|
||||||
FINISH_OP(op);
|
FINISH_OP(op);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
if (res == ENOSPC)
|
if (priv->res == ENOENT)
|
||||||
|
{
|
||||||
|
op->retval = -ENOENT;
|
||||||
|
FINISH_OP(op);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
if (priv->res == ENOSPC)
|
||||||
{
|
{
|
||||||
if (!heap->get_to_compact_count())
|
if (!heap->get_to_compact_count())
|
||||||
{
|
{
|
||||||
@@ -45,21 +75,17 @@ int blockstore_impl_t::dequeue_stable(blockstore_op_t *op)
|
|||||||
FINISH_OP(op);
|
FINISH_OP(op);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
if (priv->modified_block2 != UINT32_MAX)
|
|
||||||
{
|
|
||||||
priv->stab_pos--;
|
|
||||||
goto resume_1;
|
|
||||||
}
|
|
||||||
priv->wait_for = WAIT_COMPACTION;
|
priv->wait_for = WAIT_COMPACTION;
|
||||||
priv->wait_detail = heap->get_compacted_count();
|
priv->wait_detail = heap->get_compacted_count();
|
||||||
flusher->request_trim();
|
flusher->request_trim();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
assert(res == 0);
|
assert(priv->res == 0);
|
||||||
}
|
}
|
||||||
resume_1:
|
resume_1:
|
||||||
if (priv->modified_block != UINT32_MAX && priv->modified_block2 != priv->modified_block)
|
if (priv->modified_block != UINT32_MAX && priv->modified_block2 != priv->modified_block)
|
||||||
{
|
{
|
||||||
|
priv->op_state = 1;
|
||||||
BS_SUBMIT_CHECK_SQES(1);
|
BS_SUBMIT_CHECK_SQES(1);
|
||||||
prepare_meta_block_write(priv->modified_block);
|
prepare_meta_block_write(priv->modified_block);
|
||||||
resume_2:
|
resume_2:
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user