Compare commits
37
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b28e10660 | ||
|
|
f5d19b657a | ||
|
|
82f9515bf1 | ||
|
|
dc13f2dd45 | ||
|
|
d54f73195b | ||
|
|
6c584df3ca | ||
|
|
b2effacbc1 | ||
|
|
440a722827 | ||
|
|
e478139e4d | ||
|
|
7d68443235 | ||
|
|
ff6519a005 | ||
|
|
34a98e4dcc | ||
|
|
1ebac51110 | ||
|
|
2e261ac2cf | ||
|
|
405a8c3f53 | ||
|
|
8d4d49dad3 | ||
|
|
6a646be234 | ||
|
|
bc0aa48bc7 | ||
|
|
df08f4e4d2 | ||
|
|
2669ba96ad | ||
|
|
b5b3c95bfd | ||
|
|
0d5530f89a | ||
|
|
0b32d76f75 | ||
|
|
67132a29ae | ||
|
|
6d22ab77ec | ||
|
|
b2bcf2734c | ||
|
|
04e4fcd14d | ||
|
|
2dbfc8548a | ||
|
|
74a826d0be | ||
|
|
4aa9c8bf2b | ||
|
|
5a51415d2c | ||
|
|
2be8dfbb48 | ||
|
|
49db218f73 | ||
|
|
b04a2d62b2 | ||
|
|
dcd545a1a5 | ||
|
|
553e153c16 | ||
|
|
cc420ca32d |
@@ -234,6 +234,60 @@ jobs:
|
|||||||
echo ""
|
echo ""
|
||||||
done
|
done
|
||||||
|
|
||||||
|
test_etcd_fail_https:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 10
|
||||||
|
run: ETCD_SCHEME=https /root/vitastor/tests/test_etcd_fail.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_etcd_fail_https_antietcd:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 10
|
||||||
|
run: ETCD_SCHEME=https ANTIETCD=1 /root/vitastor/tests/test_etcd_fail.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_snapshot_https:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 3
|
||||||
|
run: ETCD_SCHEME=https /root/vitastor/tests/test_snapshot.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
|
||||||
@@ -648,6 +702,24 @@ jobs:
|
|||||||
echo ""
|
echo ""
|
||||||
done
|
done
|
||||||
|
|
||||||
|
test_snapshot_chain_encrypted:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 3
|
||||||
|
run: ENCRYPTED=1 /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
|
||||||
@@ -1224,6 +1296,24 @@ jobs:
|
|||||||
echo ""
|
echo ""
|
||||||
done
|
done
|
||||||
|
|
||||||
|
test_checksum_xxhash:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 3
|
||||||
|
run: TEST_NAME=xxhash OSD_ARGS="--data_csum_type xxh3_32" /root/vitastor/tests/test_checksum.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_checksum:
|
test_old_checksum:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
needs: build
|
||||||
@@ -2052,3 +2142,39 @@ jobs:
|
|||||||
echo ""
|
echo ""
|
||||||
done
|
done
|
||||||
|
|
||||||
|
test_write_encrypted:
|
||||||
|
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_write_encrypted.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_ec:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||||
|
steps:
|
||||||
|
- name: Run test
|
||||||
|
id: test
|
||||||
|
timeout-minutes: 3
|
||||||
|
run: SCHEME=ec /root/vitastor/tests/test_write_encrypted.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
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,14 @@ for my $line (<>)
|
|||||||
{
|
{
|
||||||
$test_name .= '_antietcd';
|
$test_name .= '_antietcd';
|
||||||
}
|
}
|
||||||
|
elsif ($1 eq 'ETCD_SCHEME' && $2 eq 'https')
|
||||||
|
{
|
||||||
|
$test_name .= '_https';
|
||||||
|
}
|
||||||
|
elsif ($1 eq 'ENCRYPTED')
|
||||||
|
{
|
||||||
|
$test_name .= '_encrypted';
|
||||||
|
}
|
||||||
elsif ($1 eq 'OLD')
|
elsif ($1 eq 'OLD')
|
||||||
{
|
{
|
||||||
$test_name =~ s/^test_/test_old_/s;
|
$test_name =~ s/^test_/test_old_/s;
|
||||||
|
|||||||
@@ -3,3 +3,4 @@
|
|||||||
package-lock.json
|
package-lock.json
|
||||||
fio
|
fio
|
||||||
qemu
|
qemu
|
||||||
|
node_modules
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ Vitastor поддерживает QEMU-драйвер, протоколы UBLK,
|
|||||||
- [Дисковые параметры OSD](docs/config/layout-osd.ru.md)
|
- [Дисковые параметры OSD](docs/config/layout-osd.ru.md)
|
||||||
- [Прочие параметры OSD](docs/config/osd.ru.md)
|
- [Прочие параметры OSD](docs/config/osd.ru.md)
|
||||||
- [Параметры мониторов](docs/config/monitor.ru.md)
|
- [Параметры мониторов](docs/config/monitor.ru.md)
|
||||||
|
- [Безопасность](docs/config/security.ru.md)
|
||||||
- [Настройки пулов](docs/config/pool.ru.md)
|
- [Настройки пулов](docs/config/pool.ru.md)
|
||||||
- [Метаданные образов в etcd](docs/config/inode.ru.md)
|
- [Метаданные образов в etcd](docs/config/inode.ru.md)
|
||||||
- Использование
|
- Использование
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ Read more details in the documentation. You can start from here: [Quick Start](d
|
|||||||
- [OSD Disk Layout](docs/config/layout-osd.en.md)
|
- [OSD Disk Layout](docs/config/layout-osd.en.md)
|
||||||
- [OSD Runtime Parameters](docs/config/osd.en.md)
|
- [OSD Runtime Parameters](docs/config/osd.en.md)
|
||||||
- [Monitor](docs/config/monitor.en.md)
|
- [Monitor](docs/config/monitor.en.md)
|
||||||
|
- [Security](docs/config/security.en.md)
|
||||||
- [Pool configuration](docs/config/pool.en.md)
|
- [Pool configuration](docs/config/pool.en.md)
|
||||||
- [Image metadata in etcd](docs/config/inode.en.md)
|
- [Image metadata in etcd](docs/config/inode.en.md)
|
||||||
- Usage
|
- Usage
|
||||||
|
|||||||
Vendored
+1
-1
@@ -3,7 +3,7 @@ Section: admin
|
|||||||
Priority: optional
|
Priority: optional
|
||||||
Maintainer: Vitaliy Filippov <vitalif@yourcmc.ru>
|
Maintainer: Vitaliy Filippov <vitalif@yourcmc.ru>
|
||||||
Build-Depends: debhelper, g++ (>= 8), libstdc++6 (>= 8),
|
Build-Depends: debhelper, g++ (>= 8), libstdc++6 (>= 8),
|
||||||
linux-libc-dev, libgoogle-perftools-dev, libjerasure-dev, libgf-complete-dev,
|
linux-libc-dev, libgoogle-perftools-dev, libjerasure-dev, libgf-complete-dev, libc-ares-dev,
|
||||||
libibverbs-dev, librdmacm-dev, libisal-dev, cmake, pkg-config, libnl-3-dev, libnl-genl-3-dev,
|
libibverbs-dev, librdmacm-dev, libisal-dev, cmake, pkg-config, libnl-3-dev, libnl-genl-3-dev,
|
||||||
node-bindings <!nocheck>, node-gyp, node-nan
|
node-bindings <!nocheck>, node-gyp, node-nan
|
||||||
Standards-Version: 4.5.0
|
Standards-Version: 4.5.0
|
||||||
|
|||||||
Vendored
+1
-1
@@ -25,7 +25,7 @@ RUN set -e -x; \
|
|||||||
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 \
|
apt-get -y install fio libgoogle-perftools-dev devscripts libjerasure-dev cmake libc-ares-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 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
|
||||||
|
|||||||
@@ -38,3 +38,4 @@ In the future, additional configuration methods may be added:
|
|||||||
- [OSD Disk Layout](config/layout-osd.en.md)
|
- [OSD Disk Layout](config/layout-osd.en.md)
|
||||||
- [OSD Runtime Parameters](config/osd.en.md)
|
- [OSD Runtime Parameters](config/osd.en.md)
|
||||||
- [Monitor](config/monitor.en.md)
|
- [Monitor](config/monitor.en.md)
|
||||||
|
- [Security Parameters](config/security.en.md)
|
||||||
|
|||||||
@@ -41,3 +41,4 @@
|
|||||||
- [Дисковые параметры OSD](config/layout-osd.ru.md)
|
- [Дисковые параметры OSD](config/layout-osd.ru.md)
|
||||||
- [Прочие параметры OSD](config/osd.ru.md)
|
- [Прочие параметры OSD](config/osd.ru.md)
|
||||||
- [Параметры мониторов](config/monitor.ru.md)
|
- [Параметры мониторов](config/monitor.ru.md)
|
||||||
|
- [Параметры безопасности](config/security.ru.md)
|
||||||
|
|||||||
@@ -198,8 +198,14 @@ put a modified value into etcd key /vitastor/config/global.
|
|||||||
- Type: string
|
- Type: string
|
||||||
- Default: none
|
- Default: none
|
||||||
|
|
||||||
Data checksum type to use. May be "crc32c" or "none". Set to "crc32c" to
|
Data and metadata checksum type to use. May be "crc32c", "xxh3_32" or "none".
|
||||||
enable data checksums.
|
Select crc32c or xxh3_32 and set csum_block_size to enable data checksums.
|
||||||
|
|
||||||
|
Both crc32c and xxh3_32 are almost equally fast, xxh3_32 is safer. xxh3_32 is
|
||||||
|
the xxhash3 algorithm truncated from 64 to 32 bits (which is still a good hash).
|
||||||
|
|
||||||
|
Note that enabled data checksums either increase memory usage or reduce
|
||||||
|
performance. Check details in [csum_block_size](#csum_block_size) description.
|
||||||
|
|
||||||
## csum_block_size
|
## csum_block_size
|
||||||
|
|
||||||
|
|||||||
@@ -209,8 +209,12 @@ journal_block_size и meta_block_size. Однако на данный момен
|
|||||||
- Тип: строка
|
- Тип: строка
|
||||||
- Значение по умолчанию: none
|
- Значение по умолчанию: none
|
||||||
|
|
||||||
Тип используемых OSD контрольных сумм данных. Может быть "crc32c" или "none".
|
Тип используемых OSD контрольных сумм данных и метаданных. Может быть "crc32c",
|
||||||
Установите в "crc32c", чтобы включить расчёт и проверку контрольных сумм данных.
|
"xxh3_32" или "none". Выберите crc32c или xxh3_32 и установите csum_block_size,
|
||||||
|
чтобы включить контрольные суммы данных.
|
||||||
|
|
||||||
|
И crc32c, и xxh3_32 примерно одинаково быстры, xxh3_32 надёжней. xxh3_32 - это
|
||||||
|
алгоритм xxhash3, обрезанный с 64 до 32 бит (это всё равно хороший хеш).
|
||||||
|
|
||||||
Следует понимать, что контрольные суммы в зависимости от размера блока их
|
Следует понимать, что контрольные суммы в зависимости от размера блока их
|
||||||
расчёта либо увеличивают потребление памяти, либо снижают производительность.
|
расчёта либо увеличивают потребление памяти, либо снижают производительность.
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
[Documentation](../../README.md#documentation) → [Configuration](../config.en.md) → Security Parameters
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
[Читать на русском](security.ru.md)
|
||||||
|
|
||||||
|
# Security Parameters
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
- [etcd_client_cert](#etcd_client_cert)
|
||||||
|
- [etcd_client_key](#etcd_client_key)
|
||||||
|
- [etcd_ca](#etcd_ca)
|
||||||
|
- [osd_etcd_client_cert](#osd_etcd_client_cert)
|
||||||
|
- [osd_etcd_client_key](#osd_etcd_client_key)
|
||||||
|
- [mon_etcd_client_cert](#mon_etcd_client_cert)
|
||||||
|
- [mon_etcd_client_key](#mon_etcd_client_key)
|
||||||
|
- [vault_url](#vault_url)
|
||||||
|
- [vault_secret_api_path](#vault_secret_api_path)
|
||||||
|
- [vault_client_cert](#vault_client_cert)
|
||||||
|
- [vault_client_key](#vault_client_key)
|
||||||
|
- [vault_ca](#vault_ca)
|
||||||
|
- [vault_timeout_ms](#vault_timeout_ms)
|
||||||
|
- [vault_error_timeout_sec](#vault_error_timeout_sec)
|
||||||
|
- [vault_refresh_leeway_sec](#vault_refresh_leeway_sec)
|
||||||
|
- [max_aes_xts_pool_size](#max_aes_xts_pool_size)
|
||||||
|
|
||||||
|
## etcd_client_cert
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Client TLS certificate to use for Vitastor client (not OSD and not monitor)
|
||||||
|
etcd https connections. May be path to a file or just a PEM string with certificate.
|
||||||
|
In the latter case, string must begin with "-----BEGIN CERTIFICATE-----".
|
||||||
|
|
||||||
|
## etcd_client_key
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Private key for etcd_client_cert (also a file or a PEM string).
|
||||||
|
|
||||||
|
## etcd_ca
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Trusted TLS CA to verify etcd server certificate. May be path to a file,
|
||||||
|
directory or just a PEM string with certificate.
|
||||||
|
|
||||||
|
## osd_etcd_client_cert
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Same as [etcd_client_cert](#etcd_client_cert), but only for OSDs.
|
||||||
|
OSDs, clients and monitors should have different permissions, so they should
|
||||||
|
use different certificates.
|
||||||
|
|
||||||
|
## osd_etcd_client_key
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Same as [etcd_client_key](#etcd_client_key), but only for OSDs.
|
||||||
|
|
||||||
|
## mon_etcd_client_cert
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Same as [etcd_client_cert](#etcd_client_cert), but only for Vitastor monitors.
|
||||||
|
|
||||||
|
## mon_etcd_client_key
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Same as [etcd_client_key](#etcd_client_key), but only for Vitastor monitors.
|
||||||
|
|
||||||
|
## vault_url
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Vault base URL.
|
||||||
|
|
||||||
|
Vitastor clients support AES-256-XTS image data encryption with different per-image keys.
|
||||||
|
Encryption is performed by the client, OSDs don't have access to decrypted data.
|
||||||
|
|
||||||
|
Encryption keys may be stored in etcd or, for the increased security level, in an external
|
||||||
|
[HashiCorp Vault](https://developer.hashicorp.com/vault/) or [OpenBao](https://openbao.org/)
|
||||||
|
instance.
|
||||||
|
|
||||||
|
Vitastor clients use [v1 k/v secrets engine](https://openbao.org/api-docs/secret/kv/kv-v1/)
|
||||||
|
and [TLS authentication engine](https://openbao.org/api-docs/auth/cert/) in Vault.
|
||||||
|
|
||||||
|
In that case, only key IDs are stored in etcd.
|
||||||
|
|
||||||
|
## vault_secret_api_path
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
- Default: /v1/secret/
|
||||||
|
|
||||||
|
Vault v1 secret API mount path to use.
|
||||||
|
|
||||||
|
## vault_client_cert
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Client TLS certificate to use for Vault connections. Just like [etcd_client_cert](#etcd_client_cert),
|
||||||
|
may be path to a file or just a certificate in PEM string.
|
||||||
|
|
||||||
|
## vault_client_key
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Private key for vault_client_cert (also a file or a PEM string).
|
||||||
|
|
||||||
|
## vault_ca
|
||||||
|
|
||||||
|
- Type: string
|
||||||
|
|
||||||
|
Trusted TLS CA to verify Vault server certificate. May be path to a file,
|
||||||
|
directory or just a PEM string with certificate.
|
||||||
|
|
||||||
|
## vault_timeout_ms
|
||||||
|
|
||||||
|
- Type: integer
|
||||||
|
- Default: 5000
|
||||||
|
|
||||||
|
Timeout for Vault requests in milliseconds.
|
||||||
|
|
||||||
|
## vault_error_timeout_sec
|
||||||
|
|
||||||
|
- Type: integer
|
||||||
|
- Default: 60
|
||||||
|
|
||||||
|
Time (in seconds) to wait before retrying after receiving an error from Vault.
|
||||||
|
|
||||||
|
## vault_refresh_leeway_sec
|
||||||
|
|
||||||
|
- Type: integer
|
||||||
|
- Default: 60
|
||||||
|
|
||||||
|
Extra time (in seconds) before real Vault token lease_timeout to refresh it, just
|
||||||
|
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.
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
[Документация](../../README-ru.md#документация) → [Конфигурация](../config.ru.md) → Параметры безопасности
|
||||||
|
|
||||||
|
-----
|
||||||
|
|
||||||
|
[Read in English](security.en.md)
|
||||||
|
|
||||||
|
# Параметры безопасности
|
||||||
|
|
||||||
|
Данные параметры затрагивают безопасность инсталляций Vitastor и используются
|
||||||
|
OSD, мониторами и клиентами.
|
||||||
|
|
||||||
|
Большая их часть может задаваться в /etc/vitastor/vitastor.conf и в etcd, но не
|
||||||
|
поддерживает онлайн-изменение.
|
||||||
|
|
||||||
|
- [etcd_client_cert](#etcd_client_cert)
|
||||||
|
- [etcd_client_key](#etcd_client_key)
|
||||||
|
- [etcd_ca](#etcd_ca)
|
||||||
|
- [osd_etcd_client_cert](#osd_etcd_client_cert)
|
||||||
|
- [osd_etcd_client_key](#osd_etcd_client_key)
|
||||||
|
- [mon_etcd_client_cert](#mon_etcd_client_cert)
|
||||||
|
- [mon_etcd_client_key](#mon_etcd_client_key)
|
||||||
|
- [vault_url](#vault_url)
|
||||||
|
- [vault_secret_api_path](#vault_secret_api_path)
|
||||||
|
- [vault_client_cert](#vault_client_cert)
|
||||||
|
- [vault_client_key](#vault_client_key)
|
||||||
|
- [vault_ca](#vault_ca)
|
||||||
|
- [vault_timeout_ms](#vault_timeout_ms)
|
||||||
|
- [vault_error_timeout_sec](#vault_error_timeout_sec)
|
||||||
|
- [vault_refresh_leeway_sec](#vault_refresh_leeway_sec)
|
||||||
|
- [max_aes_xts_pool_size](#max_aes_xts_pool_size)
|
||||||
|
|
||||||
|
## etcd_client_cert
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Клиентский TLS сертификат для https-подключений к etcd для клиентов Vitastor
|
||||||
|
(не OSD и не мониторов). Может быть путём к файлу или просто строкой с
|
||||||
|
сертификатом в формате PEM. В последнем случае строка должна начинаться с
|
||||||
|
"-----BEGIN CERTIFICATE-----".
|
||||||
|
|
||||||
|
## etcd_client_key
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Закрытый ключ для сертификата etcd_client_cert (также путь к файлу или PEM строка).
|
||||||
|
|
||||||
|
## etcd_ca
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Доверенный корневой TLS-сертификат для проверки сертификата сервера etcd.
|
||||||
|
Может быть путём к файлу, директории или просто строкой с сертификатом в
|
||||||
|
формате PEM.
|
||||||
|
|
||||||
|
## osd_etcd_client_cert
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Аналогично [etcd_client_cert](#etcd_client_cert), но только для OSD.
|
||||||
|
OSD, клиенты и мониторы должны иметь разные привилегии, поэтому они должны
|
||||||
|
использовать разные сертификаты.
|
||||||
|
|
||||||
|
## osd_etcd_client_key
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Аналогично [etcd_client_key](#etcd_client_key), но только для OSD.
|
||||||
|
|
||||||
|
## mon_etcd_client_cert
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Аналогично [etcd_client_cert](#etcd_client_cert), но только для мониторов Vitastor.
|
||||||
|
|
||||||
|
## mon_etcd_client_key
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Аналогично [etcd_client_key](#etcd_client_key), но только для мониторов Vitastor.
|
||||||
|
|
||||||
|
## vault_url
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Базовый адрес Vault.
|
||||||
|
|
||||||
|
Клиенты Vitastor поддерживают AES-256-XTS шифрование данных образов с отдельными ключами на
|
||||||
|
каждый образ. Данные шифруются клиентами, OSD не имеют доступа к незашифрованным данным.
|
||||||
|
|
||||||
|
Ключи шифрования могут храниться в etcd или, для повышенного уровня безопасности, во внешнем
|
||||||
|
[HashiCorp Vault](https://developer.hashicorp.com/vault/) или [OpenBao](https://openbao.org/).
|
||||||
|
|
||||||
|
Клиенты Vitastor используют [движок секретов v1](https://openbao.org/api-docs/secret/kv/kv-v1/)
|
||||||
|
и [TLS-аутентификацию](https://openbao.org/api-docs/auth/cert/) в Vault.
|
||||||
|
|
||||||
|
В этом случае, только ID ключей хранятся в etcd.
|
||||||
|
|
||||||
|
## vault_secret_api_path
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
- Значение по умолчанию: /v1/secret/
|
||||||
|
|
||||||
|
Путь к API секретов v1 для использования клиентами.
|
||||||
|
|
||||||
|
## vault_client_cert
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Клиентский TLS сертификат для подключений к Vault. Как и [etcd_client_cert](#etcd_client_cert),
|
||||||
|
может быть путём к файлу или просто PEM-строкой с сертификатом.
|
||||||
|
|
||||||
|
## vault_client_key
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Закрытый ключ для сертификата vault_client_cert (также путь к файлу или PEM строка).
|
||||||
|
|
||||||
|
## vault_ca
|
||||||
|
|
||||||
|
- Тип: строка
|
||||||
|
|
||||||
|
Доверенный корневой TLS-сертификат для проверки сертификата сервера Vault.
|
||||||
|
Может быть путём к файлу, директории или просто строкой с сертификатом в
|
||||||
|
формате PEM.
|
||||||
|
|
||||||
|
## vault_timeout_ms
|
||||||
|
|
||||||
|
- Тип: целое число
|
||||||
|
- Значение по умолчанию: 5000
|
||||||
|
|
||||||
|
Максимально время выполнения Vault-запросов в миллисекундах.
|
||||||
|
|
||||||
|
## vault_error_timeout_sec
|
||||||
|
|
||||||
|
- Тип: целое число
|
||||||
|
- Значение по умолчанию: 60
|
||||||
|
|
||||||
|
Время (в секундах) для ожидания перед повторной попыткой при получении ошибки от Vault.
|
||||||
|
|
||||||
|
## vault_refresh_leeway_sec
|
||||||
|
|
||||||
|
- Тип: целое число
|
||||||
|
- Значение по умолчанию: 60
|
||||||
|
|
||||||
|
Зазор времени (в секундах), чтобы обновлять токены Vault чуть раньше их реального
|
||||||
|
lease_timeout, на случай "ухода" системных часов.
|
||||||
|
|
||||||
|
## max_aes_xts_pool_size
|
||||||
|
|
||||||
|
- Тип: целое число
|
||||||
|
- Значение по умолчанию: 256
|
||||||
|
|
||||||
|
Максимальное количество кэшируемых в памяти OSD контекстов шифрования OpenSSL.
|
||||||
|
Вряд ли требует изменения.
|
||||||
@@ -44,6 +44,8 @@
|
|||||||
|
|
||||||
{{../../config/monitor.en.md|indent=2}}
|
{{../../config/monitor.en.md|indent=2}}
|
||||||
|
|
||||||
|
{{../../config/security.en.md|indent=2}}
|
||||||
|
|
||||||
{{../../config/pool.en.md|indent=2}}
|
{{../../config/pool.en.md|indent=2}}
|
||||||
|
|
||||||
{{../../config/inode.en.md|indent=2}}
|
{{../../config/inode.en.md|indent=2}}
|
||||||
|
|||||||
@@ -44,6 +44,8 @@
|
|||||||
|
|
||||||
{{../../config/monitor.ru.md|indent=2}}
|
{{../../config/monitor.ru.md|indent=2}}
|
||||||
|
|
||||||
|
{{../../config/security.ru.md|indent=2}}
|
||||||
|
|
||||||
{{../../config/pool.ru.md|indent=2}}
|
{{../../config/pool.ru.md|indent=2}}
|
||||||
|
|
||||||
{{../../config/inode.ru.md|indent=2}}
|
{{../../config/inode.ru.md|indent=2}}
|
||||||
|
|||||||
@@ -233,11 +233,21 @@
|
|||||||
type: string
|
type: string
|
||||||
default: none
|
default: none
|
||||||
info: |
|
info: |
|
||||||
Data checksum type to use. May be "crc32c" or "none". Set to "crc32c" to
|
Data and metadata checksum type to use. May be "crc32c", "xxh3_32" or "none".
|
||||||
enable data checksums.
|
Select crc32c or xxh3_32 and set csum_block_size to enable data checksums.
|
||||||
|
|
||||||
|
Both crc32c and xxh3_32 are almost equally fast, xxh3_32 is safer. xxh3_32 is
|
||||||
|
the xxhash3 algorithm truncated from 64 to 32 bits (which is still a good hash).
|
||||||
|
|
||||||
|
Note that enabled data checksums either increase memory usage or reduce
|
||||||
|
performance. Check details in [csum_block_size](#csum_block_size) description.
|
||||||
info_ru: |
|
info_ru: |
|
||||||
Тип используемых OSD контрольных сумм данных. Может быть "crc32c" или "none".
|
Тип используемых OSD контрольных сумм данных и метаданных. Может быть "crc32c",
|
||||||
Установите в "crc32c", чтобы включить расчёт и проверку контрольных сумм данных.
|
"xxh3_32" или "none". Выберите crc32c или xxh3_32 и установите csum_block_size,
|
||||||
|
чтобы включить контрольные суммы данных.
|
||||||
|
|
||||||
|
И crc32c, и xxh3_32 примерно одинаково быстры, xxh3_32 надёжней. xxh3_32 - это
|
||||||
|
алгоритм xxhash3, обрезанный с 64 до 32 бит (это всё равно хороший хеш).
|
||||||
|
|
||||||
Следует понимать, что контрольные суммы в зависимости от размера блока их
|
Следует понимать, что контрольные суммы в зависимости от размера блока их
|
||||||
расчёта либо увеличивают потребление памяти, либо снижают производительность.
|
расчёта либо увеличивают потребление памяти, либо снижают производительность.
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"yaml": "^2.8.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Security Parameters
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Параметры безопасности
|
||||||
|
|
||||||
|
Данные параметры затрагивают безопасность инсталляций Vitastor и используются
|
||||||
|
OSD, мониторами и клиентами.
|
||||||
|
|
||||||
|
Большая их часть может задаваться в /etc/vitastor/vitastor.conf и в etcd, но не
|
||||||
|
поддерживает онлайн-изменение.
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
- name: etcd_client_cert
|
||||||
|
type: string
|
||||||
|
info: |
|
||||||
|
Client TLS certificate to use for Vitastor client (not OSD and not monitor)
|
||||||
|
etcd https connections. May be path to a file or just a PEM string with certificate.
|
||||||
|
In the latter case, string must begin with "-----BEGIN CERTIFICATE-----".
|
||||||
|
info_ru: |
|
||||||
|
Клиентский TLS сертификат для https-подключений к etcd для клиентов Vitastor
|
||||||
|
(не OSD и не мониторов). Может быть путём к файлу или просто строкой с
|
||||||
|
сертификатом в формате PEM. В последнем случае строка должна начинаться с
|
||||||
|
"-----BEGIN CERTIFICATE-----".
|
||||||
|
- name: etcd_client_key
|
||||||
|
type: string
|
||||||
|
info: Private key for etcd_client_cert (also a file or a PEM string).
|
||||||
|
info_ru: Закрытый ключ для сертификата etcd_client_cert (также путь к файлу или PEM строка).
|
||||||
|
- name: etcd_ca
|
||||||
|
type: string
|
||||||
|
info: |
|
||||||
|
Trusted TLS CA to verify etcd server certificate. May be path to a file,
|
||||||
|
directory or just a PEM string with certificate.
|
||||||
|
info_ru: |
|
||||||
|
Доверенный корневой TLS-сертификат для проверки сертификата сервера etcd.
|
||||||
|
Может быть путём к файлу, директории или просто строкой с сертификатом в
|
||||||
|
формате PEM.
|
||||||
|
- name: osd_etcd_client_cert
|
||||||
|
type: string
|
||||||
|
info: |
|
||||||
|
Same as [etcd_client_cert](#etcd_client_cert), but only for OSDs.
|
||||||
|
OSDs, clients and monitors should have different permissions, so they should
|
||||||
|
use different certificates.
|
||||||
|
info_ru: |
|
||||||
|
Аналогично [etcd_client_cert](#etcd_client_cert), но только для OSD.
|
||||||
|
OSD, клиенты и мониторы должны иметь разные привилегии, поэтому они должны
|
||||||
|
использовать разные сертификаты.
|
||||||
|
- name: osd_etcd_client_key
|
||||||
|
type: string
|
||||||
|
info: Same as [etcd_client_key](#etcd_client_key), but only for OSDs.
|
||||||
|
info_ru: Аналогично [etcd_client_key](#etcd_client_key), но только для OSD.
|
||||||
|
- name: mon_etcd_client_cert
|
||||||
|
type: string
|
||||||
|
info: Same as [etcd_client_cert](#etcd_client_cert), but only for Vitastor monitors.
|
||||||
|
info_ru: Аналогично [etcd_client_cert](#etcd_client_cert), но только для мониторов Vitastor.
|
||||||
|
- name: mon_etcd_client_key
|
||||||
|
type: string
|
||||||
|
info: Same as [etcd_client_key](#etcd_client_key), but only for Vitastor monitors.
|
||||||
|
info_ru: Аналогично [etcd_client_key](#etcd_client_key), но только для мониторов Vitastor.
|
||||||
|
- name: vault_url
|
||||||
|
type: string
|
||||||
|
info: |
|
||||||
|
Vault base URL.
|
||||||
|
|
||||||
|
Vitastor clients support AES-256-XTS image data encryption with different per-image keys.
|
||||||
|
Encryption is performed by the client, OSDs don't have access to decrypted data.
|
||||||
|
|
||||||
|
Encryption keys may be stored in etcd or, for the increased security level, in an external
|
||||||
|
[HashiCorp Vault](https://developer.hashicorp.com/vault/) or [OpenBao](https://openbao.org/)
|
||||||
|
instance.
|
||||||
|
|
||||||
|
Vitastor clients use [v1 k/v secrets engine](https://openbao.org/api-docs/secret/kv/kv-v1/)
|
||||||
|
and [TLS authentication engine](https://openbao.org/api-docs/auth/cert/) in Vault.
|
||||||
|
|
||||||
|
In that case, only key IDs are stored in etcd.
|
||||||
|
info_ru: |
|
||||||
|
Базовый адрес Vault.
|
||||||
|
|
||||||
|
Клиенты Vitastor поддерживают AES-256-XTS шифрование данных образов с отдельными ключами на
|
||||||
|
каждый образ. Данные шифруются клиентами, OSD не имеют доступа к незашифрованным данным.
|
||||||
|
|
||||||
|
Ключи шифрования могут храниться в etcd или, для повышенного уровня безопасности, во внешнем
|
||||||
|
[HashiCorp Vault](https://developer.hashicorp.com/vault/) или [OpenBao](https://openbao.org/).
|
||||||
|
|
||||||
|
Клиенты Vitastor используют [движок секретов v1](https://openbao.org/api-docs/secret/kv/kv-v1/)
|
||||||
|
и [TLS-аутентификацию](https://openbao.org/api-docs/auth/cert/) в Vault.
|
||||||
|
|
||||||
|
В этом случае, только ID ключей хранятся в etcd.
|
||||||
|
- name: vault_secret_api_path
|
||||||
|
type: string
|
||||||
|
default: /v1/secret/
|
||||||
|
info: Vault v1 secret API mount path to use.
|
||||||
|
info_ru: Путь к API секретов v1 для использования клиентами.
|
||||||
|
- name: vault_client_cert
|
||||||
|
type: string
|
||||||
|
info: |
|
||||||
|
Client TLS certificate to use for Vault connections. Just like [etcd_client_cert](#etcd_client_cert),
|
||||||
|
may be path to a file or just a certificate in PEM string.
|
||||||
|
info_ru: |
|
||||||
|
Клиентский TLS сертификат для подключений к Vault. Как и [etcd_client_cert](#etcd_client_cert),
|
||||||
|
может быть путём к файлу или просто PEM-строкой с сертификатом.
|
||||||
|
- name: vault_client_key
|
||||||
|
type: string
|
||||||
|
info: Private key for vault_client_cert (also a file or a PEM string).
|
||||||
|
info_ru: Закрытый ключ для сертификата vault_client_cert (также путь к файлу или PEM строка).
|
||||||
|
- name: vault_ca
|
||||||
|
type: string
|
||||||
|
info: |
|
||||||
|
Trusted TLS CA to verify Vault server certificate. May be path to a file,
|
||||||
|
directory or just a PEM string with certificate.
|
||||||
|
info_ru: |
|
||||||
|
Доверенный корневой TLS-сертификат для проверки сертификата сервера Vault.
|
||||||
|
Может быть путём к файлу, директории или просто строкой с сертификатом в
|
||||||
|
формате PEM.
|
||||||
|
- name: vault_timeout_ms
|
||||||
|
type: int
|
||||||
|
default: 5000
|
||||||
|
info: Timeout for Vault requests in milliseconds.
|
||||||
|
info_ru: Максимально время выполнения Vault-запросов в миллисекундах.
|
||||||
|
- name: vault_error_timeout_sec
|
||||||
|
type: int
|
||||||
|
default: 60
|
||||||
|
info: |
|
||||||
|
Time (in seconds) to wait before retrying after receiving an error from Vault.
|
||||||
|
info_ru: |
|
||||||
|
Время (в секундах) для ожидания перед повторной попыткой при получении ошибки от Vault.
|
||||||
|
- name: vault_refresh_leeway_sec
|
||||||
|
type: int
|
||||||
|
default: 60
|
||||||
|
info: |
|
||||||
|
Extra time (in seconds) before real Vault token lease_timeout to refresh it, just
|
||||||
|
in case of system clock drift.
|
||||||
|
info_ru: |
|
||||||
|
Зазор времени (в секундах), чтобы обновлять токены Vault чуть раньше их реального
|
||||||
|
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.
|
||||||
|
Вряд ли требует изменения.
|
||||||
@@ -15,8 +15,8 @@
|
|||||||
- gcc and g++ 8 or newer, clang 10 or newer, or other compiler with C++11 plus
|
- gcc and g++ 8 or newer, clang 10 or newer, or other compiler with C++11 plus
|
||||||
designated initializers support from C++20
|
designated initializers support from C++20
|
||||||
- CMake
|
- CMake
|
||||||
- jerasure headers and libraries
|
- jerasure, c-ares headers and libraries
|
||||||
- ISA-L, libibverbs and librdmacm headers and libraries (optional)
|
- ISA-L, libibverbs, librdmacm, libnl3 headers and libraries (optional)
|
||||||
- tcmalloc (google-perftools-dev)
|
- tcmalloc (google-perftools-dev)
|
||||||
|
|
||||||
## Basic instructions
|
## Basic instructions
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
- gcc и g++ >= 8, либо clang >= 10, либо другой компилятор с поддержкой C++11 плюс
|
- gcc и g++ >= 8, либо clang >= 10, либо другой компилятор с поддержкой C++11 плюс
|
||||||
назначенных инициализаторов (designated initializers) из C++20
|
назначенных инициализаторов (designated initializers) из C++20
|
||||||
- CMake
|
- CMake
|
||||||
- Заголовки и библиотеки jerasure
|
- Заголовки и библиотеки jerasure, c-ares
|
||||||
- Опционально - заголовки и библиотеки ISA-L, libibverbs, librdmacm
|
- Опционально - заголовки и библиотеки ISA-L, libibverbs, librdmacm, libnl3
|
||||||
- tcmalloc (google-perftools-dev)
|
- tcmalloc (google-perftools-dev)
|
||||||
|
|
||||||
## Базовая инструкция
|
## Базовая инструкция
|
||||||
|
|||||||
@@ -41,6 +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)
|
||||||
|
- [AES-256-XTS image encryption](../usage/cli.en.md#create) and [Vault support](../config/security.en.md#vault_url) for key storage
|
||||||
|
|
||||||
## Plugins and tools
|
## Plugins and tools
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +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)
|
||||||
|
- [AES-256-XTS шифрование данных](../usage/cli.ru.md#create) и [поддержка Vault](../config/security.ru.md#vault_url) для хранения ключей
|
||||||
|
|
||||||
## Драйверы и инструменты
|
## Драйверы и инструменты
|
||||||
|
|
||||||
|
|||||||
+21
-7
@@ -125,18 +125,31 @@ bench-kaveri kaveri 10 G 10 G 0 B/s 0 0 0 us 0 B/s 0
|
|||||||
|
|
||||||
## create
|
## create
|
||||||
|
|
||||||
`vitastor-cli create -s|--size <size> [-p|--pool <id|name>] [--parent <parent_name>[@<snapshot>]] <name>`
|
`vitastor-cli create -s|--size SIZE [OPTIONS] <name>`
|
||||||
|
|
||||||
Create an image. You may use K/M/G/T suffixes for `<size>`. If `--parent` is specified,
|
Create an image. Options:
|
||||||
a copy-on-write image clone is created. Parent must be a snapshot (readonly image).
|
|
||||||
Pool must be specified if there is more than one pool.
|
* `-s|--size SIZE` - New image size in bytes or with a K/M/G/T unit suffix.
|
||||||
|
* `-p|--pool POOL` - Specify pool for the new image (may be omitted if there is only 1 pool).
|
||||||
|
* `--parent PARENT` - Create a copy-on-write image clone based on PARENT (or PARENT@SNAPSHOT).
|
||||||
|
If parent is not a snapshot, it must be a read-only 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 vault:ID` - Use an encryption key from an external Vault secret with specified ID.
|
||||||
|
|
||||||
```
|
```
|
||||||
vitastor-cli create --snapshot <snapshot> [-p|--pool <id|name>] <image>
|
vitastor-cli create --snapshot <snapshot> [OPTIONS] <image>
|
||||||
vitastor-cli snap-create [-p|--pool <id|name>] <image>@<snapshot>
|
vitastor-cli snap-create [OPTIONS] <image>@<snapshot>
|
||||||
```
|
```
|
||||||
|
|
||||||
Create a snapshot of image `<name>` (either form can be used). May be used live if only a single writer is active.
|
Create a snapshot of image `<image>`. May be used live if only a single writer is active.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
* `-p|--pool POOL` - Move image to pool POOL, leaving the snapshot in the old pool.
|
||||||
|
* `--enc-key random` - Change image encryption key to a new random AES-256-XTS key.
|
||||||
|
* `--enc-key KEY` - Change image encryption key to a specified key, Vault key or to an empty key.
|
||||||
|
By default, the image retains its old encryption key when taking a snapshot.
|
||||||
|
|
||||||
See also about [how to export snapshots](qemu.en.md#exporting-snapshots).
|
See also about [how to export snapshots](qemu.en.md#exporting-snapshots).
|
||||||
|
|
||||||
@@ -151,6 +164,7 @@ You should resize file system in the image, if present, before shrinking it.
|
|||||||
* `--deleted 1|0` - Set/clear 'deleted image' flag (set automatically during unfinished deletes).
|
* `--deleted 1|0` - Set/clear 'deleted image' flag (set automatically during unfinished deletes).
|
||||||
* `-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`).
|
||||||
|
|
||||||
## dd
|
## dd
|
||||||
|
|
||||||
|
|||||||
+22
-8
@@ -127,19 +127,32 @@ bench-kaveri kaveri 10 G 10 G 0 B/s 0 0 0 us 0 B/s 0
|
|||||||
|
|
||||||
## create
|
## create
|
||||||
|
|
||||||
`vitastor-cli create -s|--size <size> [-p|--pool <id|name>] [--parent <parent_name>[@<snapshot>]] <name>`
|
`vitastor-cli create -s|--size SIZE [ОПЦИИ] <name>`
|
||||||
|
|
||||||
Создать образ. Для размера `<size>` можно использовать суффиксы K/M/G/T (килобайт-мегабайт-гигабайт-терабайт).
|
Создать образ. Опции:
|
||||||
Если указана опция `--parent`, создаётся клон образа. Родитель `<parent_name>[@<snapshot>]` должен быть
|
|
||||||
снимком (или просто немодифицируемым образом). Пул обязательно указывать, если в кластере больше одного пула.
|
* `-s|--size SIZE` - Размер нового образа в байтах или с суффиксом K/M/G/T (кило/мега/гига/терабайт).
|
||||||
|
* `-p|--pool POOL` - Создать образ в заданном пуле (можно не указывать, если пул всего один).
|
||||||
|
* `--parent PARENT` - Создать легковесный клон на основе образа `PARENT` или снимка `PARENT@SNAP`.
|
||||||
|
Если `PARENT` - не снимок, он должен быть помечен как образ только для чтения.
|
||||||
|
* `--enc-key random` - Сгенерировать случайный ключ шифрования AES-256-XTS для нового образа.
|
||||||
|
* `--enc-key HEX` - Установить заданный ключ AES-256-XTS (64 байта в hex) для нового образа.
|
||||||
|
* `--enc-key vault:ID` - Использовать ключ из внешнего секрета с заданным ID из Vault.
|
||||||
|
|
||||||
```
|
```
|
||||||
vitastor-cli create --snapshot <snapshot> [-p|--pool <id|name>] <image>
|
vitastor-cli create --snapshot <snapshot> [ОПЦИИ] <image>
|
||||||
vitastor-cli snap-create [-p|--pool <id|name>] <image>@<snapshot>
|
vitastor-cli snap-create [ОПЦИИ] <image>@<snapshot>
|
||||||
```
|
```
|
||||||
|
|
||||||
Создать снимок образа `<name>` (можно использовать любую форму команды). Снимок можно создавать без остановки
|
Создать снимок образа `<image>` (можно использовать любую форму команды).
|
||||||
клиентов, если пишущий клиент максимум 1.
|
Снимок можно создавать без остановки клиентов, если пишущих клиентов не больше одного.
|
||||||
|
|
||||||
|
Опции:
|
||||||
|
|
||||||
|
* `-p|--pool POOL` - Переместить образ в пул POOL, оставив снимок в старом пуле.
|
||||||
|
* `--enc-key random` - Изменить ключ шифрования образа на новый случайный ключ AES-256-XTS.
|
||||||
|
* `--enc-key KEY` - Изменить ключ шифрования образа на заданный ключ, ключ из Vault или пустой ключ.
|
||||||
|
По умолчанию шифрованные образы сохраняют старый ключ при снятии снимка.
|
||||||
|
|
||||||
Смотрите также информацию о том, [как экспортировать снимки](qemu.ru.md#экспорт-снимков).
|
Смотрите также информацию о том, [как экспортировать снимки](qemu.ru.md#экспорт-снимков).
|
||||||
|
|
||||||
@@ -156,6 +169,7 @@ vitastor-cli snap-create [-p|--pool <id|name>] <image>@<snapshot>
|
|||||||
* `--deleted 1|0` - Установить/снять флаг "образ удалён" (устанавливается при незавершённом удалении).
|
* `--deleted 1|0` - Установить/снять флаг "образ удалён" (устанавливается при незавершённом удалении).
|
||||||
* `-f|--force` - Разрешить уменьшение или перевод в чтение-запись образа, у которого есть клоны.
|
* `-f|--force` - Разрешить уменьшение или перевод в чтение-запись образа, у которого есть клоны.
|
||||||
* `--down-ok` - Разрешить уменьшение, даже если часть данных останется неудалённой на недоступных OSD.
|
* `--down-ok` - Разрешить уменьшение, даже если часть данных останется неудалённой на недоступных OSD.
|
||||||
|
* `--enc-key HEX` - Изменить ключ шифрования образа (разрешено только с `--force`).
|
||||||
|
|
||||||
## dd
|
## dd
|
||||||
|
|
||||||
|
|||||||
+23
-8
@@ -18,7 +18,7 @@ class AntiEtcdAdapter
|
|||||||
cluster = cluster ? (''+(cluster||'')).split(/,+/) : [];
|
cluster = cluster ? (''+(cluster||'')).split(/,+/) : [];
|
||||||
cluster = Object.keys(cluster.reduce((a, url) =>
|
cluster = Object.keys(cluster.reduce((a, url) =>
|
||||||
{
|
{
|
||||||
a[url.toLowerCase().replace(/^(https?:\/\/)/, '').replace(/\/.*$/, '')] = true;
|
a[url.toLowerCase().replace(/^(https?:\/\/)?(.*?)(\/.*)?$/, (m, m1, m2) => (m1||'http://')+m2)] = true;
|
||||||
return a;
|
return a;
|
||||||
}, {}));
|
}, {}));
|
||||||
const cfg_port = config.antietcd_port;
|
const cfg_port = config.antietcd_port;
|
||||||
@@ -26,7 +26,8 @@ class AntiEtcdAdapter
|
|||||||
is_local['0.0.0.0'] = true;
|
is_local['0.0.0.0'] = true;
|
||||||
is_local['::'] = true;
|
is_local['::'] = true;
|
||||||
is_local[''] = true;
|
is_local[''] = true;
|
||||||
const selected = cluster.map(s => s.split(':', 2)).filter(ip => is_local[ip[0]] && (!cfg_port || ip[1] == cfg_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));
|
||||||
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');
|
||||||
@@ -35,16 +36,30 @@ class AntiEtcdAdapter
|
|||||||
else if (selected.length == 1)
|
else if (selected.length == 1)
|
||||||
{
|
{
|
||||||
const antietcd_config = {
|
const antietcd_config = {
|
||||||
ip: selected[0][0],
|
ip: selected[0][1].substr(2),
|
||||||
port: selected[0][1],
|
port: selected[0][2],
|
||||||
data: config.antietcd_data_file || ((config.antietcd_data_dir || '/var/lib/vitastor') + '/mon_'+selected[0][1]+'.json.gz'),
|
cert: config.antietcd_cert,
|
||||||
|
key: config.antietcd_key,
|
||||||
|
ca: config.etcd_ca,
|
||||||
|
data: config.antietcd_data_file || ((config.antietcd_data_dir || '/var/lib/vitastor') + '/mon_'+selected[0][2]+'.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][0]+':'+selected[0][1], // node_id = ip:port
|
node_id: selected[0][1].substr(2)+':'+selected[0][2], // node_id = ip:port
|
||||||
cluster: (cluster.length == 1 ? null : cluster.reduce((a, c) => { a[c] = "http://"+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,
|
||||||
};
|
};
|
||||||
|
if (config.use_auth)
|
||||||
|
{
|
||||||
|
antietcd_config.client_cert_auth = true;
|
||||||
|
antietcd_config.auth_filter = require('./vitastor_auth_filter.js');
|
||||||
|
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)');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
for (const key in config)
|
for (const key in config)
|
||||||
{
|
{
|
||||||
if (key.substr(0, 9) === 'antietcd_')
|
if (key.substr(0, 9) === 'antietcd_')
|
||||||
@@ -169,7 +184,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);
|
const res = await this.antietcd.api(path.replace(/^\/+/, '').replace(/\/+$/, '').replace(/\/+/g, '_'), body, { username: 'root' });
|
||||||
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);
|
||||||
|
|||||||
+27
-6
@@ -1,7 +1,9 @@
|
|||||||
// Copyright (c) Vitaliy Filippov, 2019+
|
// Copyright (c) Vitaliy Filippov, 2019+
|
||||||
// License: VNPL-1.1 (see README.md for details)
|
// License: VNPL-1.1 (see README.md for details)
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
const http = require('http');
|
const http = require('http');
|
||||||
|
const https = require('https');
|
||||||
const WebSocket = require('ws');
|
const WebSocket = require('ws');
|
||||||
const { b64, local_ips } = require('./utils.js');
|
const { b64, local_ips } = require('./utils.js');
|
||||||
|
|
||||||
@@ -15,11 +17,30 @@ class EtcdAdapter
|
|||||||
this.ws = null;
|
this.ws = null;
|
||||||
this.ws_alive = false;
|
this.ws_alive = false;
|
||||||
this.ws_keepalive_timer = null;
|
this.ws_keepalive_timer = null;
|
||||||
|
this.opts = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_config(config)
|
parse_config(config)
|
||||||
{
|
{
|
||||||
this.parse_etcd_addresses(config.etcd_address||config.etcd_url);
|
this.parse_etcd_addresses(config.etcd_address||config.etcd_url);
|
||||||
|
if (config.mon_etcd_client_cert || config.etcd_client_cert)
|
||||||
|
{
|
||||||
|
this.opts.cert = config.mon_etcd_client_cert || config.etcd_client_cert;
|
||||||
|
if (this.opts.cert.substr(0, 5) != '-----')
|
||||||
|
this.opts.cert = fs.readFileSync(this.opts.cert, { encoding: 'utf-8' });
|
||||||
|
}
|
||||||
|
if (config.mon_etcd_client_key || config.etcd_client_key)
|
||||||
|
{
|
||||||
|
this.opts.key = config.mon_etcd_client_key || config.etcd_client_key;
|
||||||
|
if (this.opts.key.substr(0, 5) != '-----')
|
||||||
|
this.opts.key = fs.readFileSync(this.opts.key, { encoding: 'utf-8' });
|
||||||
|
}
|
||||||
|
if (config.etcd_ca)
|
||||||
|
{
|
||||||
|
this.opts.ca = config.etcd_ca;
|
||||||
|
if (this.opts.ca.substr(0, 5) != '-----')
|
||||||
|
this.opts.ca = fs.readFileSync(this.opts.ca, { encoding: 'utf-8' });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_etcd_addresses(addrs)
|
parse_etcd_addresses(addrs)
|
||||||
@@ -39,7 +60,7 @@ class EtcdAdapter
|
|||||||
for (let url of addrs)
|
for (let url of addrs)
|
||||||
{
|
{
|
||||||
let scheme = 'http';
|
let scheme = 'http';
|
||||||
url = url.trim().replace(/^(https?):\/\//, (m, m1) => { scheme = m1; return ''; });
|
url = url.trim().replace(/^(https?):\/\//i, (m, m1) => { scheme = m1.toLowerCase(); return ''; });
|
||||||
const slash = url.indexOf('/');
|
const slash = url.indexOf('/');
|
||||||
const colon = url.indexOf(':');
|
const colon = url.indexOf(':');
|
||||||
const is_local = is_local_ip[colon >= 0 ? url.substr(0, colon) : (slash >= 0 ? url.substr(0, slash) : url)];
|
const is_local = is_local_ip[colon >= 0 ? url.substr(0, colon) : (slash >= 0 ? url.substr(0, slash) : url)];
|
||||||
@@ -130,7 +151,7 @@ class EtcdAdapter
|
|||||||
}
|
}
|
||||||
ok(false);
|
ok(false);
|
||||||
}, this.mon.config.etcd_mon_timeout);
|
}, this.mon.config.etcd_mon_timeout);
|
||||||
this.ws = new WebSocket(base+'/watch');
|
this.ws = new WebSocket(base+'/watch', this.opts);
|
||||||
this.ws_used_url = cur_addr;
|
this.ws_used_url = cur_addr;
|
||||||
const fail = () =>
|
const fail = () =>
|
||||||
{
|
{
|
||||||
@@ -272,7 +293,7 @@ class EtcdAdapter
|
|||||||
{
|
{
|
||||||
throw new Error(MON_STOPPED);
|
throw new Error(MON_STOPPED);
|
||||||
}
|
}
|
||||||
const res = await POST(base+path, body, timeout);
|
const res = await POST(base+path, body, timeout, this.opts);
|
||||||
if (this.mon.stopped)
|
if (this.mon.stopped)
|
||||||
{
|
{
|
||||||
throw new Error(MON_STOPPED);
|
throw new Error(MON_STOPPED);
|
||||||
@@ -298,7 +319,7 @@ class EtcdAdapter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function POST(url, body, timeout)
|
function POST(url, body, timeout, opts)
|
||||||
{
|
{
|
||||||
return new Promise(ok =>
|
return new Promise(ok =>
|
||||||
{
|
{
|
||||||
@@ -310,10 +331,10 @@ function POST(url, body, timeout)
|
|||||||
req = null;
|
req = null;
|
||||||
ok({ error: 'timeout' });
|
ok({ error: 'timeout' });
|
||||||
}, timeout) : null;
|
}, timeout) : null;
|
||||||
let req = http.request(url, { method: 'POST', headers: {
|
let req = (url.substr(0, 5) == 'https' ? https : http).request(url, { method: 'POST', headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Content-Length': body_text.length,
|
'Content-Length': body_text.length,
|
||||||
} }, (res) =>
|
}, ...(opts||{}) }, (res) =>
|
||||||
{
|
{
|
||||||
if (!req)
|
if (!req)
|
||||||
{
|
{
|
||||||
|
|||||||
+22
-1
@@ -16,6 +16,7 @@ const etcd_allow = new RegExp('^'+[
|
|||||||
'config/pools',
|
'config/pools',
|
||||||
'config/osd/[1-9]\\d*',
|
'config/osd/[1-9]\\d*',
|
||||||
'config/pgs', // old name
|
'config/pgs', // old name
|
||||||
|
'config/user/.*',
|
||||||
'pg/config',
|
'pg/config',
|
||||||
'config/inode/[1-9]\\d*/[1-9]\\d*',
|
'config/inode/[1-9]\\d*/[1-9]\\d*',
|
||||||
'osd/state/[1-9]\\d*',
|
'osd/state/[1-9]\\d*',
|
||||||
@@ -45,7 +46,14 @@ const etcd_tree = {
|
|||||||
config_path: "/etc/vitastor/vitastor.conf",
|
config_path: "/etc/vitastor/vitastor.conf",
|
||||||
etcd_prefix: "/vitastor",
|
etcd_prefix: "/vitastor",
|
||||||
// etcd connection - configurable online
|
// etcd connection - configurable online
|
||||||
etcd_address: "10.0.115.10:2379/v3",
|
etcd_address: "http://10.0.115.10:2379/v3",
|
||||||
|
etcd_client_cert: "",
|
||||||
|
etcd_client_key: "",
|
||||||
|
osd_etcd_client_cert: "",
|
||||||
|
osd_etcd_client_key: "",
|
||||||
|
mon_etcd_client_cert: "",
|
||||||
|
mon_etcd_client_key: "",
|
||||||
|
etcd_ca: "",
|
||||||
// mon
|
// mon
|
||||||
etcd_mon_ttl: 5, // min: 1
|
etcd_mon_ttl: 5, // min: 1
|
||||||
etcd_mon_timeout: 1000, // ms. min: 0
|
etcd_mon_timeout: 1000, // ms. min: 0
|
||||||
@@ -201,6 +209,8 @@ const etcd_tree = {
|
|||||||
primary_affinity_tags?: 'nvme' | [ 'nvme', ... ],
|
primary_affinity_tags?: 'nvme' | [ 'nvme', ... ],
|
||||||
// scrub interval
|
// scrub interval
|
||||||
scrub_interval?: '30d',
|
scrub_interval?: '30d',
|
||||||
|
// users allowed to create images in this pool
|
||||||
|
creator_group?: '',
|
||||||
},
|
},
|
||||||
...
|
...
|
||||||
}, */
|
}, */
|
||||||
@@ -217,10 +227,21 @@ const etcd_tree = {
|
|||||||
parent_id?: <inode_t>,
|
parent_id?: <inode_t>,
|
||||||
readonly?: boolean,
|
readonly?: boolean,
|
||||||
deleted?: boolean,
|
deleted?: boolean,
|
||||||
|
enc_key?: string,
|
||||||
|
owner?: string,
|
||||||
|
owner_group?: string,
|
||||||
|
reader_group?: string,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, */
|
}, */
|
||||||
inode: {},
|
inode: {},
|
||||||
|
/* user: {
|
||||||
|
<username>: {
|
||||||
|
type: 'osd'|'mon'|'admin'|'client',
|
||||||
|
groups: string[],
|
||||||
|
},
|
||||||
|
}, */
|
||||||
|
user: {},
|
||||||
},
|
},
|
||||||
osd: {
|
osd: {
|
||||||
state: {
|
state: {
|
||||||
|
|||||||
@@ -0,0 +1,471 @@
|
|||||||
|
// AntiEtcd authentication filter for Vitastor
|
||||||
|
// (c) Vitaliy Filippov, 2026
|
||||||
|
// License: Mozilla Public License 2.0 or Vitastor Network Public License 1.1
|
||||||
|
|
||||||
|
// Permissions are based on:
|
||||||
|
// 1. Users.
|
||||||
|
// Stored in /vitastor/config/user/<username>.
|
||||||
|
// Has 2 properties:
|
||||||
|
// - 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.
|
||||||
|
// 2. Images.
|
||||||
|
// Stored in /vitastor/config/inode/<pool>/<inode>. Has the following properties:
|
||||||
|
// - owner (user name)
|
||||||
|
// - owner_group (group name)
|
||||||
|
// - reader_group
|
||||||
|
|
||||||
|
const static_perms = {
|
||||||
|
invalid: {
|
||||||
|
keys: {},
|
||||||
|
prefixes: {},
|
||||||
|
},
|
||||||
|
osd: {
|
||||||
|
keys: { '/pg/config': false },
|
||||||
|
prefixes: { '/osd/': true, '/pg/state/': true, '/pg/history/': true, '/pgstats/': true },
|
||||||
|
},
|
||||||
|
mon: {
|
||||||
|
keys: { '/pg/config': true, '/stats': true, '/history/last_clean_pgs': true },
|
||||||
|
prefixes: {
|
||||||
|
'/config/': false, '/osd/': false, '/mon/': true, '/pg/history/': true,
|
||||||
|
'/pgstats/': false, '/inode/stats/': true, '/pool/stats/': true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
admin: {
|
||||||
|
keys: { '/stats': false },
|
||||||
|
prefixes: {
|
||||||
|
'/config/': true, '/osd/': true, '/index/': true, '/pg/history/': true,
|
||||||
|
'/mon/': false, '/pg/': false, '/pgstats/': false, '/inode/stats/': false, '/pool/stats/': false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
client: {
|
||||||
|
keys: { '/config/global': false, '/config/node_placement': false, '/config/pools': false, '/pg/config': false },
|
||||||
|
prefixes: { '/osd/stats/': false, '/pg/state/': false, '/index/maxid/': false },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const api_perms = {
|
||||||
|
osd: { lease_grant: true, lease_revoke: true, lease_keepalive: true },
|
||||||
|
mon: { lease_grant: true, lease_revoke: true, lease_keepalive: true },
|
||||||
|
admin: { maintenance_status: true },
|
||||||
|
client: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
class VitastorAuthFilter
|
||||||
|
{
|
||||||
|
constructor(antietcd)
|
||||||
|
{
|
||||||
|
this.cfg = antietcd.cfg;
|
||||||
|
this.antietcd = antietcd;
|
||||||
|
this.prefix = this.cfg.vitastor_prefix || '/vitastor';
|
||||||
|
this.prefix_parts = this.prefix.split('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
_get(path, decode)
|
||||||
|
{
|
||||||
|
let cur = this.antietcd.etctree.state;
|
||||||
|
path = path instanceof Array ? path : path.split('/');
|
||||||
|
for (const p of path)
|
||||||
|
{
|
||||||
|
if (!cur.children)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
cur = cur.children[p];
|
||||||
|
if (!cur)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (decode)
|
||||||
|
{
|
||||||
|
return this._decode(path, cur.value);
|
||||||
|
}
|
||||||
|
return cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
_decode(path, cur)
|
||||||
|
{
|
||||||
|
if (!cur)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (cur)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
cur = JSON.parse(cur);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
console.warn('Invalid JSON in '+(path instanceof Array ? path.join('/') : path)+': '+e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
// userInfo: { name: string, type: string, perms: static_perms[type], groups: { [string]: true } }
|
||||||
|
_check_compare(check, userInfo, checked)
|
||||||
|
{
|
||||||
|
let key = String(check.key);
|
||||||
|
if (key.substr(0, this.prefix.length) !== this.prefix)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
key = key.substr(this.prefix.length);
|
||||||
|
if (key in userInfo.perms.keys)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (const pfx in userInfo.perms.prefixes)
|
||||||
|
{
|
||||||
|
if (key.substr(0, pfx.length) == pfx)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (userInfo.type == 'client')
|
||||||
|
{
|
||||||
|
// Image permissions
|
||||||
|
if (key.substr(0, 14) == '/config/inode/')
|
||||||
|
{
|
||||||
|
// Allowed to check that a key does not exist
|
||||||
|
if (check.target == 'VERSION' && check.version == 0)
|
||||||
|
{
|
||||||
|
checked['M'+key] = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (check.target == 'MOD')
|
||||||
|
{
|
||||||
|
const data = this._get(check.key);
|
||||||
|
if (!data || data.mod_revision != check.mod_revision)
|
||||||
|
{
|
||||||
|
// Break check to trigger CAS failure
|
||||||
|
check.mod_revision = '18446744073709551615'; // UINT64_MAX
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const inode = this._decode(check.key, data.value);
|
||||||
|
if (inode && (inode.owner_group && userInfo.groups[inode.owner_group] ||
|
||||||
|
inode.owner === userInfo.name))
|
||||||
|
{
|
||||||
|
checked['M'+key] = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (key.substr(0, 13) == '/index/image/')
|
||||||
|
{
|
||||||
|
// Allowed to check that a key does not exist
|
||||||
|
if (check.target == 'VERSION' && check.version == 0)
|
||||||
|
{
|
||||||
|
checked['M'+key] = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (check.target == 'MOD')
|
||||||
|
{
|
||||||
|
let data = this._get(check.key);
|
||||||
|
if (!data || data.mod_revision != check.mod_revision)
|
||||||
|
{
|
||||||
|
// Break check to trigger CAS failure
|
||||||
|
check.mod_revision = '18446744073709551615'; // UINT64_MAX
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
data = this._decode(check.key, data.value);
|
||||||
|
if (data)
|
||||||
|
{
|
||||||
|
const inode = this._get([ ...this.prefix_parts, 'config', 'inode', data.pool_id, data.id ], true);
|
||||||
|
if (inode && (inode.owner_group && userInfo.groups[inode.owner_group] ||
|
||||||
|
inode.owner === userInfo.name))
|
||||||
|
{
|
||||||
|
checked['M'+key] = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (key.substr(0, 13) == '/index/maxid/')
|
||||||
|
{
|
||||||
|
const pool_id = key.substr(13);
|
||||||
|
const pool_cfg = this._get([ ...this.prefix_parts, 'config', 'pools' ], true);
|
||||||
|
if (!pool_cfg || !pool_cfg[pool_id] || !pool_cfg[pool_id].creator_group || !userInfo.groups[pool_cfg[pool_id].creator_group])
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (check.target == 'VERSION' && check.version == 0)
|
||||||
|
{
|
||||||
|
checked['I'+parseInt(key.substr(13))+'_0'] = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (check.target == 'MOD')
|
||||||
|
{
|
||||||
|
const data = this._get(check.key);
|
||||||
|
if (!data || data.mod_revision != check.mod_revision)
|
||||||
|
{
|
||||||
|
// Break check to trigger CAS failure
|
||||||
|
check.mod_revision = '18446744073709551615'; // UINT64_MAX
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
checked['I'+parseInt(key.substr(13))+'_'+data.value] = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_check_read(kv, userInfo)
|
||||||
|
{
|
||||||
|
let key = String(kv.key);
|
||||||
|
if (key.substr(0, this.prefix.length) !== this.prefix)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
key = key.substr(this.prefix.length);
|
||||||
|
if (key in userInfo.perms.keys)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (const pfx in userInfo.perms.prefixes)
|
||||||
|
{
|
||||||
|
if (key.substr(0, pfx.length) == pfx)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (userInfo.type == 'client')
|
||||||
|
{
|
||||||
|
// Image permissions
|
||||||
|
if (key.substr(0, 14) == '/config/inode/')
|
||||||
|
{
|
||||||
|
const inode = this._decode(kv.key, kv.value);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
if (key.substr(0, 13) == '/index/image/')
|
||||||
|
{
|
||||||
|
const data = this._decode(kv.key, kv.value);
|
||||||
|
const inode = this._get([ ...this.prefix_parts, 'config', 'inode', data.pool_id, data.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
_check_write(put, userInfo, checked)
|
||||||
|
{
|
||||||
|
let key = String(put.key);
|
||||||
|
if (key.substr(0, this.prefix.length) !== this.prefix)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
key = key.substr(this.prefix.length);
|
||||||
|
if (userInfo.perms.keys[key])
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (const pfx in userInfo.perms.prefixes)
|
||||||
|
{
|
||||||
|
if (userInfo.perms.prefixes[pfx] && key.substr(0, pfx.length) == pfx)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (checked && userInfo.type == 'client')
|
||||||
|
{
|
||||||
|
if (key.substr(0, 13) == '/index/maxid/' &&
|
||||||
|
checked['I'+parseInt(key.substr(13))+'_'+(put.value-1)])
|
||||||
|
{
|
||||||
|
// Allowed to increment maxid
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (checked['M'+key])
|
||||||
|
{
|
||||||
|
// Allowed to modify known images with CAS checks
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_check_req(req, userInfo, checked)
|
||||||
|
{
|
||||||
|
let r;
|
||||||
|
if ((r = (req.request_range || req.requestRange)))
|
||||||
|
{
|
||||||
|
// All range queries are allowed, but responses are filtered - it's simpler
|
||||||
|
}
|
||||||
|
else if ((r = (req.request_put || req.requestPut)))
|
||||||
|
{
|
||||||
|
if (!this._check_write(r, userInfo, checked))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if ((r = (req.request_delete_range || req.requestDeleteRange)))
|
||||||
|
{
|
||||||
|
if (!r.range_end || r.range_end === r.key)
|
||||||
|
{
|
||||||
|
if (!this._check_write({ key: r.key }, userInfo))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// All keys in range must satisfy prefix
|
||||||
|
r.range_end = String(r.range_end);
|
||||||
|
if (r.key.length != r.range_end.length ||
|
||||||
|
r.key[r.key.length-1] != '/' ||
|
||||||
|
r.range_end[r.range_end.length-1] != '0')
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let key = r.key.substr(this.prefix.length);
|
||||||
|
let found = false;
|
||||||
|
for (const pfx in userInfo.perms.prefixes)
|
||||||
|
{
|
||||||
|
if (userInfo.perms.prefixes[pfx] && key.substr(0, pfx.length) == pfx)
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_get_user(username)
|
||||||
|
{
|
||||||
|
if (!username)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let userInfo = this._get([ ...this.prefix_parts, 'config', 'user', username ], true);
|
||||||
|
if (!userInfo)
|
||||||
|
{
|
||||||
|
userInfo = { type: 'client' };
|
||||||
|
}
|
||||||
|
userInfo.perms = static_perms[userInfo.type] || static_perms['invalid'];
|
||||||
|
userInfo.name = username;
|
||||||
|
if (userInfo.groups instanceof Array)
|
||||||
|
{
|
||||||
|
userInfo.groups = userInfo.groups.reduce((a, c) => { a[c] = true; return a; }, {});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
userInfo.groups = {};
|
||||||
|
}
|
||||||
|
return userInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
filter_api(username, api/*, data*/)
|
||||||
|
{
|
||||||
|
if (username === 'root')
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const userInfo = this._get([ ...this.prefix_parts, 'config', 'user', username ], true);
|
||||||
|
return userInfo && api_perms[userInfo.type] && api_perms[userInfo.type][api];
|
||||||
|
}
|
||||||
|
|
||||||
|
filter_txn(username, txn)
|
||||||
|
{
|
||||||
|
if (username === 'root')
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const userInfo = this._get_user(username);
|
||||||
|
if (!userInfo)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const checked = {};
|
||||||
|
if (txn.compare)
|
||||||
|
{
|
||||||
|
for (const check of txn.compare)
|
||||||
|
{
|
||||||
|
if (!this._check_compare(check, userInfo, checked))
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Special transactions:
|
||||||
|
// 1. create image: create config/inode and index/image, increment index/maxid/<pool> (with CAS)
|
||||||
|
// 2. create snapshot: same as create image but also rename previous to @snap
|
||||||
|
if (txn.success)
|
||||||
|
{
|
||||||
|
for (const req of txn.success)
|
||||||
|
{
|
||||||
|
if (!this._check_req(req, userInfo, checked))
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (txn.failure)
|
||||||
|
{
|
||||||
|
for (const req of txn.failure)
|
||||||
|
{
|
||||||
|
if (!this._check_req(req, userInfo, null))
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return txn;
|
||||||
|
}
|
||||||
|
|
||||||
|
filter_txn_response(username, txn, res)
|
||||||
|
{
|
||||||
|
if (!res.responses || username === 'root')
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const userInfo = this._get_user(username);
|
||||||
|
if (!userInfo)
|
||||||
|
{
|
||||||
|
for (const resp of res.responses)
|
||||||
|
{
|
||||||
|
if (resp.response_range && resp.response_range.kvs)
|
||||||
|
{
|
||||||
|
resp.response_range.kvs = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const resp of res.responses)
|
||||||
|
{
|
||||||
|
if (resp.response_range && resp.response_range.kvs)
|
||||||
|
{
|
||||||
|
resp.response_range.kvs = resp.response_range.kvs.filter(kv => this._check_read(kv, userInfo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filter_watch_message(username, msg)
|
||||||
|
{
|
||||||
|
if (!msg.result || !msg.result.events || username === 'root')
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const userInfo = this._get_user(username);
|
||||||
|
if (!userInfo)
|
||||||
|
{
|
||||||
|
msg.result.events = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
msg.result.events = msg.result.events.filter(ev => this._check_read(ev.kv, userInfo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = VitastorAuthFilter;
|
||||||
@@ -11,7 +11,7 @@ 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
|
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 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
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ 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
|
||||||
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}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ RUN yum -y --enablerepo=extras install centos-release-scl epel-release yum-utils
|
|||||||
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-1.el7.noarch.rpm
|
||||||
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
|
fio rh-nodejs12 jerasure-devel libisa-l-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
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ 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
|
||||||
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}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ RUN dnf -y install centos-release-advanced-virtualization epel-release dnf-plugi
|
|||||||
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-1.el8.noarch.rpm
|
||||||
RUN dnf -y install gcc-toolset-9 gcc-toolset-9-gcc-c++ gperftools-devel \
|
RUN dnf -y install gcc-toolset-9 gcc-toolset-9-gcc-c++ gperftools-devel \
|
||||||
fio nodejs rpm-build jerasure-devel libisa-l-devel gf-complete-devel libibverbs-devel libarchive cmake libnl3-devel
|
fio nodejs rpm-build jerasure-devel libisa-l-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
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ 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
|
||||||
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}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ 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
|
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 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
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ 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
|
||||||
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}
|
||||||
|
|||||||
@@ -74,6 +74,14 @@ if (RDMACM_LIBRARIES)
|
|||||||
add_definitions(-DWITH_RDMACM)
|
add_definitions(-DWITH_RDMACM)
|
||||||
endif (RDMACM_LIBRARIES)
|
endif (RDMACM_LIBRARIES)
|
||||||
|
|
||||||
|
find_package(OpenSSL REQUIRED)
|
||||||
|
if (OPENSSL_FOUND)
|
||||||
|
add_definitions(-DWITH_OPENSSL)
|
||||||
|
endif (OPENSSL_FOUND)
|
||||||
|
|
||||||
|
pkg_check_modules(CARES REQUIRED libcares)
|
||||||
|
include_directories(${CARES_INCLUDE_DIRS})
|
||||||
|
|
||||||
if (${WITH_SYSTEM_LIBURING})
|
if (${WITH_SYSTEM_LIBURING})
|
||||||
pkg_check_modules(LIBURING REQUIRED liburing>=2.10)
|
pkg_check_modules(LIBURING REQUIRED liburing>=2.10)
|
||||||
include_directories(${LIBURING_INCLUDE_DIRS})
|
include_directories(${LIBURING_INCLUDE_DIRS})
|
||||||
|
|||||||
@@ -83,13 +83,17 @@ void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config
|
|||||||
{
|
{
|
||||||
data_csum_type = BLOCKSTORE_CSUM_CRC32C;
|
data_csum_type = BLOCKSTORE_CSUM_CRC32C;
|
||||||
}
|
}
|
||||||
|
else if (config["data_csum_type"] == "xxh3_32")
|
||||||
|
{
|
||||||
|
data_csum_type = BLOCKSTORE_CSUM_XXH3_32;
|
||||||
|
}
|
||||||
else if (config["data_csum_type"] == "" || config["data_csum_type"] == "none")
|
else if (config["data_csum_type"] == "" || config["data_csum_type"] == "none")
|
||||||
{
|
{
|
||||||
data_csum_type = BLOCKSTORE_CSUM_NONE;
|
data_csum_type = BLOCKSTORE_CSUM_NONE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw std::runtime_error("data_csum_type="+config["data_csum_type"]+" is unsupported, only \"crc32c\" and \"none\" are supported");
|
throw std::runtime_error("data_csum_type="+config["data_csum_type"]+" is unsupported, only \"crc32c\", \"xxh3_32\" and \"none\" are supported");
|
||||||
}
|
}
|
||||||
csum_block_size = parse_size(config["csum_block_size"]);
|
csum_block_size = parse_size(config["csum_block_size"]);
|
||||||
discard_on_start = config.find("discard_on_start") != config.end() &&
|
discard_on_start = config.find("discard_on_start") != config.end() &&
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#define BLOCKSTORE_CSUM_NONE 0
|
#define BLOCKSTORE_CSUM_NONE 0
|
||||||
// Lower byte of checksum type is its length
|
// Lower byte of checksum type is its length
|
||||||
#define BLOCKSTORE_CSUM_CRC32C 0x104
|
#define BLOCKSTORE_CSUM_CRC32C 0x104
|
||||||
|
#define BLOCKSTORE_CSUM_XXH3_32 0x204
|
||||||
|
|
||||||
#define MOCK_DATA_FD 1000
|
#define MOCK_DATA_FD 1000
|
||||||
#define MOCK_META_FD 1001
|
#define MOCK_META_FD 1001
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "blockstore_heap.h"
|
#include "blockstore_heap.h"
|
||||||
#include "../util/allocator.h"
|
#include "../util/allocator.h"
|
||||||
#include "../util/crc32c.h"
|
#include "../util/crc32c.h"
|
||||||
|
#include "../util/xxh_x86dispatch.h"
|
||||||
#include "../util/malloc_or_die.h"
|
#include "../util/malloc_or_die.h"
|
||||||
|
|
||||||
#define BS_HEAP_FREE_MVCC 1
|
#define BS_HEAP_FREE_MVCC 1
|
||||||
@@ -64,19 +65,19 @@ uint32_t blockstore_heap_t::get_simple_entry_size()
|
|||||||
uint32_t blockstore_heap_t::get_big_entry_size()
|
uint32_t blockstore_heap_t::get_big_entry_size()
|
||||||
{
|
{
|
||||||
return sizeof(heap_big_write_t) + dsk->clean_entry_bitmap_size*2 +
|
return sizeof(heap_big_write_t) + dsk->clean_entry_bitmap_size*2 +
|
||||||
(!dsk->data_csum_type ? 0 : dsk->data_block_size/dsk->csum_block_size * (dsk->data_csum_type & 0xFF));
|
(!dsk->csum_block_size ? 0 : dsk->data_block_size/dsk->csum_block_size * (dsk->data_csum_type & 0xFF));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t blockstore_heap_t::get_big_intent_entry_size()
|
uint32_t blockstore_heap_t::get_big_intent_entry_size()
|
||||||
{
|
{
|
||||||
return sizeof(heap_big_intent_t) + dsk->clean_entry_bitmap_size*2 +
|
return sizeof(heap_big_intent_t) + dsk->clean_entry_bitmap_size*2 +
|
||||||
(!dsk->data_csum_type ? 4 : dsk->data_block_size/dsk->csum_block_size * (dsk->data_csum_type & 0xFF));
|
(!dsk->csum_block_size ? 4 : dsk->data_block_size/dsk->csum_block_size * (dsk->data_csum_type & 0xFF));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t blockstore_heap_t::get_small_entry_size(uint32_t offset, uint32_t len)
|
uint32_t blockstore_heap_t::get_small_entry_size(uint32_t offset, uint32_t len)
|
||||||
{
|
{
|
||||||
return sizeof(heap_small_write_t) + dsk->clean_entry_bitmap_size +
|
return sizeof(heap_small_write_t) + dsk->clean_entry_bitmap_size +
|
||||||
(!dsk->data_csum_type ? 4 : (dsk->data_csum_type & 0xFF) *
|
(!dsk->csum_block_size ? 4 : (dsk->data_csum_type & 0xFF) *
|
||||||
((offset+len+dsk->csum_block_size-1)/dsk->csum_block_size - offset/dsk->csum_block_size));
|
((offset+len+dsk->csum_block_size-1)/dsk->csum_block_size - offset/dsk->csum_block_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +92,7 @@ uint32_t blockstore_heap_t::get_csum_size(heap_entry_t *wr)
|
|||||||
|
|
||||||
uint32_t blockstore_heap_t::get_csum_size(uint32_t entry_type, uint32_t offset, uint32_t len)
|
uint32_t blockstore_heap_t::get_csum_size(uint32_t entry_type, uint32_t offset, uint32_t len)
|
||||||
{
|
{
|
||||||
if (!dsk->data_csum_type)
|
if (!dsk->csum_block_size)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -214,15 +215,24 @@ void heap_entry_t::set_big_location(blockstore_heap_t *heap, uint64_t location)
|
|||||||
big().block_num = location / heap->dsk->data_block_size;
|
big().block_num = location / heap->dsk->data_block_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t heap_entry_t::calc_crc32c()
|
uint32_t heap_entry_t::calc_checksum(blockstore_disk_t *dsk)
|
||||||
{
|
{
|
||||||
auto old_crc32c = crc32c;
|
auto old_checksum = checksum;
|
||||||
crc32c = 0;
|
checksum = 0;
|
||||||
uint32_t res = ::crc32c(0, (uint8_t*)this, size);
|
uint32_t res = 0;
|
||||||
crc32c = old_crc32c;
|
if (dsk->data_csum_type == BLOCKSTORE_CSUM_XXH3_32)
|
||||||
|
res = (uint32_t)XXH3_64bits(this, size);
|
||||||
|
else
|
||||||
|
res = ::crc32c(0, (uint8_t*)this, size);
|
||||||
|
checksum = old_checksum;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t heap_entry_t::calc_checksum(blockstore_heap_t *heap)
|
||||||
|
{
|
||||||
|
return calc_checksum(heap->dsk);
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t blockstore_heap_t::get_pg_id(inode_t inode, uint64_t stripe)
|
uint64_t blockstore_heap_t::get_pg_id(inode_t inode, uint64_t stripe)
|
||||||
{
|
{
|
||||||
uint64_t pg_num = 0;
|
uint64_t pg_num = 0;
|
||||||
@@ -387,12 +397,12 @@ corrupted_object:
|
|||||||
goto corrupted_object;
|
goto corrupted_object;
|
||||||
}
|
}
|
||||||
// Verify crc
|
// Verify crc
|
||||||
uint32_t expected_crc32c = wr->calc_crc32c();
|
uint32_t expected_checksum = wr->calc_checksum(this);
|
||||||
if (wr->crc32c != expected_crc32c)
|
if (wr->checksum != expected_checksum)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Error: entry %jx:%jx v%ju l%ju in metadata block %u at %u is corrupt (crc32c mismatch: expected %08x, got %08x). ",
|
fprintf(stderr, "Error: entry %jx:%jx v%ju l%ju in metadata block %u at %u is corrupt (checksum mismatch: expected %08x, got %08x). ",
|
||||||
wr->inode, wr->stripe, wr->version, wr->lsn,
|
wr->inode, wr->stripe, wr->version, wr->lsn,
|
||||||
block_num, block_offset, expected_crc32c, wr->crc32c);
|
block_num, block_offset, expected_checksum, wr->checksum);
|
||||||
goto corrupted_object;
|
goto corrupted_object;
|
||||||
}
|
}
|
||||||
// Verify offset & len
|
// Verify offset & len
|
||||||
@@ -941,7 +951,11 @@ bool blockstore_heap_t::calc_checksums(heap_entry_t *wr, uint8_t *data, bool set
|
|||||||
len = wr->big_intent().len;
|
len = wr->big_intent().len;
|
||||||
else
|
else
|
||||||
assert(0);
|
assert(0);
|
||||||
uint32_t real_csum = crc32c(0, data, len);
|
uint32_t real_csum = 0;
|
||||||
|
if (dsk->data_csum_type == BLOCKSTORE_CSUM_XXH3_32)
|
||||||
|
real_csum = (uint32_t)XXH3_64bits(data, len);
|
||||||
|
else
|
||||||
|
real_csum = crc32c(0, data, len);
|
||||||
if (set)
|
if (set)
|
||||||
{
|
{
|
||||||
*wr_csum = real_csum;
|
*wr_csum = real_csum;
|
||||||
@@ -991,11 +1005,26 @@ static uint32_t crc32c_iter(uint32_t prev_crc, const std::function<uint8_t*(uint
|
|||||||
return prev_crc;
|
return prev_crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void xxh3_iter(XXH3_state_t* xxh3_state, const std::function<uint8_t*(uint32_t start, uint32_t & len)> & next, uint32_t pos, uint32_t size)
|
||||||
|
{
|
||||||
|
uint32_t cur_len = 0;
|
||||||
|
while (size > 0)
|
||||||
|
{
|
||||||
|
uint8_t *data = next(pos, cur_len);
|
||||||
|
assert(data);
|
||||||
|
cur_len = (cur_len < size ? cur_len : size);
|
||||||
|
XXH3_64bits_update(xxh3_state, data, cur_len);
|
||||||
|
pos += cur_len;
|
||||||
|
size -= cur_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool blockstore_heap_t::calc_block_checksums(uint32_t *block_csums, uint8_t *bitmap,
|
bool blockstore_heap_t::calc_block_checksums(uint32_t *block_csums, uint8_t *bitmap,
|
||||||
uint32_t start, uint32_t end, std::function<uint8_t*(uint32_t start, uint32_t & len)> next,
|
uint32_t start, uint32_t end, std::function<uint8_t*(uint32_t start, uint32_t & len)> next,
|
||||||
bool set, std::function<void(uint32_t, uint32_t, uint32_t)> bad_block_cb)
|
bool set, std::function<void(uint32_t, uint32_t, uint32_t)> bad_block_cb)
|
||||||
{
|
{
|
||||||
bool res = true;
|
bool res = true;
|
||||||
|
XXH3_state_t* xxh3_state = NULL;
|
||||||
uint32_t pos = start;
|
uint32_t pos = start;
|
||||||
uint32_t block_end = (start/dsk->csum_block_size + 1)*dsk->csum_block_size;
|
uint32_t block_end = (start/dsk->csum_block_size + 1)*dsk->csum_block_size;
|
||||||
uint32_t block_crc = 0;
|
uint32_t block_crc = 0;
|
||||||
@@ -1012,42 +1041,89 @@ bool blockstore_heap_t::calc_block_checksums(uint32_t *block_csums, uint8_t *bit
|
|||||||
pos += dsk->bitmap_granularity;
|
pos += dsk->bitmap_granularity;
|
||||||
// zero padding at the beginning or at the end of the block is not counted
|
// zero padding at the beginning or at the end of the block is not counted
|
||||||
if (pos > prev && prev > 0 && pos < block_end)
|
if (pos > prev && prev > 0 && pos < block_end)
|
||||||
block_crc = crc32c_pad(block_crc, NULL, 0, pos-prev, 0);
|
{
|
||||||
|
if (dsk->data_csum_type == BLOCKSTORE_CSUM_XXH3_32)
|
||||||
|
{
|
||||||
|
if (!xxh3_state)
|
||||||
|
{
|
||||||
|
xxh3_state = XXH3_createState();
|
||||||
|
XXH3_64bits_reset(xxh3_state);
|
||||||
|
}
|
||||||
|
uint32_t zeropad = pos-prev;
|
||||||
|
while (zeropad > 0)
|
||||||
|
{
|
||||||
|
uint32_t zerolen = zeropad > 4096 ? 4096 : zeropad;
|
||||||
|
XXH3_64bits_update(xxh3_state, zero_page, zerolen);
|
||||||
|
zeropad -= zerolen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
block_crc = crc32c_pad(block_crc, NULL, 0, pos-prev, 0);
|
||||||
|
}
|
||||||
prev = pos;
|
prev = pos;
|
||||||
while (pos < end && pos < block_end && (bitmap[pos/dsk->bitmap_granularity/8] & (1 << ((pos/dsk->bitmap_granularity) % 8))))
|
while (pos < end && pos < block_end && (bitmap[pos/dsk->bitmap_granularity/8] & (1 << ((pos/dsk->bitmap_granularity) % 8))))
|
||||||
pos += dsk->bitmap_granularity;
|
pos += dsk->bitmap_granularity;
|
||||||
if (pos > prev)
|
if (pos > prev)
|
||||||
{
|
{
|
||||||
isset = true;
|
isset = true;
|
||||||
block_crc = crc32c_iter(block_crc, next, prev, pos-prev);
|
if (dsk->data_csum_type == BLOCKSTORE_CSUM_XXH3_32)
|
||||||
|
{
|
||||||
|
if (!xxh3_state)
|
||||||
|
{
|
||||||
|
xxh3_state = XXH3_createState();
|
||||||
|
XXH3_64bits_reset(xxh3_state);
|
||||||
|
}
|
||||||
|
xxh3_iter(xxh3_state, next, prev, pos-prev);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
block_crc = crc32c_iter(block_crc, next, prev, pos-prev);
|
||||||
}
|
}
|
||||||
prev = pos;
|
prev = pos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
block_crc = crc32c_iter(block_crc, next, pos, (end > block_end ? block_end : end)-pos);
|
if (dsk->data_csum_type == BLOCKSTORE_CSUM_XXH3_32)
|
||||||
|
{
|
||||||
|
if (!xxh3_state)
|
||||||
|
{
|
||||||
|
xxh3_state = XXH3_createState();
|
||||||
|
XXH3_64bits_reset(xxh3_state);
|
||||||
|
}
|
||||||
|
xxh3_iter(xxh3_state, next, pos, (end > block_end ? block_end : end)-pos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
block_crc = crc32c_iter(block_crc, next, pos, (end > block_end ? block_end : end)-pos);
|
||||||
pos = (end > block_end ? block_end : end);
|
pos = (end > block_end ? block_end : end);
|
||||||
isset = true;
|
isset = true;
|
||||||
}
|
}
|
||||||
|
if (dsk->data_csum_type == BLOCKSTORE_CSUM_XXH3_32 && xxh3_state)
|
||||||
|
{
|
||||||
|
block_crc = (uint32_t)XXH3_64bits_digest(xxh3_state);
|
||||||
|
XXH3_64bits_reset(xxh3_state);
|
||||||
|
}
|
||||||
if (set)
|
if (set)
|
||||||
{
|
{
|
||||||
*block_csums = block_crc;
|
*block_csums = block_crc;
|
||||||
}
|
}
|
||||||
else if (isset && block_crc != *block_csums)
|
else if (isset && block_crc != *block_csums)
|
||||||
{
|
{
|
||||||
|
res = false;
|
||||||
if (bad_block_cb)
|
if (bad_block_cb)
|
||||||
{
|
|
||||||
bad_block_cb(blk_start, *block_csums, block_crc);
|
bad_block_cb(blk_start, *block_csums, block_crc);
|
||||||
res = false;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
return false;
|
break;
|
||||||
}
|
}
|
||||||
block_end += dsk->csum_block_size;
|
block_end += dsk->csum_block_size;
|
||||||
block_crc = 0;
|
block_crc = 0;
|
||||||
block_csums++;
|
block_csums++;
|
||||||
}
|
}
|
||||||
|
if (dsk->data_csum_type == BLOCKSTORE_CSUM_XXH3_32 && xxh3_state)
|
||||||
|
{
|
||||||
|
block_crc = (uint32_t)XXH3_64bits_digest(xxh3_state);
|
||||||
|
XXH3_freeState(xxh3_state);
|
||||||
|
xxh3_state = NULL;
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1406,7 +1482,7 @@ int blockstore_heap_t::add_entry(uint32_t wr_size, uint32_t *modified_block,
|
|||||||
insert_list_item(li);
|
insert_list_item(li);
|
||||||
li->block_num = block_num;
|
li->block_num = block_num;
|
||||||
new_wr->size = wr_size;
|
new_wr->size = wr_size;
|
||||||
new_wr->crc32c = new_wr->calc_crc32c();
|
new_wr->checksum = new_wr->calc_checksum(this);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1466,7 +1542,7 @@ int blockstore_heap_t::add_big_write(object_id oid, heap_entry_t *old_head, bool
|
|||||||
memset(wr->get_ext_bitmap(this), 0, dsk->clean_entry_bitmap_size);
|
memset(wr->get_ext_bitmap(this), 0, dsk->clean_entry_bitmap_size);
|
||||||
memset(wr->get_int_bitmap(this), 0, dsk->clean_entry_bitmap_size);
|
memset(wr->get_int_bitmap(this), 0, dsk->clean_entry_bitmap_size);
|
||||||
bitmap_set(wr->get_int_bitmap(this), offset, len, dsk->bitmap_granularity);
|
bitmap_set(wr->get_int_bitmap(this), offset, len, dsk->bitmap_granularity);
|
||||||
if (dsk->data_csum_type)
|
if (dsk->csum_block_size)
|
||||||
{
|
{
|
||||||
memset(wr->get_checksums(this), 0, get_csum_size(wr));
|
memset(wr->get_checksums(this), 0, get_csum_size(wr));
|
||||||
calc_checksums(wr, (uint8_t*)data, true, offset, len);
|
calc_checksums(wr, (uint8_t*)data, true, offset, len);
|
||||||
@@ -1495,7 +1571,7 @@ int blockstore_heap_t::add_redirect_intent(object_id oid, heap_entry_t **obj_ptr
|
|||||||
memset(wr->get_ext_bitmap(this), 0, dsk->clean_entry_bitmap_size);
|
memset(wr->get_ext_bitmap(this), 0, dsk->clean_entry_bitmap_size);
|
||||||
memset(wr->get_int_bitmap(this), 0, dsk->clean_entry_bitmap_size);
|
memset(wr->get_int_bitmap(this), 0, dsk->clean_entry_bitmap_size);
|
||||||
bitmap_set(wr->get_int_bitmap(this), offset, len, dsk->bitmap_granularity);
|
bitmap_set(wr->get_int_bitmap(this), offset, len, dsk->bitmap_granularity);
|
||||||
if (dsk->data_csum_type)
|
if (dsk->csum_block_size)
|
||||||
memset(wr->get_checksums(this), 0, get_csum_size(wr));
|
memset(wr->get_checksums(this), 0, get_csum_size(wr));
|
||||||
calc_checksums(wr, (uint8_t*)data, true);
|
calc_checksums(wr, (uint8_t*)data, true);
|
||||||
*obj_ptr = wr;
|
*obj_ptr = wr;
|
||||||
@@ -1533,7 +1609,7 @@ int blockstore_heap_t::add_big_intent(object_id oid, heap_entry_t **obj_ptr, uin
|
|||||||
memcpy(wr->get_ext_bitmap(this), obj->get_ext_bitmap(this), dsk->clean_entry_bitmap_size);
|
memcpy(wr->get_ext_bitmap(this), obj->get_ext_bitmap(this), dsk->clean_entry_bitmap_size);
|
||||||
memcpy(wr->get_int_bitmap(this), obj->get_int_bitmap(this), dsk->clean_entry_bitmap_size);
|
memcpy(wr->get_int_bitmap(this), obj->get_int_bitmap(this), dsk->clean_entry_bitmap_size);
|
||||||
bitmap_set(wr->get_int_bitmap(this), offset, len, dsk->bitmap_granularity);
|
bitmap_set(wr->get_int_bitmap(this), offset, len, dsk->bitmap_granularity);
|
||||||
if (dsk->data_csum_type)
|
if (dsk->csum_block_size)
|
||||||
{
|
{
|
||||||
if (checksums)
|
if (checksums)
|
||||||
memcpy(wr->get_checksums(this), checksums, get_csum_size(wr));
|
memcpy(wr->get_checksums(this), checksums, get_csum_size(wr));
|
||||||
@@ -1580,7 +1656,7 @@ int blockstore_heap_t::add_compact(heap_entry_t *obj, uint64_t compact_version,
|
|||||||
new_wr->set_big_location(this, compact_location);
|
new_wr->set_big_location(this, compact_location);
|
||||||
memcpy(new_wr->get_int_bitmap(this), new_int_bitmap, dsk->clean_entry_bitmap_size);
|
memcpy(new_wr->get_int_bitmap(this), new_int_bitmap, dsk->clean_entry_bitmap_size);
|
||||||
memcpy(new_wr->get_ext_bitmap(this), new_ext_bitmap, dsk->clean_entry_bitmap_size);
|
memcpy(new_wr->get_ext_bitmap(this), new_ext_bitmap, dsk->clean_entry_bitmap_size);
|
||||||
if (dsk->data_csum_type && new_csums)
|
if (dsk->csum_block_size && new_csums)
|
||||||
memcpy(new_wr->get_checksums(this), new_csums, dsk->data_block_size/dsk->csum_block_size*(dsk->data_csum_type & 0xFF));
|
memcpy(new_wr->get_checksums(this), new_csums, dsk->data_block_size/dsk->csum_block_size*(dsk->data_csum_type & 0xFF));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ struct __attribute__((__packed__)) heap_entry_t
|
|||||||
{
|
{
|
||||||
uint16_t size;
|
uint16_t size;
|
||||||
uint16_t entry_type;
|
uint16_t entry_type;
|
||||||
uint32_t crc32c;
|
uint32_t checksum;
|
||||||
uint64_t lsn;
|
uint64_t lsn;
|
||||||
uint64_t inode;
|
uint64_t inode;
|
||||||
uint64_t stripe;
|
uint64_t stripe;
|
||||||
@@ -69,7 +69,8 @@ struct __attribute__((__packed__)) heap_entry_t
|
|||||||
uint32_t *get_checksum(blockstore_heap_t *heap);
|
uint32_t *get_checksum(blockstore_heap_t *heap);
|
||||||
uint64_t big_location(blockstore_heap_t *heap);
|
uint64_t big_location(blockstore_heap_t *heap);
|
||||||
void set_big_location(blockstore_heap_t *heap, uint64_t location);
|
void set_big_location(blockstore_heap_t *heap, uint64_t location);
|
||||||
uint32_t calc_crc32c();
|
uint32_t calc_checksum(blockstore_heap_t *heap);
|
||||||
|
uint32_t calc_checksum(blockstore_disk_t *dsk);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct __attribute__((__packed__)) heap_small_write_t
|
struct __attribute__((__packed__)) heap_small_write_t
|
||||||
@@ -80,7 +81,7 @@ struct __attribute__((__packed__)) heap_small_write_t
|
|||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
|
|
||||||
// Also includes 1 bitmap and 1 crc32c after the bitmap if checksums are disabled
|
// Also includes 1 bitmap and 1 checksum after the bitmap if block checksums are disabled
|
||||||
};
|
};
|
||||||
|
|
||||||
struct __attribute__((__packed__)) heap_big_write_t
|
struct __attribute__((__packed__)) heap_big_write_t
|
||||||
@@ -98,7 +99,7 @@ struct __attribute__((__packed__)) heap_big_intent_t
|
|||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
|
|
||||||
// Also includes 2 bitmaps and 1 crc32c if checksums are disabled
|
// Also includes 2 bitmaps and 1 checksums if block checksums are disabled
|
||||||
};
|
};
|
||||||
|
|
||||||
struct __attribute__((__packed__)) heap_list_item_t
|
struct __attribute__((__packed__)) heap_list_item_t
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ if (RDMACM_LIBRARIES)
|
|||||||
set(MSGR_RDMACM "msgr_rdmacm.cpp")
|
set(MSGR_RDMACM "msgr_rdmacm.cpp")
|
||||||
endif (RDMACM_LIBRARIES)
|
endif (RDMACM_LIBRARIES)
|
||||||
add_library(vitastor_common STATIC
|
add_library(vitastor_common STATIC
|
||||||
../util/epoll_manager.cpp etcd_state_client.cpp messenger.cpp ../util/addr_util.cpp
|
../util/epoll_manager.cpp etcd_state_client.cpp messenger.cpp ../util/addr_util.cpp ../util/xxh_x86dispatch.c
|
||||||
msgr_stop.cpp msgr_op.cpp msgr_send.cpp msgr_receive.cpp ../util/ringloop.cpp ../../json11/json11.cpp
|
msgr_encrypt.cpp msgr_stop.cpp msgr_op.cpp msgr_send.cpp msgr_receive.cpp ../util/ringloop.cpp ../../json11/json11.cpp
|
||||||
http_client.cpp osd_ops.cpp pg_states.cpp ../util/timerfd_manager.cpp ../util/str_util.cpp ../util/json_util.cpp ${MSGR_RDMA} ${MSGR_RDMACM}
|
http_client.cpp osd_ops.cpp pg_states.cpp ../util/timerfd_manager.cpp ../util/str_util.cpp ../util/json_util.cpp ${MSGR_RDMA} ${MSGR_RDMACM}
|
||||||
)
|
)
|
||||||
target_link_libraries(vitastor_common pthread)
|
target_link_libraries(vitastor_common pthread ${OPENSSL_LIBRARIES} ${CARES_LIBRARIES})
|
||||||
target_compile_options(vitastor_common PUBLIC -fPIC)
|
target_compile_options(vitastor_common PUBLIC -fPIC)
|
||||||
|
|
||||||
# libvitastor_client.so
|
# libvitastor_client.so
|
||||||
@@ -24,6 +24,7 @@ add_library(vitastor_client SHARED
|
|||||||
cluster_client.cpp
|
cluster_client.cpp
|
||||||
cluster_client_list.cpp
|
cluster_client_list.cpp
|
||||||
cluster_client_wb.cpp
|
cluster_client_wb.cpp
|
||||||
|
cluster_client_icache.cpp
|
||||||
vitastor_c.cpp
|
vitastor_c.cpp
|
||||||
)
|
)
|
||||||
set_target_properties(vitastor_client PROPERTIES PUBLIC_HEADER "client/vitastor_c.h")
|
set_target_properties(vitastor_client PROPERTIES PUBLIC_HEADER "client/vitastor_c.h")
|
||||||
@@ -33,6 +34,7 @@ target_link_libraries(vitastor_client
|
|||||||
${LIBURING_LIBRARIES}
|
${LIBURING_LIBRARIES}
|
||||||
${IBVERBS_LIBRARIES}
|
${IBVERBS_LIBRARIES}
|
||||||
${RDMACM_LIBRARIES}
|
${RDMACM_LIBRARIES}
|
||||||
|
${OPENSSL_LIBRARIES}
|
||||||
)
|
)
|
||||||
set_target_properties(vitastor_client PROPERTIES VERSION ${VITASTOR_VERSION} SOVERSION 0)
|
set_target_properties(vitastor_client PROPERTIES VERSION ${VITASTOR_VERSION} SOVERSION 0)
|
||||||
configure_file(vitastor.pc.in vitastor.pc @ONLY)
|
configure_file(vitastor.pc.in vitastor.pc @ONLY)
|
||||||
@@ -98,9 +100,10 @@ endif (${WITH_QEMU})
|
|||||||
add_executable(test_cluster_client
|
add_executable(test_cluster_client
|
||||||
EXCLUDE_FROM_ALL
|
EXCLUDE_FROM_ALL
|
||||||
../test/test_cluster_client.cpp
|
../test/test_cluster_client.cpp
|
||||||
pg_states.cpp osd_ops.cpp cluster_client.cpp cluster_client_list.cpp cluster_client_wb.cpp msgr_op.cpp ../test/mock/messenger.cpp msgr_stop.cpp
|
pg_states.cpp osd_ops.cpp cluster_client.cpp cluster_client_list.cpp cluster_client_wb.cpp cluster_client_icache.cpp msgr_op.cpp ../test/mock/messenger.cpp msgr_stop.cpp msgr_encrypt.cpp
|
||||||
etcd_state_client.cpp ../util/timerfd_manager.cpp ../util/addr_util.cpp ../util/str_util.cpp ../util/json_util.cpp ../../json11/json11.cpp
|
etcd_state_client.cpp ../util/timerfd_manager.cpp ../util/addr_util.cpp ../util/str_util.cpp ../util/json_util.cpp ../util/xxh_x86dispatch.c ../../json11/json11.cpp
|
||||||
)
|
)
|
||||||
|
target_link_libraries(test_cluster_client ${OPENSSL_LIBRARIES})
|
||||||
target_compile_definitions(test_cluster_client PUBLIC -D__MOCK__)
|
target_compile_definitions(test_cluster_client PUBLIC -D__MOCK__)
|
||||||
target_include_directories(test_cluster_client BEFORE PUBLIC ${CMAKE_SOURCE_DIR}/src/test/mock)
|
target_include_directories(test_cluster_client BEFORE PUBLIC ${CMAKE_SOURCE_DIR}/src/test/mock)
|
||||||
add_dependencies(build_tests test_cluster_client)
|
add_dependencies(build_tests test_cluster_client)
|
||||||
|
|||||||
+106
-61
@@ -62,6 +62,7 @@ cluster_client_t::cluster_client_t(ring_loop_t *ringloop, timerfd_manager_t *tfd
|
|||||||
st_cli.on_change_node_placement_hook = [this]() { on_change_node_placement_hook(); };
|
st_cli.on_change_node_placement_hook = [this]() { on_change_node_placement_hook(); };
|
||||||
st_cli.on_load_pgs_hook = [this](bool success) { on_load_pgs_hook(success); };
|
st_cli.on_load_pgs_hook = [this](bool success) { on_load_pgs_hook(success); };
|
||||||
st_cli.on_reload_hook = [this]() { st_cli.load_global_config(); };
|
st_cli.on_reload_hook = [this]() { st_cli.load_global_config(); };
|
||||||
|
st_cli.on_inode_change_hook = [this](uint64_t inode, bool removed) { on_change_inode_hook(inode, removed); };
|
||||||
|
|
||||||
st_cli.parse_config(config);
|
st_cli.parse_config(config);
|
||||||
st_cli.infinite_start = false;
|
st_cli.infinite_start = false;
|
||||||
@@ -70,13 +71,11 @@ cluster_client_t::cluster_client_t(ring_loop_t *ringloop, timerfd_manager_t *tfd
|
|||||||
st_cli.infinite_start = config["client_infinite_start"].bool_value();
|
st_cli.infinite_start = config["client_infinite_start"].bool_value();
|
||||||
}
|
}
|
||||||
st_cli.load_global_config();
|
st_cli.load_global_config();
|
||||||
|
|
||||||
scrap_buffer_size = SCRAP_BUFFER_SIZE;
|
|
||||||
scrap_buffer = malloc_or_die(scrap_buffer_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cluster_client_t::~cluster_client_t()
|
cluster_client_t::~cluster_client_t()
|
||||||
{
|
{
|
||||||
|
vault_destroy();
|
||||||
if (retry_timeout_id >= 0)
|
if (retry_timeout_id >= 0)
|
||||||
{
|
{
|
||||||
tfd->clear_timer(retry_timeout_id);
|
tfd->clear_timer(retry_timeout_id);
|
||||||
@@ -94,7 +93,6 @@ cluster_client_t::~cluster_client_t()
|
|||||||
{
|
{
|
||||||
ringloop->unregister_consumer(&consumer);
|
ringloop->unregister_consumer(&consumer);
|
||||||
}
|
}
|
||||||
free(scrap_buffer);
|
|
||||||
delete wb;
|
delete wb;
|
||||||
wb = NULL;
|
wb = NULL;
|
||||||
}
|
}
|
||||||
@@ -481,6 +479,8 @@ void cluster_client_t::on_load_config_hook(json11::Json::object & etcd_global_co
|
|||||||
self_tree_metrics.clear();
|
self_tree_metrics.clear();
|
||||||
client_hostname = new_hostname;
|
client_hostname = new_hostname;
|
||||||
}
|
}
|
||||||
|
// vault
|
||||||
|
vault_parse_config();
|
||||||
msgr.parse_config(config);
|
msgr.parse_config(config);
|
||||||
st_cli.parse_config(config);
|
st_cli.parse_config(config);
|
||||||
st_cli.load_pgs();
|
st_cli.load_pgs();
|
||||||
@@ -607,6 +607,9 @@ void cluster_client_t::on_change_pool_config_hook()
|
|||||||
pg_counts[pool_item.first] = pool_item.second.real_pg_count;
|
pg_counts[pool_item.first] = pool_item.second.real_pg_count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
inode_cache.clear();
|
||||||
|
inode_cache_children.clear();
|
||||||
|
vault_keys.clear();
|
||||||
continue_ops();
|
continue_ops();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -673,6 +676,10 @@ bool cluster_client_t::flush()
|
|||||||
{
|
{
|
||||||
if (!ringloop)
|
if (!ringloop)
|
||||||
{
|
{
|
||||||
|
if (vault_loading)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (wb->writeback_queue.size())
|
if (wb->writeback_queue.size())
|
||||||
{
|
{
|
||||||
wb->start_writebacks(this, 0);
|
wb->start_writebacks(this, 0);
|
||||||
@@ -695,7 +702,7 @@ bool cluster_client_t::flush()
|
|||||||
sync_done = true;
|
sync_done = true;
|
||||||
};
|
};
|
||||||
execute(sync);
|
execute(sync);
|
||||||
while (!sync_done)
|
while (!sync_done || vault_loading)
|
||||||
{
|
{
|
||||||
ringloop->loop();
|
ringloop->loop();
|
||||||
if (!sync_done)
|
if (!sync_done)
|
||||||
@@ -958,10 +965,40 @@ bool cluster_client_t::check_rw(cluster_op_t *op)
|
|||||||
{
|
{
|
||||||
op->flags |= OP_IMMEDIATE_COMMIT;
|
op->flags |= OP_IMMEDIATE_COMMIT;
|
||||||
}
|
}
|
||||||
|
bool searched = false;
|
||||||
|
std::shared_ptr<inode_cache_t> icache;
|
||||||
|
if (op->opcode == OSD_OP_READ || op->opcode == OSD_OP_WRITE)
|
||||||
|
{
|
||||||
|
if (!searched)
|
||||||
|
{
|
||||||
|
icache = inode_cache_get(op->inode);
|
||||||
|
searched = true;
|
||||||
|
}
|
||||||
|
if (icache && icache->has_parent_loop && op->opcode == OSD_OP_READ)
|
||||||
|
{
|
||||||
|
op->retval = -EINVAL;
|
||||||
|
auto cb = std::move(op->callback);
|
||||||
|
cb(op);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (icache && icache->op_enc)
|
||||||
|
{
|
||||||
|
// Use shared_ptr aliasing to attach op_enc to the inode cache entry
|
||||||
|
op->enc = std::shared_ptr<osd_op_enc_t>(icache, icache->op_enc);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
op->enc.reset();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
op->enc.reset();
|
||||||
if ((op->opcode == OSD_OP_WRITE || op->opcode == OSD_OP_DELETE) && !(op->flags & OSD_OP_IGNORE_READONLY))
|
if ((op->opcode == OSD_OP_WRITE || op->opcode == OSD_OP_DELETE) && !(op->flags & OSD_OP_IGNORE_READONLY))
|
||||||
{
|
{
|
||||||
auto ino_it = st_cli.inode_config.find(op->inode);
|
if (!searched)
|
||||||
if (ino_it != st_cli.inode_config.end() && ino_it->second.readonly)
|
{
|
||||||
|
icache = inode_cache_get(op->inode);
|
||||||
|
searched = true;
|
||||||
|
}
|
||||||
|
if (icache && icache->readonly)
|
||||||
{
|
{
|
||||||
op->retval = -EROFS;
|
op->retval = -EROFS;
|
||||||
auto cb = std::move(op->callback);
|
auto cb = std::move(op->callback);
|
||||||
@@ -972,33 +1009,39 @@ bool cluster_client_t::check_rw(cluster_op_t *op)
|
|||||||
op->deoptimise_snapshot = false;
|
op->deoptimise_snapshot = false;
|
||||||
if (enable_writeback && (op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_BITMAP || op->opcode == OSD_OP_READ_CHAIN_BITMAP))
|
if (enable_writeback && (op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_BITMAP || op->opcode == OSD_OP_READ_CHAIN_BITMAP))
|
||||||
{
|
{
|
||||||
auto ino_it = st_cli.inode_config.find(op->inode);
|
if (!searched)
|
||||||
if (ino_it != st_cli.inode_config.end())
|
|
||||||
{
|
{
|
||||||
int chain_size = 0;
|
icache = inode_cache_get(op->inode);
|
||||||
while (ino_it != st_cli.inode_config.end() && ino_it->second.parent_id)
|
searched = true;
|
||||||
|
}
|
||||||
|
if (icache)
|
||||||
|
{
|
||||||
|
for (auto & parent: icache->chain)
|
||||||
{
|
{
|
||||||
// Check for loops - FIXME check it in etcd_state_client
|
if (INODE_POOL(parent) == INODE_POOL(op->inode) && wb->has_inode(parent))
|
||||||
if (ino_it->second.parent_id == op->inode ||
|
|
||||||
chain_size > st_cli.inode_config.size())
|
|
||||||
{
|
|
||||||
op->retval = -EINVAL;
|
|
||||||
auto cb = std::move(op->callback);
|
|
||||||
cb(op);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (INODE_POOL(ino_it->second.parent_id) == INODE_POOL(ino_it->first) &&
|
|
||||||
wb->has_inode(ino_it->second.parent_id))
|
|
||||||
{
|
{
|
||||||
// Deoptimise reads - we have dirty data for one of the parent layer(s).
|
// Deoptimise reads - we have dirty data for one of the parent layer(s).
|
||||||
op->deoptimise_snapshot = true;
|
op->deoptimise_snapshot = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
chain_size++;
|
|
||||||
ino_it = st_cli.inode_config.find(ino_it->second.parent_id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (icache && icache->err_code)
|
||||||
|
{
|
||||||
|
if (icache->err_code == EPERM)
|
||||||
|
{
|
||||||
|
op->retval = -EPERM;
|
||||||
|
auto cb = std::move(op->callback);
|
||||||
|
cb(op);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (icache->err_code == EAGAIN)
|
||||||
|
{
|
||||||
|
key_wait_ops.push_back(op);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1121,31 +1164,33 @@ resume_2:
|
|||||||
// because if some operations were invalid for the new PG count we'd get errors
|
// because if some operations were invalid for the new PG count we'd get errors
|
||||||
if (op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_CHAIN_BITMAP)
|
if (op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_CHAIN_BITMAP)
|
||||||
{
|
{
|
||||||
// Check parent inode
|
uint64_t next_inode = 0;
|
||||||
auto ino_it = st_cli.inode_config.find(op->cur_inode);
|
auto icache = inode_cache_get(op->cur_inode);
|
||||||
// Skip parents from the same pool
|
if (icache)
|
||||||
int skipped = 0;
|
|
||||||
while (!op->deoptimise_snapshot &&
|
|
||||||
ino_it != st_cli.inode_config.end() && ino_it->second.parent_id &&
|
|
||||||
INODE_POOL(ino_it->second.parent_id) == INODE_POOL(op->cur_inode))
|
|
||||||
{
|
{
|
||||||
// Check for loops - FIXME check it in etcd_state_client
|
if (icache->has_parent_loop)
|
||||||
if (ino_it->second.parent_id == op->inode ||
|
|
||||||
skipped > st_cli.inode_config.size())
|
|
||||||
{
|
{
|
||||||
op->retval = -EINVAL;
|
op->retval = -EINVAL;
|
||||||
erase_op(op);
|
erase_op(op);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
skipped++;
|
if (op->deoptimise_snapshot)
|
||||||
ino_it = st_cli.inode_config.find(ino_it->second.parent_id);
|
{
|
||||||
|
if (icache->chain.size() > 1)
|
||||||
|
next_inode = icache->chain[1];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (icache->other_pool_parent_id)
|
||||||
|
next_inode = icache->other_pool_parent_id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ino_it != st_cli.inode_config.end() &&
|
if (next_inode)
|
||||||
ino_it->second.parent_id &&
|
|
||||||
ino_it->second.parent_id != op->inode)
|
|
||||||
{
|
{
|
||||||
// Continue reading from the parent inode
|
// Continue reading from the parent inode
|
||||||
op->cur_inode = ino_it->second.parent_id;
|
icache = inode_cache_get(next_inode);
|
||||||
|
op->cur_inode = next_inode;
|
||||||
|
op->enc = (icache && icache->op_enc ? std::shared_ptr<osd_op_enc_t>(icache, icache->op_enc) : nullptr);
|
||||||
op->parts.clear();
|
op->parts.clear();
|
||||||
op->done_count = 0;
|
op->done_count = 0;
|
||||||
goto resume_0;
|
goto resume_0;
|
||||||
@@ -1196,7 +1241,7 @@ resume_2:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_iov(int size, bool skip, cluster_op_t *op, int &iov_idx, size_t &iov_pos, osd_op_buf_list_t &iov, void *scrap, int scrap_len)
|
static void add_iov(int size, int skip, cluster_op_t *op, int &iov_idx, size_t &iov_pos, osd_op_buf_list_t &iov)
|
||||||
{
|
{
|
||||||
int left = size;
|
int left = size;
|
||||||
while (left > 0 && iov_idx < op->iov.count)
|
while (left > 0 && iov_idx < op->iov.count)
|
||||||
@@ -1204,7 +1249,7 @@ static void add_iov(int size, bool skip, cluster_op_t *op, int &iov_idx, size_t
|
|||||||
int cur_left = op->iov.buf[iov_idx].iov_len - iov_pos;
|
int cur_left = op->iov.buf[iov_idx].iov_len - iov_pos;
|
||||||
if (cur_left < left)
|
if (cur_left < left)
|
||||||
{
|
{
|
||||||
if (!skip)
|
if (skip == 0)
|
||||||
{
|
{
|
||||||
iov.push_back((uint8_t*)op->iov.buf[iov_idx].iov_base + iov_pos, cur_left);
|
iov.push_back((uint8_t*)op->iov.buf[iov_idx].iov_base + iov_pos, cur_left);
|
||||||
}
|
}
|
||||||
@@ -1214,7 +1259,7 @@ static void add_iov(int size, bool skip, cluster_op_t *op, int &iov_idx, size_t
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!skip)
|
if (skip == 0)
|
||||||
{
|
{
|
||||||
iov.push_back((uint8_t*)op->iov.buf[iov_idx].iov_base + iov_pos, left);
|
iov.push_back((uint8_t*)op->iov.buf[iov_idx].iov_base + iov_pos, left);
|
||||||
}
|
}
|
||||||
@@ -1223,16 +1268,10 @@ static void add_iov(int size, bool skip, cluster_op_t *op, int &iov_idx, size_t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(left == 0);
|
assert(left == 0);
|
||||||
if (skip && scrap_len > 0)
|
if (skip == 1)
|
||||||
{
|
{
|
||||||
// All skipped ranges are read into the same useless buffer
|
// data read into a NULL buffer will be discarded by messenger
|
||||||
left = size;
|
iov.push_back(NULL, size);
|
||||||
while (left > 0)
|
|
||||||
{
|
|
||||||
int cur_left = scrap_len < left ? scrap_len : left;
|
|
||||||
iov.push_back(scrap, cur_left);
|
|
||||||
left -= cur_left;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1252,7 +1291,11 @@ void cluster_client_t::slice_rw(cluster_op_t *op)
|
|||||||
// Allocate memory for the bitmap
|
// Allocate memory for the bitmap
|
||||||
unsigned object_bitmap_size = ((op->len / pool_cfg.bitmap_granularity + 7) / 8);
|
unsigned object_bitmap_size = ((op->len / pool_cfg.bitmap_granularity + 7) / 8);
|
||||||
object_bitmap_size = (object_bitmap_size < 8 ? 8 : object_bitmap_size);
|
object_bitmap_size = (object_bitmap_size < 8 ? 8 : object_bitmap_size);
|
||||||
unsigned bitmap_mem = object_bitmap_size + (pool_cfg.data_block_size / pool_cfg.bitmap_granularity / 8 * pg_data_size) * op->parts.size();
|
unsigned bitmap_mem = object_bitmap_size +
|
||||||
|
op->parts.size() * pg_data_size *
|
||||||
|
(pool_cfg.data_block_size / pool_cfg.bitmap_granularity / 8
|
||||||
|
// read chain info - 1 byte per block
|
||||||
|
+ (op->enc ? op->len/pool_cfg.bitmap_granularity : 0));
|
||||||
if (!op->bitmap_buf || op->bitmap_buf_size < bitmap_mem)
|
if (!op->bitmap_buf || op->bitmap_buf_size < bitmap_mem)
|
||||||
{
|
{
|
||||||
op->bitmap_buf = realloc_or_die(op->bitmap_buf, bitmap_mem);
|
op->bitmap_buf = realloc_or_die(op->bitmap_buf, bitmap_mem);
|
||||||
@@ -1294,10 +1337,10 @@ void cluster_client_t::slice_rw(cluster_op_t *op)
|
|||||||
{
|
{
|
||||||
begin = cur;
|
begin = cur;
|
||||||
// Just advance iov_idx & iov_pos
|
// Just advance iov_idx & iov_pos
|
||||||
add_iov(cur-prev, true, op, iov_idx, iov_pos, op->parts[i].iov, NULL, 0);
|
add_iov(cur-prev, 2, op, iov_idx, iov_pos, op->parts[i].iov);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
add_iov(cur-prev, skip_prev, op, iov_idx, iov_pos, op->parts[i].iov, scrap_buffer, scrap_buffer_size);
|
add_iov(cur-prev, skip_prev ? 1 : 0, op, iov_idx, iov_pos, op->parts[i].iov);
|
||||||
}
|
}
|
||||||
skip_prev = skip;
|
skip_prev = skip;
|
||||||
prev = cur;
|
prev = cur;
|
||||||
@@ -1308,11 +1351,11 @@ void cluster_client_t::slice_rw(cluster_op_t *op)
|
|||||||
if (skip_prev)
|
if (skip_prev)
|
||||||
{
|
{
|
||||||
// Just advance iov_idx & iov_pos
|
// Just advance iov_idx & iov_pos
|
||||||
add_iov(end-prev, true, op, iov_idx, iov_pos, op->parts[i].iov, NULL, 0);
|
add_iov(end-prev, 2, op, iov_idx, iov_pos, op->parts[i].iov);
|
||||||
end = prev;
|
end = prev;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
add_iov(cur-prev, skip_prev, op, iov_idx, iov_pos, op->parts[i].iov, scrap_buffer, scrap_buffer_size);
|
add_iov(cur-prev, skip_prev ? 1 : 0, op, iov_idx, iov_pos, op->parts[i].iov);
|
||||||
if (end == begin)
|
if (end == begin)
|
||||||
{
|
{
|
||||||
op->done_count++;
|
op->done_count++;
|
||||||
@@ -1321,7 +1364,7 @@ void cluster_client_t::slice_rw(cluster_op_t *op)
|
|||||||
}
|
}
|
||||||
else if (op->opcode != OSD_OP_READ_BITMAP && op->opcode != OSD_OP_READ_CHAIN_BITMAP && op->opcode != OSD_OP_DELETE)
|
else if (op->opcode != OSD_OP_READ_BITMAP && op->opcode != OSD_OP_READ_CHAIN_BITMAP && op->opcode != OSD_OP_DELETE)
|
||||||
{
|
{
|
||||||
add_iov(end-begin, false, op, iov_idx, iov_pos, op->parts[i].iov, NULL, 0);
|
add_iov(end-begin, 0, op, iov_idx, iov_pos, op->parts[i].iov);
|
||||||
}
|
}
|
||||||
op->parts[i].parent = op;
|
op->parts[i].parent = op;
|
||||||
op->parts[i].offset = begin;
|
op->parts[i].offset = begin;
|
||||||
@@ -1407,9 +1450,9 @@ int cluster_client_t::try_send(cluster_op_t *op, int i, std::function<void(osd_o
|
|||||||
osd_client_t *cl = peer_it->second;
|
osd_client_t *cl = peer_it->second;
|
||||||
part->flags |= PART_SENT|PART_VALID;
|
part->flags |= PART_SENT|PART_VALID;
|
||||||
op->inflight_count++;
|
op->inflight_count++;
|
||||||
uint64_t pg_bitmap_size = (pool_cfg.data_block_size / pool_cfg.bitmap_granularity / 8) * (
|
uint32_t pg_data_size = (pool_cfg.scheme == POOL_SCHEME_REPLICATED ? 1 : pool_cfg.pg_size-pool_cfg.parity_chunks);
|
||||||
pool_cfg.scheme == POOL_SCHEME_REPLICATED ? 1 : pool_cfg.pg_size-pool_cfg.parity_chunks
|
uint64_t pg_bitmap_size = pg_data_size * (pool_cfg.data_block_size / pool_cfg.bitmap_granularity / 8
|
||||||
);
|
+ (op->opcode == OSD_OP_READ && op->enc ? pool_cfg.data_block_size/pool_cfg.bitmap_granularity : 0));
|
||||||
uint64_t meta_rev = 0;
|
uint64_t meta_rev = 0;
|
||||||
if (op->opcode != OSD_OP_READ_BITMAP && op->opcode != OSD_OP_DELETE && !op->deoptimise_snapshot)
|
if (op->opcode != OSD_OP_READ_BITMAP && op->opcode != OSD_OP_DELETE && !op->deoptimise_snapshot)
|
||||||
{
|
{
|
||||||
@@ -1428,6 +1471,7 @@ int cluster_client_t::try_send(cluster_op_t *op, int i, std::function<void(osd_o
|
|||||||
.inode = op->cur_inode,
|
.inode = op->cur_inode,
|
||||||
.offset = part->offset,
|
.offset = part->offset,
|
||||||
.len = part->len,
|
.len = part->len,
|
||||||
|
.flags = op->opcode == OSD_OP_READ && op->enc && !op->deoptimise_snapshot ? OSD_OP_RETURN_CHAIN : 0,
|
||||||
.meta_revision = meta_rev,
|
.meta_revision = meta_rev,
|
||||||
.version = op->opcode == OSD_OP_WRITE || op->opcode == OSD_OP_DELETE ? op->version : 0,
|
.version = op->opcode == OSD_OP_WRITE || op->opcode == OSD_OP_DELETE ? op->version : 0,
|
||||||
} },
|
} },
|
||||||
@@ -1435,6 +1479,7 @@ int cluster_client_t::try_send(cluster_op_t *op, int i, std::function<void(osd_o
|
|||||||
? (uint8_t*)op->part_bitmaps + pg_bitmap_size*i : NULL),
|
? (uint8_t*)op->part_bitmaps + pg_bitmap_size*i : NULL),
|
||||||
.bitmap_len = (unsigned)(op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_BITMAP || op->opcode == OSD_OP_READ_CHAIN_BITMAP
|
.bitmap_len = (unsigned)(op->opcode == OSD_OP_READ || op->opcode == OSD_OP_READ_BITMAP || op->opcode == OSD_OP_READ_CHAIN_BITMAP
|
||||||
? pg_bitmap_size : 0),
|
? pg_bitmap_size : 0),
|
||||||
|
.enc = op->enc,
|
||||||
.callback = cb ? cb : [this, part](osd_op_t *op_part)
|
.callback = cb ? cb : [this, part](osd_op_t *op_part)
|
||||||
{
|
{
|
||||||
handle_op_part(part);
|
handle_op_part(part);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "messenger.h"
|
#include "messenger.h"
|
||||||
#include "etcd_state_client.h"
|
#include "etcd_state_client.h"
|
||||||
|
#include "../util/robin_hood.h"
|
||||||
|
|
||||||
#define DEFAULT_CLIENT_MAX_DIRTY_BYTES 32*1024*1024
|
#define DEFAULT_CLIENT_MAX_DIRTY_BYTES 32*1024*1024
|
||||||
#define DEFAULT_CLIENT_MAX_DIRTY_OPS 1024
|
#define DEFAULT_CLIENT_MAX_DIRTY_OPS 1024
|
||||||
@@ -71,6 +72,7 @@ protected:
|
|||||||
cluster_op_t *prev = NULL, *next = NULL;
|
cluster_op_t *prev = NULL, *next = NULL;
|
||||||
int prev_wait = 0;
|
int prev_wait = 0;
|
||||||
uint64_t flush_id = 0;
|
uint64_t flush_id = 0;
|
||||||
|
std::shared_ptr<osd_op_enc_t> enc;
|
||||||
friend class cluster_client_t;
|
friend class cluster_client_t;
|
||||||
friend class writeback_cache_t;
|
friend class writeback_cache_t;
|
||||||
};
|
};
|
||||||
@@ -80,6 +82,25 @@ struct inode_list_osd_t;
|
|||||||
struct inode_list_pg_t;
|
struct inode_list_pg_t;
|
||||||
class writeback_cache_t;
|
class writeback_cache_t;
|
||||||
|
|
||||||
|
struct inode_cache_t
|
||||||
|
{
|
||||||
|
std::vector<inode_t> chain;
|
||||||
|
uint8_t *key_data = NULL;
|
||||||
|
osd_op_enc_t *op_enc = NULL;
|
||||||
|
bool readonly = false;
|
||||||
|
bool has_parent_loop = false;
|
||||||
|
inode_t other_pool_parent_id = 0;
|
||||||
|
int err_code = 0;
|
||||||
|
|
||||||
|
~inode_cache_t();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct vault_load_key_t
|
||||||
|
{
|
||||||
|
int key_state = 0;
|
||||||
|
std::string key;
|
||||||
|
};
|
||||||
|
|
||||||
// FIXME: Split into public and private interfaces
|
// FIXME: Split into public and private interfaces
|
||||||
class __attribute__((visibility("default"))) cluster_client_t
|
class __attribute__((visibility("default"))) cluster_client_t
|
||||||
{
|
{
|
||||||
@@ -89,8 +110,8 @@ public:
|
|||||||
timerfd_manager_t *tfd = NULL;
|
timerfd_manager_t *tfd = NULL;
|
||||||
ring_loop_t *ringloop = NULL;
|
ring_loop_t *ringloop = NULL;
|
||||||
|
|
||||||
std::map<pool_id_t, uint64_t> pg_counts;
|
// config:
|
||||||
std::map<pool_pg_num_t, osd_num_t> pg_primary;
|
|
||||||
// client_max_dirty_* is actually "max unsynced", for the case when immediate_commit is off
|
// client_max_dirty_* is actually "max unsynced", for the case when immediate_commit is off
|
||||||
uint64_t client_max_dirty_bytes = 0;
|
uint64_t client_max_dirty_bytes = 0;
|
||||||
uint64_t client_max_dirty_ops = 0;
|
uint64_t client_max_dirty_ops = 0;
|
||||||
@@ -102,12 +123,23 @@ public:
|
|||||||
uint64_t client_max_writeback_iodepth = 0;
|
uint64_t client_max_writeback_iodepth = 0;
|
||||||
std::string conf_hostname;
|
std::string conf_hostname;
|
||||||
|
|
||||||
|
std::string vault_url;
|
||||||
|
std::string vault_client_cert;
|
||||||
|
std::string vault_client_key;
|
||||||
|
std::string vault_ca;
|
||||||
|
std::string vault_secret_api_path;
|
||||||
|
uint64_t vault_timeout_ms = 0;
|
||||||
|
uint64_t vault_error_timeout_sec = 0;
|
||||||
|
uint64_t vault_refresh_leeway_sec = 0;
|
||||||
|
|
||||||
int log_level = 0;
|
int log_level = 0;
|
||||||
int client_retry_interval = 50; // ms
|
int client_retry_interval = 50; // ms
|
||||||
int client_eio_retry_interval = 1000; // ms
|
int client_eio_retry_interval = 1000; // ms
|
||||||
bool client_retry_enospc = true;
|
bool client_retry_enospc = true;
|
||||||
int client_wait_up_timeout = 16; // sec (for listings)
|
int client_wait_up_timeout = 16; // sec (for listings)
|
||||||
|
|
||||||
|
// state:
|
||||||
|
|
||||||
std::string client_hostname;
|
std::string client_hostname;
|
||||||
std::map<std::string, int> self_tree_metrics;
|
std::map<std::string, int> self_tree_metrics;
|
||||||
std::map<osd_num_t, int> osd_tree_metrics;
|
std::map<osd_num_t, int> osd_tree_metrics;
|
||||||
@@ -115,15 +147,28 @@ public:
|
|||||||
int retry_timeout_id = -1;
|
int retry_timeout_id = -1;
|
||||||
int retry_timeout_duration = 0;
|
int retry_timeout_duration = 0;
|
||||||
std::vector<cluster_op_t*> offline_ops;
|
std::vector<cluster_op_t*> offline_ops;
|
||||||
|
std::vector<cluster_op_t*> key_wait_ops;
|
||||||
cluster_op_t *op_queue_head = NULL, *op_queue_tail = NULL;
|
cluster_op_t *op_queue_head = NULL, *op_queue_tail = NULL;
|
||||||
writeback_cache_t *wb = NULL;
|
writeback_cache_t *wb = NULL;
|
||||||
std::set<osd_num_t> dirty_osds;
|
std::set<osd_num_t> dirty_osds;
|
||||||
uint64_t dirty_bytes = 0, dirty_ops = 0;
|
uint64_t dirty_bytes = 0, dirty_ops = 0;
|
||||||
|
|
||||||
void *scrap_buffer = NULL;
|
// inodes require some extra state for read/write, it's stored here.
|
||||||
unsigned scrap_buffer_size = 0;
|
// moreover, robin_hood access is slightly faster than std::map :)
|
||||||
|
robin_hood::unordered_flat_map<inode_t, std::shared_ptr<inode_cache_t>> inode_cache;
|
||||||
|
std::set<std::pair<inode_t, inode_t>> inode_cache_children;
|
||||||
|
|
||||||
|
http_context_t *vault_http_ctx = NULL;
|
||||||
|
http_co_t *vault_http_cli = NULL;
|
||||||
|
bool vault_loading = false;
|
||||||
|
std::string vault_token;
|
||||||
|
bool vault_auth_error = false;
|
||||||
|
timespec vault_token_expire = {};
|
||||||
|
std::vector<std::string> vault_key_load_queue;
|
||||||
|
std::map<std::string, vault_load_key_t> vault_keys;
|
||||||
|
|
||||||
bool pgs_loaded = false;
|
bool pgs_loaded = false;
|
||||||
|
std::map<pool_id_t, uint64_t> pg_counts;
|
||||||
ring_consumer_t consumer;
|
ring_consumer_t consumer;
|
||||||
std::vector<std::function<void(void)>> on_ready_hooks;
|
std::vector<std::function<void(void)>> on_ready_hooks;
|
||||||
int list_retry_timeout_id = -1;
|
int list_retry_timeout_id = -1;
|
||||||
@@ -163,6 +208,13 @@ protected:
|
|||||||
#endif
|
#endif
|
||||||
void continue_ops(int time_passed = 0);
|
void continue_ops(int time_passed = 0);
|
||||||
|
|
||||||
|
std::shared_ptr<inode_cache_t> inode_cache_get(inode_t ino);
|
||||||
|
void vault_parse_config();
|
||||||
|
bool vault_check_token();
|
||||||
|
void vault_load_keys();
|
||||||
|
void vault_destroy();
|
||||||
|
void vault_parse_secret(const std::string & key_id, const std::string & err, json11::Json data);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool affects_osd(uint64_t inode, uint64_t offset, uint64_t len, osd_num_t osd);
|
bool affects_osd(uint64_t inode, uint64_t offset, uint64_t len, osd_num_t osd);
|
||||||
bool affects_pg(uint64_t inode, uint64_t offset, uint64_t len, pool_id_t pool_id, pg_num_t pg_num);
|
bool affects_pg(uint64_t inode, uint64_t offset, uint64_t len, pool_id_t pool_id, pg_num_t pg_num);
|
||||||
@@ -173,6 +225,7 @@ protected:
|
|||||||
void on_change_pg_state_hook(pool_id_t pool_id, pg_num_t pg_num, osd_num_t prev_primary);
|
void on_change_pg_state_hook(pool_id_t pool_id, pg_num_t pg_num, osd_num_t prev_primary);
|
||||||
void on_change_osd_state_hook(uint64_t peer_osd);
|
void on_change_osd_state_hook(uint64_t peer_osd);
|
||||||
void on_change_node_placement_hook();
|
void on_change_node_placement_hook();
|
||||||
|
void on_change_inode_hook(uint64_t inode, bool removed);
|
||||||
|
|
||||||
void execute_internal(cluster_op_t *op);
|
void execute_internal(cluster_op_t *op);
|
||||||
void execute_cas(cluster_op_t *op);
|
void execute_cas(cluster_op_t *op);
|
||||||
@@ -189,6 +242,7 @@ protected:
|
|||||||
void erase_op(cluster_op_t *op);
|
void erase_op(cluster_op_t *op);
|
||||||
void calc_wait(cluster_op_t *op);
|
void calc_wait(cluster_op_t *op);
|
||||||
void inc_wait(uint64_t opcode, uint64_t flags, cluster_op_t *next, int inc);
|
void inc_wait(uint64_t opcode, uint64_t flags, cluster_op_t *next, int inc);
|
||||||
|
|
||||||
void continue_lists();
|
void continue_lists();
|
||||||
bool continue_listing(inode_list_t *lst);
|
bool continue_listing(inode_list_t *lst);
|
||||||
bool restart_listing(inode_list_t* lst);
|
bool restart_listing(inode_list_t* lst);
|
||||||
|
|||||||
@@ -0,0 +1,367 @@
|
|||||||
|
// Copyright (c) Vitaliy Filippov, 2019+
|
||||||
|
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "cluster_client_impl.h"
|
||||||
|
#include "http_client.h"
|
||||||
|
#include "str_util.h"
|
||||||
|
|
||||||
|
#define VAULT_KEY_NOT_LOADED 0
|
||||||
|
#define VAULT_KEY_LOADING 1
|
||||||
|
#define VAULT_KEY_LOADED 2
|
||||||
|
#define VAULT_KEY_ERROR 3
|
||||||
|
|
||||||
|
inode_cache_t::~inode_cache_t()
|
||||||
|
{
|
||||||
|
if (key_data)
|
||||||
|
{
|
||||||
|
free(key_data);
|
||||||
|
key_data = NULL;
|
||||||
|
op_enc = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cluster_client_t::vault_destroy()
|
||||||
|
{
|
||||||
|
if (vault_http_ctx)
|
||||||
|
{
|
||||||
|
#ifndef __MOCK__
|
||||||
|
http_destroy(vault_http_cli);
|
||||||
|
http_context_destroy(vault_http_ctx);
|
||||||
|
vault_http_cli = NULL;
|
||||||
|
vault_http_ctx = NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cluster_client_t::vault_parse_config()
|
||||||
|
{
|
||||||
|
vault_url = config["vault_url"].string_value();
|
||||||
|
vault_client_cert = config["vault_client_cert"].string_value();
|
||||||
|
vault_client_key = config["vault_client_key"].string_value();
|
||||||
|
vault_ca = config["vault_ca"].string_value();
|
||||||
|
vault_secret_api_path = "/v1/secret/";
|
||||||
|
if (config["vault_secret_api_path"].is_string())
|
||||||
|
vault_secret_api_path = config["vault_secret_api_path"].string_value();
|
||||||
|
vault_timeout_ms = config["vault_timeout_ms"].uint64_value();
|
||||||
|
if (!vault_timeout_ms)
|
||||||
|
vault_timeout_ms = 5000;
|
||||||
|
vault_error_timeout_sec = config["vault_error_timeout_sec"].uint64_value();
|
||||||
|
if (!vault_error_timeout_sec)
|
||||||
|
vault_error_timeout_sec = 60;
|
||||||
|
vault_refresh_leeway_sec = config["vault_refresh_leeway_sec"].uint64_value();
|
||||||
|
if (!vault_refresh_leeway_sec)
|
||||||
|
vault_refresh_leeway_sec = 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Rework client API by adding open/close and cache inode information in the "FD" (maybe)
|
||||||
|
void cluster_client_t::on_change_inode_hook(uint64_t inode, bool removed)
|
||||||
|
{
|
||||||
|
std::vector<inode_t> children = { inode };
|
||||||
|
for (size_t i = 0; i < children.size(); i++)
|
||||||
|
{
|
||||||
|
auto it = inode_cache_children.lower_bound(std::make_pair(children[i], (inode_t)0));
|
||||||
|
while (it != inode_cache_children.end() && it->first == children[i])
|
||||||
|
{
|
||||||
|
children.push_back(it->second);
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto & inode: children)
|
||||||
|
{
|
||||||
|
auto it = inode_cache.find(inode);
|
||||||
|
if (it != inode_cache.end())
|
||||||
|
{
|
||||||
|
auto icache = it->second;
|
||||||
|
for (auto & parent: icache->chain)
|
||||||
|
{
|
||||||
|
inode_cache_children.erase(std::make_pair(parent, inode));
|
||||||
|
}
|
||||||
|
inode_cache.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<inode_cache_t> cluster_client_t::inode_cache_get(inode_t ino)
|
||||||
|
{
|
||||||
|
auto icache_it = inode_cache.find(ino);
|
||||||
|
if (icache_it != inode_cache.end())
|
||||||
|
{
|
||||||
|
return icache_it->second;
|
||||||
|
}
|
||||||
|
// Fill inode cache
|
||||||
|
auto ino_it = st_cli.inode_config.find(ino);
|
||||||
|
if (ino_it == st_cli.inode_config.end())
|
||||||
|
{
|
||||||
|
inode_cache[ino] = NULL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
auto pool_it = st_cli.pool_config.find(INODE_POOL(ino));
|
||||||
|
if (pool_it == st_cli.pool_config.end())
|
||||||
|
{
|
||||||
|
inode_cache[ino] = NULL;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
auto & inode_cfg = ino_it->second;
|
||||||
|
auto & pool_cfg = pool_it->second;
|
||||||
|
std::shared_ptr<inode_cache_t> icache = std::make_shared<inode_cache_t>();
|
||||||
|
icache->readonly = inode_cfg.readonly;
|
||||||
|
icache->chain.push_back(ino);
|
||||||
|
std::vector<inode_config_t*> chain_cfg;
|
||||||
|
// FIXME: Allow unencrypted read & write when all chain is encrypted with the same key
|
||||||
|
int enc_key_count = !inode_cfg.enc_key.empty() ? 1 : 0;
|
||||||
|
if (inode_cfg.parent_id)
|
||||||
|
{
|
||||||
|
// Check for loops and cache the chain
|
||||||
|
robin_hood::unordered_flat_set<inode_t> seen;
|
||||||
|
seen.insert(ino);
|
||||||
|
uint64_t parent_id = inode_cfg.parent_id;
|
||||||
|
while (parent_id)
|
||||||
|
{
|
||||||
|
if (seen.find(parent_id) != seen.end())
|
||||||
|
{
|
||||||
|
icache->has_parent_loop = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
seen.insert(parent_id);
|
||||||
|
ino_it = st_cli.inode_config.find(parent_id);
|
||||||
|
if (INODE_POOL(parent_id) == INODE_POOL(ino))
|
||||||
|
{
|
||||||
|
icache->chain.push_back(parent_id);
|
||||||
|
if (ino_it == st_cli.inode_config.end())
|
||||||
|
chain_cfg.push_back(NULL);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
chain_cfg.push_back(&ino_it->second);
|
||||||
|
if (!ino_it->second.enc_key.empty())
|
||||||
|
enc_key_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!icache->other_pool_parent_id)
|
||||||
|
icache->other_pool_parent_id = parent_id;
|
||||||
|
if (ino_it == st_cli.inode_config.end())
|
||||||
|
break;
|
||||||
|
parent_id = ino_it->second.parent_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check external keys and wait for loading, if required
|
||||||
|
if (enc_key_count)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i <= chain_cfg.size(); i++)
|
||||||
|
{
|
||||||
|
inode_config_t *cfg = !i ? &inode_cfg : chain_cfg[i-1];
|
||||||
|
if (cfg && cfg->enc_key.substr(0, strlen(VAULT_KEY_PREFIX)) == VAULT_KEY_PREFIX)
|
||||||
|
{
|
||||||
|
auto & ik = vault_keys[inode_cfg.enc_key];
|
||||||
|
if (ik.key_state == VAULT_KEY_ERROR || vault_url.empty())
|
||||||
|
{
|
||||||
|
icache->err_code = EPERM;
|
||||||
|
enc_key_count = 0;
|
||||||
|
}
|
||||||
|
else if (ik.key_state == VAULT_KEY_NOT_LOADED)
|
||||||
|
{
|
||||||
|
ik.key_state = VAULT_KEY_LOADING;
|
||||||
|
vault_key_load_queue.push_back(inode_cfg.enc_key);
|
||||||
|
vault_load_keys();
|
||||||
|
icache->err_code = EAGAIN;
|
||||||
|
enc_key_count = 0;
|
||||||
|
}
|
||||||
|
else if (ik.key_state == VAULT_KEY_LOADING)
|
||||||
|
{
|
||||||
|
icache->err_code = EAGAIN;
|
||||||
|
enc_key_count = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(ik.key_state == VAULT_KEY_LOADED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Generate encryption key chain, if applicable
|
||||||
|
if (enc_key_count)
|
||||||
|
{
|
||||||
|
uint8_t *key_data = (uint8_t*)malloc_or_die(
|
||||||
|
AES_256_XTS_KEY_SIZE * enc_key_count +
|
||||||
|
sizeof(uint8_t*) * icache->chain.size() +
|
||||||
|
sizeof(osd_op_enc_t)
|
||||||
|
);
|
||||||
|
uint8_t **keys = (uint8_t**)(key_data + AES_256_XTS_KEY_SIZE * enc_key_count);
|
||||||
|
osd_op_enc_t *enc = (osd_op_enc_t*)((uint8_t*)keys + sizeof(uint8_t*)*icache->chain.size());
|
||||||
|
size_t key_pos = 0;
|
||||||
|
for (size_t i = 0; i <= chain_cfg.size(); i++)
|
||||||
|
{
|
||||||
|
inode_config_t *cfg = !i ? &inode_cfg : chain_cfg[i-1];
|
||||||
|
if (cfg && !cfg->enc_key.empty())
|
||||||
|
{
|
||||||
|
const auto & key = cfg->enc_key.substr(0, strlen(VAULT_KEY_PREFIX)) == VAULT_KEY_PREFIX
|
||||||
|
? vault_keys.at(cfg->enc_key).key
|
||||||
|
: cfg->enc_key;
|
||||||
|
assert(key_pos < AES_256_XTS_KEY_SIZE * enc_key_count);
|
||||||
|
assert(key.size() == 2*AES_256_XTS_KEY_SIZE);
|
||||||
|
keys[i] = key_data + key_pos;
|
||||||
|
fromhexstr(key, AES_256_XTS_KEY_SIZE, key_data + key_pos);
|
||||||
|
key_pos += AES_256_XTS_KEY_SIZE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
keys[i] = NULL;
|
||||||
|
}
|
||||||
|
enc->key_chain = keys;
|
||||||
|
enc->chain_size = icache->chain.size();
|
||||||
|
enc->read_chain_bitmap_pos = pool_cfg.data_block_size/pool_cfg.bitmap_granularity/8;
|
||||||
|
enc->bitmap_granularity = pool_cfg.bitmap_granularity;
|
||||||
|
icache->key_data = key_data;
|
||||||
|
icache->op_enc = enc;
|
||||||
|
}
|
||||||
|
inode_cache[ino] = icache;
|
||||||
|
for (auto & parent: icache->chain)
|
||||||
|
{
|
||||||
|
if (parent != ino)
|
||||||
|
inode_cache_children.insert(std::make_pair(parent, ino));
|
||||||
|
}
|
||||||
|
return icache;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef __MOCK__
|
||||||
|
bool cluster_client_t::vault_check_token()
|
||||||
|
{
|
||||||
|
timespec now;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &now);
|
||||||
|
if (!vault_token_expire.tv_sec || vault_token_expire.tv_sec < now.tv_sec)
|
||||||
|
{
|
||||||
|
vault_loading = true;
|
||||||
|
http_json_post(
|
||||||
|
vault_http_cli, vault_url+"/v1/auth/cert/login", json11::Json::object{}, "",
|
||||||
|
(http_options_t){ .timeout = (int)vault_timeout_ms, .keepalive = true },
|
||||||
|
[this](http_message_t *response)
|
||||||
|
{
|
||||||
|
clock_gettime(CLOCK_REALTIME, &vault_token_expire);
|
||||||
|
vault_loading = false;
|
||||||
|
std::string err;
|
||||||
|
json11::Json data;
|
||||||
|
response->parse_json_response(err, data);
|
||||||
|
if (err != "")
|
||||||
|
{
|
||||||
|
vault_token_expire.tv_sec += vault_error_timeout_sec;
|
||||||
|
fprintf(stderr, "Vault request failed: %s\n", err.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint64_t ttl = data["auth"]["lease_duration"].uint64_value();
|
||||||
|
vault_token = data["auth"]["client_token"].string_value();
|
||||||
|
if (vault_token.empty() || !ttl)
|
||||||
|
{
|
||||||
|
vault_token_expire.tv_sec += vault_error_timeout_sec;
|
||||||
|
fprintf(stderr, "No token or lease_duration in Vault response: %s\n", data.dump().c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ttl < vault_refresh_leeway_sec)
|
||||||
|
vault_token_expire.tv_sec += ttl/2;
|
||||||
|
else
|
||||||
|
vault_token_expire.tv_sec += ttl - vault_refresh_leeway_sec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vault_load_keys();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (vault_token.empty())
|
||||||
|
{
|
||||||
|
// Auth error happened, mark all loads as failed
|
||||||
|
for (auto & key_id: vault_key_load_queue)
|
||||||
|
{
|
||||||
|
auto & k = vault_keys[key_id];
|
||||||
|
k.key_state = VAULT_KEY_ERROR;
|
||||||
|
}
|
||||||
|
vault_key_load_queue.clear();
|
||||||
|
auto ops = std::move(key_wait_ops);
|
||||||
|
for (cluster_op_t *op: ops)
|
||||||
|
inode_cache.erase(op->inode);
|
||||||
|
for (cluster_op_t *op: ops)
|
||||||
|
execute_internal(op);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void cluster_client_t::vault_load_keys()
|
||||||
|
{
|
||||||
|
if (vault_loading || !vault_key_load_queue.size())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#ifdef __MOCK__
|
||||||
|
vault_loading = true;
|
||||||
|
#else
|
||||||
|
if (!vault_http_ctx)
|
||||||
|
{
|
||||||
|
std::string error;
|
||||||
|
vault_http_ctx = http_context_init(tfd, vault_client_cert, vault_client_key, vault_ca, true, error);
|
||||||
|
if (!vault_http_ctx)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Failed to initialize HTTP context for Vault: %s\n", error.c_str());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
vault_http_cli = http_init(vault_http_ctx);
|
||||||
|
}
|
||||||
|
if (!vault_check_token())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::string key_id = vault_key_load_queue[0];
|
||||||
|
vault_key_load_queue.erase(vault_key_load_queue.begin());
|
||||||
|
vault_loading = true;
|
||||||
|
http_get(
|
||||||
|
vault_http_cli, vault_url+vault_secret_api_path+key_id.substr(strlen(VAULT_KEY_PREFIX)), "X-Vault-Token: "+vault_token+"\r\n",
|
||||||
|
(http_options_t){ .timeout = (int)vault_timeout_ms, .keepalive = true },
|
||||||
|
[this, key_id](http_message_t *response)
|
||||||
|
{
|
||||||
|
vault_loading = false;
|
||||||
|
std::string err;
|
||||||
|
json11::Json data;
|
||||||
|
response->parse_json_response(err, data);
|
||||||
|
vault_parse_secret(key_id, err, data);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void cluster_client_t::vault_parse_secret(const std::string & key_id, const std::string & err, json11::Json data)
|
||||||
|
{
|
||||||
|
vault_loading = false;
|
||||||
|
auto & k = vault_keys[key_id];
|
||||||
|
if (err != "")
|
||||||
|
{
|
||||||
|
k.key_state = VAULT_KEY_ERROR;
|
||||||
|
fprintf(stderr, "Vault %s%s%s request failed: %s\n", vault_url.c_str(),
|
||||||
|
vault_secret_api_path.c_str(), key_id.c_str()+strlen(VAULT_KEY_PREFIX), err.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto hexkey = data["data"]["key"].string_value();
|
||||||
|
if (hexkey.empty() || !ishexstr(hexkey) || hexkey.size() != 2*AES_256_XTS_KEY_SIZE)
|
||||||
|
{
|
||||||
|
k.key_state = VAULT_KEY_ERROR;
|
||||||
|
fprintf(stderr, "Vault /v1/secret/%s request failed: 'key' is empty or has invalid format\n", key_id.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
k.key_state = VAULT_KEY_LOADED;
|
||||||
|
k.key = hexkey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (vault_key_load_queue.empty())
|
||||||
|
{
|
||||||
|
auto ops = std::move(key_wait_ops);
|
||||||
|
for (cluster_op_t *op: ops)
|
||||||
|
inode_cache.erase(op->inode);
|
||||||
|
for (cluster_op_t *op: ops)
|
||||||
|
execute_internal(op);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
vault_load_keys();
|
||||||
|
}
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
#include "cluster_client.h"
|
#include "cluster_client.h"
|
||||||
|
|
||||||
#define SCRAP_BUFFER_SIZE 4*1024*1024
|
|
||||||
#define PART_SENT 1
|
#define PART_SENT 1
|
||||||
#define PART_DONE 2
|
#define PART_DONE 2
|
||||||
#define PART_ERROR 4
|
#define PART_ERROR 4
|
||||||
|
|||||||
+288
-165
@@ -1,7 +1,10 @@
|
|||||||
// Copyright (c) Vitaliy Filippov, 2019+
|
// Copyright (c) Vitaliy Filippov, 2019+
|
||||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include "malloc_or_die.h"
|
||||||
#include "osd_ops.h"
|
#include "osd_ops.h"
|
||||||
|
#include "msgr_op.h"
|
||||||
#include "pg_states.h"
|
#include "pg_states.h"
|
||||||
#include "etcd_state_client.h"
|
#include "etcd_state_client.h"
|
||||||
#ifndef __MOCK__
|
#ifndef __MOCK__
|
||||||
@@ -22,14 +25,19 @@ etcd_state_client_t::~etcd_state_client_t()
|
|||||||
stop_ws_keepalive();
|
stop_ws_keepalive();
|
||||||
if (etcd_watch_ws)
|
if (etcd_watch_ws)
|
||||||
{
|
{
|
||||||
http_close(etcd_watch_ws);
|
http_destroy(etcd_watch_ws);
|
||||||
etcd_watch_ws = NULL;
|
etcd_watch_ws = NULL;
|
||||||
}
|
}
|
||||||
if (keepalive_client)
|
if (keepalive_client)
|
||||||
{
|
{
|
||||||
http_close(keepalive_client);
|
http_destroy(keepalive_client);
|
||||||
keepalive_client = NULL;
|
keepalive_client = NULL;
|
||||||
}
|
}
|
||||||
|
if (http_ctx)
|
||||||
|
{
|
||||||
|
http_context_destroy(http_ctx);
|
||||||
|
http_ctx = NULL;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (load_pgs_timer_id >= 0)
|
if (load_pgs_timer_id >= 0)
|
||||||
{
|
{
|
||||||
@@ -72,55 +80,51 @@ std::vector<std::string> etcd_state_client_t::get_addresses()
|
|||||||
return addrs;
|
return addrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
void etcd_state_client_t::etcd_call_oneshot(std::string etcd_address, std::string api, json11::Json payload,
|
http_context_t *etcd_state_client_t::get_http_ctx()
|
||||||
|
{
|
||||||
|
if (!http_ctx)
|
||||||
|
{
|
||||||
|
std::string error;
|
||||||
|
http_ctx = http_context_init(tfd, etcd_client_cert, etcd_client_key, etcd_ca, true, error);
|
||||||
|
if (!http_ctx)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Failed to initialize HTTP context: %s\n", error.c_str());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return http_ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void etcd_state_client_t::etcd_call_oneshot(const std::string & etcd_url, const std::string & api, json11::Json payload,
|
||||||
int timeout, std::function<void(std::string, json11::Json)> callback)
|
int timeout, std::function<void(std::string, json11::Json)> callback)
|
||||||
{
|
{
|
||||||
std::string etcd_api_path;
|
auto http_cli = http_init(get_http_ctx());
|
||||||
int pos = etcd_address.find('/');
|
http_json_post(http_cli, etcd_url+api, payload, "", { .timeout = timeout }, [http_cli, callback](http_message_t *response)
|
||||||
if (pos >= 0)
|
|
||||||
{
|
|
||||||
etcd_api_path = etcd_address.substr(pos);
|
|
||||||
etcd_address = etcd_address.substr(0, pos);
|
|
||||||
}
|
|
||||||
std::string req = payload.dump();
|
|
||||||
req = "POST "+etcd_api_path+api+" HTTP/1.1\r\n"
|
|
||||||
"Host: "+etcd_address+"\r\n"
|
|
||||||
"Content-Type: application/json\r\n"
|
|
||||||
"Content-Length: "+std::to_string(req.size())+"\r\n"
|
|
||||||
"Connection: close\r\n"
|
|
||||||
"\r\n"+req;
|
|
||||||
auto http_cli = http_init(tfd);
|
|
||||||
auto cb = [http_cli, callback](const http_response_t *response)
|
|
||||||
{
|
{
|
||||||
std::string err;
|
std::string err;
|
||||||
json11::Json data;
|
json11::Json data;
|
||||||
response->parse_json_response(err, data);
|
response->parse_json_response(err, data);
|
||||||
callback(err, data);
|
callback(err, data);
|
||||||
http_close(http_cli);
|
http_destroy(http_cli);
|
||||||
};
|
});
|
||||||
http_request(http_cli, etcd_address, req, { .timeout = timeout }, cb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void etcd_state_client_t::etcd_call(std::string api, json11::Json payload, int timeout,
|
void etcd_state_client_t::etcd_call(const std::string & api, json11::Json payload, int timeout,
|
||||||
int retries, int interval, std::function<void(std::string, json11::Json)> callback)
|
int retries, int interval, std::function<void(std::string, json11::Json)> callback)
|
||||||
{
|
{
|
||||||
if (!etcd_addresses.size() && !etcd_local.size())
|
pick_next_etcd([=]()
|
||||||
{
|
{
|
||||||
fprintf(stderr, "etcd_address is missing in Vitastor configuration\n");
|
etcd_call_selected(api, payload, timeout, retries, interval, callback);
|
||||||
exit(1);
|
});
|
||||||
}
|
}
|
||||||
pick_next_etcd();
|
|
||||||
std::string etcd_address = selected_etcd_address;
|
void etcd_state_client_t::etcd_call_selected(const std::string & api, json11::Json payload, int timeout,
|
||||||
std::string etcd_api_path;
|
int retries, int interval, std::function<void(std::string, json11::Json)> callback)
|
||||||
int pos = etcd_address.find('/');
|
{
|
||||||
if (pos >= 0)
|
const auto & url = selected_etcd_url;
|
||||||
{
|
|
||||||
etcd_api_path = etcd_address.substr(pos);
|
|
||||||
etcd_address = etcd_address.substr(0, pos);
|
|
||||||
}
|
|
||||||
std::string req = payload.dump();
|
std::string req = payload.dump();
|
||||||
req = "POST "+etcd_api_path+api+" HTTP/1.1\r\n"
|
req = "POST "+url.path+api+" HTTP/1.1\r\n"
|
||||||
"Host: "+etcd_address+"\r\n"
|
"Host: "+url.hostname+"\r\n"
|
||||||
"Content-Type: application/json\r\n"
|
"Content-Type: application/json\r\n"
|
||||||
"Content-Length: "+std::to_string(req.size())+"\r\n"
|
"Content-Length: "+std::to_string(req.size())+"\r\n"
|
||||||
"Connection: keep-alive\r\n"
|
"Connection: keep-alive\r\n"
|
||||||
@@ -128,15 +132,15 @@ void etcd_state_client_t::etcd_call(std::string api, json11::Json payload, int t
|
|||||||
"\r\n"+req;
|
"\r\n"+req;
|
||||||
retries--;
|
retries--;
|
||||||
auto cb = [this, api, payload, timeout, retries, interval, callback,
|
auto cb = [this, api, payload, timeout, retries, interval, callback,
|
||||||
cur_addr = selected_etcd_address](const http_response_t *response)
|
cur_addr = url.addr](http_message_t *response)
|
||||||
{
|
{
|
||||||
std::string err;
|
std::string err;
|
||||||
json11::Json data;
|
json11::Json data;
|
||||||
response->parse_json_response(err, data);
|
response->parse_json_response(err, data);
|
||||||
if (err != "")
|
if (err != "")
|
||||||
{
|
{
|
||||||
if (cur_addr == selected_etcd_address)
|
if (cur_addr == selected_etcd_url.addr)
|
||||||
selected_etcd_address = "";
|
selected_etcd_url = (http_url_t){};
|
||||||
if (retries > 0)
|
if (retries > 0)
|
||||||
{
|
{
|
||||||
if (this->log_level > 0)
|
if (this->log_level > 0)
|
||||||
@@ -164,54 +168,58 @@ void etcd_state_client_t::etcd_call(std::string api, json11::Json payload, int t
|
|||||||
callback(err, data);
|
callback(err, data);
|
||||||
};
|
};
|
||||||
if (!keepalive_client)
|
if (!keepalive_client)
|
||||||
{
|
keepalive_client = http_init(get_http_ctx());
|
||||||
keepalive_client = http_init(tfd);
|
http_request(keepalive_client, url.addr, req, { .timeout = timeout, .keepalive = true, .ssl = url.ssl }, cb);
|
||||||
}
|
|
||||||
http_request(keepalive_client, etcd_address, req, { .timeout = timeout, .keepalive = true }, cb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void etcd_state_client_t::add_etcd_url(std::string addr)
|
|
||||||
|
void etcd_state_client_t::add_etcd_url(std::string etcd_address)
|
||||||
{
|
{
|
||||||
if (addr.length() > 0)
|
if (etcd_address.size() > 0)
|
||||||
{
|
{
|
||||||
if (strtolower(addr.substr(0, 7)) == "http://")
|
|
||||||
addr = addr.substr(7);
|
|
||||||
else if (strtolower(addr.substr(0, 8)) == "https://")
|
|
||||||
{
|
|
||||||
fprintf(stderr, "HTTPS is unsupported for etcd. Either use plain HTTP or setup a local proxy for etcd interaction\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (!local_ips.size())
|
if (!local_ips.size())
|
||||||
local_ips = getifaddr_list(std::vector<addr_mask_t>(), true);
|
|
||||||
std::string check_addr;
|
|
||||||
int pos = addr.find('/');
|
|
||||||
int pos2 = addr.find(':');
|
|
||||||
if (pos2 >= 0)
|
|
||||||
check_addr = addr.substr(0, pos2);
|
|
||||||
else if (pos >= 0)
|
|
||||||
check_addr = addr.substr(0, pos);
|
|
||||||
else
|
|
||||||
check_addr = addr;
|
|
||||||
if (pos == std::string::npos)
|
|
||||||
addr += "/v3";
|
|
||||||
bool local = false;
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < local_ips.size(); i++)
|
|
||||||
{
|
{
|
||||||
if (local_ips[i] == check_addr)
|
// Fill local_ips
|
||||||
{
|
for (auto & ip: getifaddr_list(std::vector<addr_mask_t>(), true))
|
||||||
local = true;
|
local_ips.insert(ip);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
auto & to = local ? this->etcd_local : this->etcd_addresses;
|
std::string etcd_api_path;
|
||||||
|
bool ssl = false;
|
||||||
|
if (etcd_address.substr(0, 8) == "https://")
|
||||||
|
{
|
||||||
|
ssl = true;
|
||||||
|
etcd_address = etcd_address.substr(8);
|
||||||
|
}
|
||||||
|
else if (etcd_address.substr(0, 7) == "http://")
|
||||||
|
etcd_address = etcd_address.substr(7);
|
||||||
|
auto pos = etcd_address.find('/');
|
||||||
|
if (pos != std::string::npos)
|
||||||
|
{
|
||||||
|
etcd_api_path = etcd_address.substr(pos);
|
||||||
|
etcd_address = etcd_address.substr(0, pos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
etcd_api_path = "/v3";
|
||||||
|
pos = etcd_address.find(':');
|
||||||
|
auto check_addr = (pos != std::string::npos ? etcd_address.substr(0, pos) : etcd_address);
|
||||||
|
bool is_local = local_ips.find(check_addr) != local_ips.end();
|
||||||
|
auto & to = (is_local ? etcd_local : etcd_addresses);
|
||||||
|
check_addr = (ssl ? "https://" : "http://") + etcd_address + etcd_api_path;
|
||||||
|
size_t i;
|
||||||
for (i = 0; i < to.size(); i++)
|
for (i = 0; i < to.size(); i++)
|
||||||
{
|
{
|
||||||
if (to[i] == addr)
|
if (to[i] == check_addr)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (i >= to.size())
|
if (i >= to.size())
|
||||||
to.push_back(addr);
|
{
|
||||||
|
to.push_back(check_addr);
|
||||||
|
// Check if it's a domain name
|
||||||
|
sockaddr_storage ss;
|
||||||
|
bool is_name = !is_local && !string_to_addr(etcd_address, true, 0, &ss);
|
||||||
|
auto & to_addr = (is_local ? etcd_local_addr_urls : (is_name ? etcd_name_urls : etcd_nonlocal_addr_urls));
|
||||||
|
to_addr.push_back((http_url_t){ .ssl = ssl, .addr = etcd_address, .hostname = etcd_address, .path = etcd_api_path });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,6 +227,9 @@ void etcd_state_client_t::parse_config(const json11::Json & config)
|
|||||||
{
|
{
|
||||||
this->etcd_local.clear();
|
this->etcd_local.clear();
|
||||||
this->etcd_addresses.clear();
|
this->etcd_addresses.clear();
|
||||||
|
this->etcd_local_addr_urls.clear();
|
||||||
|
this->etcd_nonlocal_addr_urls.clear();
|
||||||
|
this->etcd_name_urls.clear();
|
||||||
if (config["etcd_address"].is_string())
|
if (config["etcd_address"].is_string())
|
||||||
{
|
{
|
||||||
std::string ea = config["etcd_address"].string_value();
|
std::string ea = config["etcd_address"].string_value();
|
||||||
@@ -239,7 +250,19 @@ void etcd_state_client_t::parse_config(const json11::Json & config)
|
|||||||
add_etcd_url(ea.string_value());
|
add_etcd_url(ea.string_value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (this->osd_num)
|
||||||
|
{
|
||||||
|
this->etcd_client_cert = config["osd_etcd_client_cert"].string_value();
|
||||||
|
this->etcd_client_key = config["osd_etcd_client_key"].string_value();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->etcd_client_cert = config["etcd_client_cert"].string_value();
|
||||||
|
this->etcd_client_key = config["etcd_client_key"].string_value();
|
||||||
|
}
|
||||||
|
this->etcd_ca = config["etcd_ca"].string_value();
|
||||||
this->etcd_prefix = config["etcd_prefix"].string_value();
|
this->etcd_prefix = config["etcd_prefix"].string_value();
|
||||||
|
this->use_auth = config["use_auth"].bool_value();
|
||||||
if (this->etcd_prefix == "")
|
if (this->etcd_prefix == "")
|
||||||
{
|
{
|
||||||
this->etcd_prefix = "/vitastor";
|
this->etcd_prefix = "/vitastor";
|
||||||
@@ -291,66 +314,130 @@ void etcd_state_client_t::parse_config(const json11::Json & config)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void etcd_state_client_t::pick_next_etcd()
|
void etcd_state_client_t::pick_next_etcd(std::function<void()> cb)
|
||||||
{
|
|
||||||
if (selected_etcd_address != "")
|
|
||||||
return;
|
|
||||||
if (addresses_to_try.size() == 0)
|
|
||||||
{
|
|
||||||
// Prefer local etcd, if any
|
|
||||||
for (int i = 0; i < etcd_local.size(); i++)
|
|
||||||
addresses_to_try.push_back(etcd_local[i]);
|
|
||||||
std::vector<int> ns;
|
|
||||||
for (int i = 0; i < etcd_addresses.size(); i++)
|
|
||||||
ns.push_back(i);
|
|
||||||
if (!rand_initialized)
|
|
||||||
{
|
|
||||||
timespec tv;
|
|
||||||
clock_gettime(CLOCK_REALTIME, &tv);
|
|
||||||
srand48(tv.tv_sec*1000000000 + tv.tv_nsec);
|
|
||||||
rand_initialized = true;
|
|
||||||
}
|
|
||||||
while (ns.size())
|
|
||||||
{
|
|
||||||
int i = lrand48() % ns.size();
|
|
||||||
addresses_to_try.push_back(etcd_addresses[ns[i]]);
|
|
||||||
ns.erase(ns.begin()+i, ns.begin()+i+1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
selected_etcd_address = addresses_to_try[0];
|
|
||||||
addresses_to_try.erase(addresses_to_try.begin(), addresses_to_try.begin()+1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void etcd_state_client_t::start_etcd_watcher()
|
|
||||||
{
|
{
|
||||||
if (!etcd_addresses.size() && !etcd_local.size())
|
if (!etcd_addresses.size() && !etcd_local.size())
|
||||||
{
|
{
|
||||||
fprintf(stderr, "etcd_address is missing in Vitastor configuration\n");
|
fprintf(stderr, "etcd_address is missing in Vitastor configuration\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
pick_next_etcd();
|
if (selected_etcd_url.addr != "")
|
||||||
std::string etcd_address = selected_etcd_address;
|
|
||||||
std::string etcd_api_path;
|
|
||||||
int pos = etcd_address.find('/');
|
|
||||||
if (pos >= 0)
|
|
||||||
{
|
{
|
||||||
etcd_api_path = etcd_address.substr(pos);
|
cb();
|
||||||
etcd_address = etcd_address.substr(0, pos);
|
return;
|
||||||
}
|
}
|
||||||
|
if (etcd_urls_to_try.size() != 0)
|
||||||
|
{
|
||||||
|
selected_etcd_url = std::move(etcd_urls_to_try[0]);
|
||||||
|
etcd_urls_to_try.erase(etcd_urls_to_try.begin());
|
||||||
|
cb();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
on_resolve_queue.push_back(std::move(cb));
|
||||||
|
if (on_resolve_queue.size() > 1)
|
||||||
|
{
|
||||||
|
// Already resolving
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
assert(!resolve_count);
|
||||||
|
local_to_try = 0;
|
||||||
|
for (auto & url: etcd_local_addr_urls)
|
||||||
|
{
|
||||||
|
// Prefer local IPs, if any
|
||||||
|
etcd_urls_to_try.push_back(url);
|
||||||
|
local_to_try++;
|
||||||
|
}
|
||||||
|
for (auto & url: etcd_nonlocal_addr_urls)
|
||||||
|
{
|
||||||
|
etcd_urls_to_try.push_back(url);
|
||||||
|
}
|
||||||
|
resolve_count++;
|
||||||
|
for (auto & url: etcd_name_urls)
|
||||||
|
{
|
||||||
|
resolve_count++;
|
||||||
|
http_resolve(get_http_ctx(), url.ssl, url.addr, [this, url](const std::string & error, const std::vector<std::string>& addresses)
|
||||||
|
{
|
||||||
|
if (error != "")
|
||||||
|
fprintf(stderr, "Error resolving %s: %s\n", url.addr.c_str(), error.c_str());
|
||||||
|
for (auto & addr: addresses)
|
||||||
|
{
|
||||||
|
auto url_copy = url;
|
||||||
|
url_copy.addr = addr;
|
||||||
|
if (local_ips.find(addr) != local_ips.end())
|
||||||
|
{
|
||||||
|
etcd_urls_to_try.insert(etcd_urls_to_try.begin(), std::move(url_copy));
|
||||||
|
local_to_try++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
etcd_urls_to_try.push_back(std::move(url_copy));
|
||||||
|
}
|
||||||
|
resolve_count--;
|
||||||
|
if (!resolve_count)
|
||||||
|
pick_next_etcd_on_resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
resolve_count--;
|
||||||
|
if (!resolve_count)
|
||||||
|
{
|
||||||
|
pick_next_etcd_on_resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void etcd_state_client_t::pick_next_etcd_on_resolve()
|
||||||
|
{
|
||||||
|
if (!etcd_urls_to_try.size())
|
||||||
|
{
|
||||||
|
fprintf(stderr, "None of etcd_address could be resolved\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (!rand_initialized)
|
||||||
|
{
|
||||||
|
timespec tv;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &tv);
|
||||||
|
srand48(tv.tv_sec*1000000000 + tv.tv_nsec);
|
||||||
|
rand_initialized = true;
|
||||||
|
}
|
||||||
|
// Shuffle addresses
|
||||||
|
for (size_t i = etcd_urls_to_try.size()-1; i > local_to_try; i--)
|
||||||
|
{
|
||||||
|
size_t j = local_to_try + lrand48() % (i - local_to_try);
|
||||||
|
if (j != i)
|
||||||
|
std::swap(etcd_urls_to_try[i], etcd_urls_to_try[j]);
|
||||||
|
}
|
||||||
|
selected_etcd_url = std::move(etcd_urls_to_try[0]);
|
||||||
|
etcd_urls_to_try.erase(etcd_urls_to_try.begin());
|
||||||
|
auto cbs = std::move(on_resolve_queue);
|
||||||
|
for (auto cb: cbs)
|
||||||
|
{
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void etcd_state_client_t::start_etcd_watcher()
|
||||||
|
{
|
||||||
|
pick_next_etcd([this]()
|
||||||
|
{
|
||||||
|
start_etcd_watcher_selected();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void etcd_state_client_t::start_etcd_watcher_selected()
|
||||||
|
{
|
||||||
|
const auto & url = selected_etcd_url;
|
||||||
etcd_watches_initialised = 0;
|
etcd_watches_initialised = 0;
|
||||||
ws_alive = 1;
|
ws_alive = 1;
|
||||||
if (etcd_watch_ws)
|
|
||||||
{
|
|
||||||
http_close(etcd_watch_ws);
|
|
||||||
etcd_watch_ws = NULL;
|
|
||||||
}
|
|
||||||
if (this->log_level > 1)
|
if (this->log_level > 1)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Trying to connect to etcd websocket at %s, watch from revision %ju/%ju/%ju\n", etcd_address.c_str(),
|
fprintf(stderr, "Trying to connect to etcd websocket at %s%s%s (hostname %s), watch from revision %ju/%ju/%ju\n",
|
||||||
|
url.ssl ? "https://" : "http://", url.addr.c_str(), url.path.c_str(), url.hostname.c_str(),
|
||||||
etcd_watch_revision_config, etcd_watch_revision_osd, etcd_watch_revision_pg);
|
etcd_watch_revision_config, etcd_watch_revision_osd, etcd_watch_revision_pg);
|
||||||
}
|
}
|
||||||
etcd_watch_ws = open_websocket(tfd, etcd_address, etcd_api_path+"/watch", etcd_slow_timeout,
|
if (!etcd_watch_ws)
|
||||||
[this, cur_addr = selected_etcd_address](const http_response_t *msg)
|
etcd_watch_ws = http_init(get_http_ctx());
|
||||||
|
else
|
||||||
|
http_close(etcd_watch_ws);
|
||||||
|
open_websocket(etcd_watch_ws, url.addr, url.hostname, url.path+"/watch", { .timeout = etcd_slow_timeout, .ssl = url.ssl },
|
||||||
|
[this, cur_addr = url.addr](http_message_t *msg)
|
||||||
{
|
{
|
||||||
if (msg->body.length())
|
if (msg->body.length())
|
||||||
{
|
{
|
||||||
@@ -393,7 +480,6 @@ void etcd_state_client_t::start_etcd_watcher()
|
|||||||
fprintf(stderr, "Revisions before %ju were compacted by etcd, reloading state\n",
|
fprintf(stderr, "Revisions before %ju were compacted by etcd, reloading state\n",
|
||||||
data["result"]["compact_revision"].uint64_value());
|
data["result"]["compact_revision"].uint64_value());
|
||||||
http_close(etcd_watch_ws);
|
http_close(etcd_watch_ws);
|
||||||
etcd_watch_ws = NULL;
|
|
||||||
etcd_watch_revision_config = etcd_watch_revision_osd = etcd_watch_revision_pg = 0;
|
etcd_watch_revision_config = etcd_watch_revision_osd = etcd_watch_revision_pg = 0;
|
||||||
on_reload_hook();
|
on_reload_hook();
|
||||||
}
|
}
|
||||||
@@ -438,7 +524,7 @@ void etcd_state_client_t::start_etcd_watcher()
|
|||||||
etcd_watch_revision_pg = watch_rev;
|
etcd_watch_revision_pg = watch_rev;
|
||||||
else if (watch_id == ETCD_OSD_STATE_WATCH_ID)
|
else if (watch_id == ETCD_OSD_STATE_WATCH_ID)
|
||||||
etcd_watch_revision_osd = watch_rev;
|
etcd_watch_revision_osd = watch_rev;
|
||||||
addresses_to_try.clear();
|
etcd_urls_to_try.clear();
|
||||||
}
|
}
|
||||||
// First gather all changes into a hash to remove multiple overwrites
|
// First gather all changes into a hash to remove multiple overwrites
|
||||||
std::map<std::string, etcd_kv_t> changes;
|
std::map<std::string, etcd_kv_t> changes;
|
||||||
@@ -468,13 +554,8 @@ void etcd_state_client_t::start_etcd_watcher()
|
|||||||
if (msg->eof)
|
if (msg->eof)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Disconnected from etcd %s\n", cur_addr.c_str());
|
fprintf(stderr, "Disconnected from etcd %s\n", cur_addr.c_str());
|
||||||
if (cur_addr == selected_etcd_address)
|
if (cur_addr == selected_etcd_url.addr)
|
||||||
selected_etcd_address = "";
|
selected_etcd_url = (http_url_t){};
|
||||||
if (etcd_watch_ws)
|
|
||||||
{
|
|
||||||
http_close(etcd_watch_ws);
|
|
||||||
etcd_watch_ws = NULL;
|
|
||||||
}
|
|
||||||
if (etcd_watches_initialised == 0)
|
if (etcd_watches_initialised == 0)
|
||||||
{
|
{
|
||||||
// Connection not established, retry in <etcd_quick_timeout>
|
// Connection not established, retry in <etcd_quick_timeout>
|
||||||
@@ -549,12 +630,7 @@ void etcd_state_client_t::start_ws_keepalive()
|
|||||||
{
|
{
|
||||||
if (this->log_level > 0)
|
if (this->log_level > 0)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Websocket ping failed, disconnecting from etcd %s\n", selected_etcd_address.c_str());
|
fprintf(stderr, "Websocket ping failed, disconnecting from etcd %s\n", selected_etcd_url.addr.c_str());
|
||||||
}
|
|
||||||
if (etcd_watch_ws)
|
|
||||||
{
|
|
||||||
http_close(etcd_watch_ws);
|
|
||||||
etcd_watch_ws = NULL;
|
|
||||||
}
|
}
|
||||||
start_etcd_watcher();
|
start_etcd_watcher();
|
||||||
}
|
}
|
||||||
@@ -942,6 +1018,8 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
|
|||||||
pc.used_for_app = "fs:"+pc.used_for_app;
|
pc.used_for_app = "fs:"+pc.used_for_app;
|
||||||
else
|
else
|
||||||
pc.used_for_app = pool_item.second["used_for_app"].as_string();
|
pc.used_for_app = pool_item.second["used_for_app"].as_string();
|
||||||
|
// Create group permission
|
||||||
|
pc.creator_group = pool_item.second["creator_group"].string_value();
|
||||||
// Local Read Configuration
|
// Local Read Configuration
|
||||||
std::string local_reads = pool_item.second["local_reads"].string_value();
|
std::string local_reads = pool_item.second["local_reads"].string_value();
|
||||||
if (local_reads == "nearest")
|
if (local_reads == "nearest")
|
||||||
@@ -1265,33 +1343,7 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
inode_t parent_inode_num = value["parent_id"].uint64_value();
|
insert_inode_config(deserialize_inode_cfg(inode_num, kv.value, kv.mod_revision));
|
||||||
if (parent_inode_num && !(parent_inode_num >> (64-POOL_ID_BITS)))
|
|
||||||
{
|
|
||||||
uint64_t parent_pool_id = value["parent_pool"].uint64_value();
|
|
||||||
if (!parent_pool_id)
|
|
||||||
parent_inode_num |= pool_id << (64-POOL_ID_BITS);
|
|
||||||
else if (parent_pool_id >= POOL_ID_MAX)
|
|
||||||
{
|
|
||||||
fprintf(
|
|
||||||
stderr, "Inode %ju/%ju parent_pool value is invalid, ignoring parent setting\n",
|
|
||||||
inode_num >> (64-POOL_ID_BITS), inode_num & (((uint64_t)1 << (64-POOL_ID_BITS)) - 1)
|
|
||||||
);
|
|
||||||
parent_inode_num = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
parent_inode_num |= parent_pool_id << (64-POOL_ID_BITS);
|
|
||||||
}
|
|
||||||
insert_inode_config((inode_config_t){
|
|
||||||
.num = inode_num,
|
|
||||||
.name = value["name"].string_value(),
|
|
||||||
.size = value["size"].uint64_value(),
|
|
||||||
.parent_id = parent_inode_num,
|
|
||||||
.readonly = value["readonly"].bool_value(),
|
|
||||||
.deleted = value["deleted"].bool_value(),
|
|
||||||
.meta = value["meta"],
|
|
||||||
.mod_revision = kv.mod_revision,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1302,6 +1354,14 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
|
|||||||
if (on_change_node_placement_hook)
|
if (on_change_node_placement_hook)
|
||||||
on_change_node_placement_hook();
|
on_change_node_placement_hook();
|
||||||
}
|
}
|
||||||
|
else if (use_auth && key.substr(0, etcd_prefix.length()+13) == etcd_prefix+"/config/user/")
|
||||||
|
{
|
||||||
|
// <etcd_prefix>/config/user/<username>
|
||||||
|
if (!value.is_object())
|
||||||
|
user_info.erase(key.substr(etcd_prefix.length()+13));
|
||||||
|
else
|
||||||
|
user_info[key.substr(etcd_prefix.length()+13)] = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t etcd_state_client_t::parse_immediate_commit(const std::string & immediate_commit_str, uint32_t default_value)
|
uint32_t etcd_state_client_t::parse_immediate_commit(const std::string & immediate_commit_str, uint32_t default_value)
|
||||||
@@ -1380,6 +1440,10 @@ json11::Json::object etcd_state_client_t::serialize_inode_cfg(inode_config_t *cf
|
|||||||
new_cfg["parent_pool"] = (uint64_t)INODE_POOL(cfg->parent_id);
|
new_cfg["parent_pool"] = (uint64_t)INODE_POOL(cfg->parent_id);
|
||||||
new_cfg["parent_id"] = (uint64_t)INODE_NO_POOL(cfg->parent_id);
|
new_cfg["parent_id"] = (uint64_t)INODE_NO_POOL(cfg->parent_id);
|
||||||
}
|
}
|
||||||
|
if (!cfg->enc_key.empty())
|
||||||
|
{
|
||||||
|
new_cfg["enc_key"] = cfg->enc_key;
|
||||||
|
}
|
||||||
if (cfg->readonly)
|
if (cfg->readonly)
|
||||||
{
|
{
|
||||||
new_cfg["readonly"] = true;
|
new_cfg["readonly"] = true;
|
||||||
@@ -1388,6 +1452,18 @@ json11::Json::object etcd_state_client_t::serialize_inode_cfg(inode_config_t *cf
|
|||||||
{
|
{
|
||||||
new_cfg["deleted"] = true;
|
new_cfg["deleted"] = true;
|
||||||
}
|
}
|
||||||
|
if (!cfg->owner.empty())
|
||||||
|
{
|
||||||
|
new_cfg["owner"] = cfg->owner;
|
||||||
|
}
|
||||||
|
if (!cfg->owner_group.empty())
|
||||||
|
{
|
||||||
|
new_cfg["owner_group"] = cfg->owner_group;
|
||||||
|
}
|
||||||
|
if (!cfg->reader_group.empty())
|
||||||
|
{
|
||||||
|
new_cfg["reader_group"] = cfg->reader_group;
|
||||||
|
}
|
||||||
if (cfg->meta.is_object())
|
if (cfg->meta.is_object())
|
||||||
{
|
{
|
||||||
new_cfg["meta"] = cfg->meta;
|
new_cfg["meta"] = cfg->meta;
|
||||||
@@ -1395,6 +1471,53 @@ json11::Json::object etcd_state_client_t::serialize_inode_cfg(inode_config_t *cf
|
|||||||
return new_cfg;
|
return new_cfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inode_config_t etcd_state_client_t::deserialize_inode_cfg(uint64_t inode_num, json11::Json value, uint64_t mod_revision)
|
||||||
|
{
|
||||||
|
inode_t parent_inode_num = value["parent_id"].uint64_value();
|
||||||
|
if (parent_inode_num && !INODE_POOL(parent_inode_num))
|
||||||
|
{
|
||||||
|
uint64_t parent_pool_id = value["parent_pool"].uint64_value();
|
||||||
|
if (!parent_pool_id)
|
||||||
|
parent_inode_num = INODE_WITH_POOL(INODE_POOL(inode_num), parent_inode_num);
|
||||||
|
else if (parent_pool_id >= POOL_ID_MAX)
|
||||||
|
{
|
||||||
|
fprintf(
|
||||||
|
stderr, "Inode %u/%ju parent_pool value is invalid, ignoring parent setting\n",
|
||||||
|
INODE_POOL(inode_num), INODE_NO_POOL(inode_num)
|
||||||
|
);
|
||||||
|
parent_inode_num = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
parent_inode_num |= parent_pool_id << (64-POOL_ID_BITS);
|
||||||
|
}
|
||||||
|
std::string enc_key;
|
||||||
|
if (!value["enc_key"].is_null())
|
||||||
|
{
|
||||||
|
enc_key = value["enc_key"].string_value();
|
||||||
|
if (enc_key.substr(0, strlen(VAULT_KEY_PREFIX)) != VAULT_KEY_PREFIX &&
|
||||||
|
(enc_key.size() != 2*AES_256_XTS_KEY_SIZE || !ishexstr(enc_key)))
|
||||||
|
{
|
||||||
|
enc_key = "";
|
||||||
|
fprintf(stderr, "Inode %u/%ju has invalid enc_key, should be %u bit hex string or Vault key reference\n",
|
||||||
|
INODE_POOL(inode_num), INODE_NO_POOL(inode_num), AES_256_XTS_KEY_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (inode_config_t){
|
||||||
|
.num = inode_num,
|
||||||
|
.name = value["name"].string_value(),
|
||||||
|
.size = value["size"].uint64_value(),
|
||||||
|
.parent_id = parent_inode_num,
|
||||||
|
.readonly = value["readonly"].bool_value(),
|
||||||
|
.deleted = value["deleted"].bool_value(),
|
||||||
|
.enc_key = std::move(enc_key),
|
||||||
|
.owner = value["owner"].string_value(),
|
||||||
|
.owner_group = value["owner_group"].string_value(),
|
||||||
|
.reader_group = value["reader_group"].string_value(),
|
||||||
|
.meta = value["meta"],
|
||||||
|
.mod_revision = mod_revision,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
int etcd_state_client_t::address_count()
|
int etcd_state_client_t::address_count()
|
||||||
{
|
{
|
||||||
return etcd_addresses.size() + etcd_local.size();
|
return etcd_addresses.size() + etcd_local.size();
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "json11/json11.hpp"
|
#include "json11/json11.hpp"
|
||||||
#include "object_id.h"
|
#include "object_id.h"
|
||||||
@@ -19,6 +20,8 @@
|
|||||||
#define MAX_DATA_BLOCK_SIZE 128*1024*1024
|
#define MAX_DATA_BLOCK_SIZE 128*1024*1024
|
||||||
#define DEFAULT_BITMAP_GRANULARITY 4096
|
#define DEFAULT_BITMAP_GRANULARITY 4096
|
||||||
|
|
||||||
|
#define VAULT_KEY_PREFIX "vault:"
|
||||||
|
|
||||||
#ifndef IMMEDIATE_NONE
|
#ifndef IMMEDIATE_NONE
|
||||||
#define IMMEDIATE_NONE 0
|
#define IMMEDIATE_NONE 0
|
||||||
#define IMMEDIATE_SMALL 1
|
#define IMMEDIATE_SMALL 1
|
||||||
@@ -66,6 +69,7 @@ struct pool_config_t
|
|||||||
std::map<pg_num_t, pg_config_t> pg_config;
|
std::map<pg_num_t, pg_config_t> pg_config;
|
||||||
uint64_t scrub_interval = 0;
|
uint64_t scrub_interval = 0;
|
||||||
std::string used_for_app;
|
std::string used_for_app;
|
||||||
|
std::string creator_group;
|
||||||
int backfillfull = 0;
|
int backfillfull = 0;
|
||||||
int local_reads = 0;
|
int local_reads = 0;
|
||||||
|
|
||||||
@@ -83,6 +87,9 @@ struct inode_config_t
|
|||||||
inode_t parent_id = 0;
|
inode_t parent_id = 0;
|
||||||
bool readonly = false;
|
bool readonly = false;
|
||||||
bool deleted = false;
|
bool deleted = false;
|
||||||
|
std::string enc_key;
|
||||||
|
// Permissions
|
||||||
|
std::string owner, owner_group, reader_group;
|
||||||
// Arbitrary metadata
|
// Arbitrary metadata
|
||||||
json11::Json meta;
|
json11::Json meta;
|
||||||
// Change revision of the metadata in etcd
|
// Change revision of the metadata in etcd
|
||||||
@@ -95,23 +102,41 @@ struct inode_watch_t
|
|||||||
inode_config_t cfg = {};
|
inode_config_t cfg = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct http_url_t
|
||||||
|
{
|
||||||
|
bool ssl;
|
||||||
|
std::string addr;
|
||||||
|
std::string hostname;
|
||||||
|
std::string path;
|
||||||
|
};
|
||||||
|
|
||||||
struct http_co_t;
|
struct http_co_t;
|
||||||
|
struct http_context_t;
|
||||||
|
|
||||||
struct __attribute__((visibility("default"))) etcd_state_client_t
|
struct __attribute__((visibility("default"))) etcd_state_client_t
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::vector<std::string> local_ips;
|
std::set<std::string> local_ips;
|
||||||
std::vector<std::string> etcd_addresses;
|
|
||||||
std::vector<std::string> etcd_local;
|
std::vector<std::string> etcd_local;
|
||||||
std::string selected_etcd_address;
|
std::vector<std::string> etcd_addresses;
|
||||||
std::vector<std::string> addresses_to_try;
|
std::vector<http_url_t> etcd_local_addr_urls;
|
||||||
|
std::vector<http_url_t> etcd_nonlocal_addr_urls;
|
||||||
|
std::vector<http_url_t> etcd_name_urls;
|
||||||
|
size_t local_to_try = 0;
|
||||||
|
std::vector<http_url_t> etcd_urls_to_try;
|
||||||
|
http_url_t selected_etcd_url;
|
||||||
|
size_t resolve_count = 0;
|
||||||
std::vector<inode_watch_t*> watches;
|
std::vector<inode_watch_t*> watches;
|
||||||
|
std::vector<std::function<void()>> on_resolve_queue;
|
||||||
bool new_pg_config = false;
|
bool new_pg_config = false;
|
||||||
int ws_keepalive_timer = -1;
|
int ws_keepalive_timer = -1;
|
||||||
int ws_alive = 0;
|
int ws_alive = 0;
|
||||||
bool rand_initialized = false;
|
bool rand_initialized = false;
|
||||||
void add_etcd_url(std::string);
|
void add_etcd_url(std::string);
|
||||||
void pick_next_etcd();
|
void pick_next_etcd(std::function<void()> cb);
|
||||||
|
void pick_next_etcd_on_resolve();
|
||||||
|
void etcd_call_selected(const std::string & api, json11::Json payload, int timeout, int retries, int interval, std::function<void(std::string, json11::Json)> callback);
|
||||||
|
void start_etcd_watcher_selected();
|
||||||
public:
|
public:
|
||||||
int etcd_keepalive_timeout = 30;
|
int etcd_keepalive_timeout = 30;
|
||||||
int etcd_ws_keepalive_interval = 5;
|
int etcd_ws_keepalive_interval = 5;
|
||||||
@@ -120,14 +145,20 @@ public:
|
|||||||
int etcd_slow_timeout = 5000;
|
int etcd_slow_timeout = 5000;
|
||||||
int etcd_min_reload_interval = 1000;
|
int etcd_min_reload_interval = 1000;
|
||||||
bool infinite_start = true;
|
bool infinite_start = true;
|
||||||
|
bool use_auth = false;
|
||||||
uint64_t global_block_size = DEFAULT_BLOCK_SIZE;
|
uint64_t global_block_size = DEFAULT_BLOCK_SIZE;
|
||||||
uint32_t global_bitmap_granularity = DEFAULT_BITMAP_GRANULARITY;
|
uint32_t global_bitmap_granularity = DEFAULT_BITMAP_GRANULARITY;
|
||||||
uint32_t global_immediate_commit = IMMEDIATE_NONE;
|
uint32_t global_immediate_commit = IMMEDIATE_NONE;
|
||||||
|
|
||||||
|
uint64_t osd_num = 0;
|
||||||
std::string etcd_prefix;
|
std::string etcd_prefix;
|
||||||
|
std::string etcd_client_cert;
|
||||||
|
std::string etcd_client_key;
|
||||||
|
std::string etcd_ca;
|
||||||
int log_level = 0;
|
int log_level = 0;
|
||||||
timerfd_manager_t *tfd = NULL;
|
timerfd_manager_t *tfd = NULL;
|
||||||
|
|
||||||
|
http_context_t *http_ctx = NULL;
|
||||||
http_co_t *etcd_watch_ws = NULL, *keepalive_client = NULL;
|
http_co_t *etcd_watch_ws = NULL, *keepalive_client = NULL;
|
||||||
int etcd_watches_initialised = 0;
|
int etcd_watches_initialised = 0;
|
||||||
uint64_t etcd_watch_revision_config = 0;
|
uint64_t etcd_watch_revision_config = 0;
|
||||||
@@ -140,6 +171,7 @@ public:
|
|||||||
std::set<osd_num_t> seen_peers;
|
std::set<osd_num_t> seen_peers;
|
||||||
std::map<inode_t, inode_config_t> inode_config;
|
std::map<inode_t, inode_config_t> inode_config;
|
||||||
std::map<std::string, inode_t> inode_by_name;
|
std::map<std::string, inode_t> inode_by_name;
|
||||||
|
std::map<std::string, json11::Json> user_info;
|
||||||
json11::Json node_placement;
|
json11::Json node_placement;
|
||||||
|
|
||||||
std::function<void(std::map<std::string, etcd_kv_t> &)> on_change_hook;
|
std::function<void(std::map<std::string, etcd_kv_t> &)> on_change_hook;
|
||||||
@@ -158,10 +190,12 @@ public:
|
|||||||
std::function<void(http_co_t *)> on_start_watcher_hook;
|
std::function<void(http_co_t *)> on_start_watcher_hook;
|
||||||
|
|
||||||
json11::Json::object serialize_inode_cfg(inode_config_t *cfg);
|
json11::Json::object serialize_inode_cfg(inode_config_t *cfg);
|
||||||
|
inode_config_t deserialize_inode_cfg(uint64_t inode_num, json11::Json value, uint64_t mod_revision);
|
||||||
etcd_kv_t parse_etcd_kv(const json11::Json & kv_json);
|
etcd_kv_t parse_etcd_kv(const json11::Json & kv_json);
|
||||||
std::vector<std::string> get_addresses();
|
std::vector<std::string> get_addresses();
|
||||||
void etcd_call_oneshot(std::string etcd_address, std::string api, json11::Json payload, int timeout, std::function<void(std::string, json11::Json)> callback);
|
http_context_t *get_http_ctx();
|
||||||
void etcd_call(std::string api, json11::Json payload, int timeout, int retries, int interval, std::function<void(std::string, json11::Json)> callback);
|
void etcd_call_oneshot(const std::string & etcd_address, const std::string & api, json11::Json payload, int timeout, std::function<void(std::string, json11::Json)> callback);
|
||||||
|
void etcd_call(const std::string & api, json11::Json payload, int timeout, int retries, int interval, std::function<void(std::string, json11::Json)> callback);
|
||||||
void etcd_txn(json11::Json txn, int timeout, int retries, int interval, std::function<void(std::string, json11::Json)> callback);
|
void etcd_txn(json11::Json txn, int timeout, int retries, int interval, std::function<void(std::string, json11::Json)> callback);
|
||||||
void etcd_txn_slow(json11::Json txn, std::function<void(std::string, json11::Json)> callback);
|
void etcd_txn_slow(json11::Json txn, std::function<void(std::string, json11::Json)> callback);
|
||||||
void start_etcd_watcher();
|
void start_etcd_watcher();
|
||||||
|
|||||||
+726
-87
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,10 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include "json11/json11.hpp"
|
#include "json11/json11.hpp"
|
||||||
|
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
#include <openssl/types.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define WS_CONTINUATION 0
|
#define WS_CONTINUATION 0
|
||||||
#define WS_TEXT 1
|
#define WS_TEXT 1
|
||||||
#define WS_BINARY 2
|
#define WS_BINARY 2
|
||||||
@@ -17,14 +21,19 @@
|
|||||||
|
|
||||||
class timerfd_manager_t;
|
class timerfd_manager_t;
|
||||||
|
|
||||||
|
#pragma GCC visibility push(default)
|
||||||
|
|
||||||
struct http_options_t
|
struct http_options_t
|
||||||
{
|
{
|
||||||
int timeout;
|
int timeout;
|
||||||
bool want_streaming;
|
bool want_streaming;
|
||||||
bool keepalive;
|
bool keepalive;
|
||||||
|
bool ssl;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct http_response_t
|
struct http_context_t;
|
||||||
|
|
||||||
|
struct http_message_t
|
||||||
{
|
{
|
||||||
std::string error;
|
std::string error;
|
||||||
|
|
||||||
@@ -41,10 +50,34 @@ struct http_response_t
|
|||||||
// Opened websocket or keepalive HTTP connection
|
// Opened websocket or keepalive HTTP connection
|
||||||
struct http_co_t;
|
struct http_co_t;
|
||||||
|
|
||||||
http_co_t* http_init(timerfd_manager_t *tfd);
|
http_context_t* http_context_init(timerfd_manager_t *tfd, const std::string & ssl_cert, const std::string & ssl_key,
|
||||||
http_co_t* open_websocket(timerfd_manager_t *tfd, const std::string & host, const std::string & path,
|
const std::string & ssl_ca, bool verify_peer, std::string & error);
|
||||||
int timeout, std::function<void(const http_response_t *msg)> on_message);
|
std::string http_context_get_ssl_cn(http_context_t *ctx);
|
||||||
|
void http_resolve(http_context_t *ctx, bool ssl, std::string host,
|
||||||
|
std::function<void(const std::string & error, const std::vector<std::string> & addrs)> cb);
|
||||||
|
void http_context_destroy(http_context_t *ctx);
|
||||||
|
http_co_t* http_init(http_context_t *ctx = NULL);
|
||||||
|
void open_websocket(http_co_t *handler, const std::string & addr, const std::string & hostname, const std::string & path,
|
||||||
|
const http_options_t & options, std::function<void(http_message_t *msg)> on_message);
|
||||||
void http_request(http_co_t *handler, const std::string & host, const std::string & request,
|
void http_request(http_co_t *handler, const std::string & host, const std::string & request,
|
||||||
const http_options_t & options, std::function<void(const http_response_t *response)> response_callback);
|
const http_options_t & options, std::function<void(http_message_t *response)> response_callback);
|
||||||
|
void http_get(http_co_t *handler, const std::string & url, const std::string & headers,
|
||||||
|
const http_options_t & options, std::function<void(http_message_t *response)> response_callback);
|
||||||
|
void http_json_post(http_co_t *handler, const std::string & url, json11::Json body, const std::string & headers,
|
||||||
|
const http_options_t & options, std::function<void(http_message_t *response)> response_callback);
|
||||||
void http_post_message(http_co_t *handler, uint8_t type, const std::string & msg);
|
void http_post_message(http_co_t *handler, uint8_t type, const std::string & msg);
|
||||||
|
void http_serve(http_co_t *handler, int peer_fd, const http_options_t & options,
|
||||||
|
std::function<void(http_message_t *msg)> request_callback);
|
||||||
|
void http_reply(http_co_t *handler, const std::string & reply);
|
||||||
void http_close(http_co_t *co);
|
void http_close(http_co_t *co);
|
||||||
|
void http_destroy(http_co_t *co);
|
||||||
|
|
||||||
|
#pragma GCC visibility pop
|
||||||
|
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
bool openssl_ctx_add_ca(SSL_CTX *ssl_ctx, const std::string & file_or_pem);
|
||||||
|
bool openssl_ctx_use_ca(SSL_CTX *ssl_ctx, const std::string & file_or_pem);
|
||||||
|
std::string openssl_get_cn(X509 *x509);
|
||||||
|
bool openssl_ctx_use_cert(SSL_CTX *ssl_ctx, const std::string & file_or_pem, std::string & common_name);
|
||||||
|
bool openssl_ctx_use_key(SSL_CTX *ssl_ctx, const std::string & file_or_pem);
|
||||||
|
#endif
|
||||||
|
|||||||
+127
-3
@@ -14,6 +14,13 @@
|
|||||||
#ifdef WITH_RDMA
|
#ifdef WITH_RDMA
|
||||||
#include "msgr_rdma.h"
|
#include "msgr_rdma.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include "http_client.h"
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
#include <openssl/bio.h>
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#include <openssl/pem.h>
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
|
|
||||||
@@ -117,6 +124,43 @@ void msgr_iothread_t::run()
|
|||||||
|
|
||||||
void osd_messenger_t::init()
|
void osd_messenger_t::init()
|
||||||
{
|
{
|
||||||
|
if (!tls_cert.empty() || !tls_key.empty() || !osd_tls_ca.empty() || !client_tls_ca.empty())
|
||||||
|
{
|
||||||
|
// Initialize TLS context
|
||||||
|
// FIXME: require OpenSSL
|
||||||
|
#ifndef WITH_OPENSSL
|
||||||
|
fprintf(stderr, "Vitastor is built without OpenSSL support\n");
|
||||||
|
exit(1);
|
||||||
|
#else
|
||||||
|
if (tls_cert.empty() || tls_key.empty() || osd_tls_ca.empty() || osd_num && client_tls_ca.empty())
|
||||||
|
{
|
||||||
|
if (osd_num)
|
||||||
|
fprintf(stderr, "Vitastor OSD TLS requires osd_tls_cert, osd_tls_key, osd_tls_ca, client_tls_ca\n");
|
||||||
|
else
|
||||||
|
fprintf(stderr, "Vitastor client TLS requires tls_cert, tls_key and osd_tls_ca\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ssl_ctx = SSL_CTX_new(TLS_method());
|
||||||
|
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
|
||||||
|
bool ok = SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
|
||||||
|
ok = ok && openssl_ctx_add_ca(ssl_ctx, osd_tls_ca);
|
||||||
|
if (osd_num)
|
||||||
|
{
|
||||||
|
// OSD uses 2 separate root certificates to distinguish between clients and peer OSDs
|
||||||
|
ok = ok && openssl_ctx_add_ca(ssl_ctx, client_tls_ca);
|
||||||
|
}
|
||||||
|
ok = ok && openssl_ctx_use_cert(ssl_ctx, tls_cert, tls_cn);
|
||||||
|
ok = ok && openssl_ctx_use_key(ssl_ctx, tls_key);
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
SSL_CTX_free(ssl_ctx);
|
||||||
|
ssl_ctx = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
#ifdef WITH_RDMACM
|
#ifdef WITH_RDMACM
|
||||||
if (use_rdmacm)
|
if (use_rdmacm)
|
||||||
{
|
{
|
||||||
@@ -294,6 +338,21 @@ osd_messenger_t::~osd_messenger_t()
|
|||||||
rdma_destroy_event_channel(rdmacm_evch);
|
rdma_destroy_event_channel(rdmacm_evch);
|
||||||
rdmacm_evch = NULL;
|
rdmacm_evch = NULL;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
for (auto encrypt_ctx: encrypt_ctx_pool)
|
||||||
|
{
|
||||||
|
destroy_aes_xts_encrypt(encrypt_ctx);
|
||||||
|
}
|
||||||
|
for (auto decrypt_ctx: decrypt_ctx_pool)
|
||||||
|
{
|
||||||
|
destroy_aes_xts_decrypt(decrypt_ctx);
|
||||||
|
}
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
if (ssl_ctx)
|
||||||
|
{
|
||||||
|
SSL_CTX_free(ssl_ctx);
|
||||||
|
ssl_ctx = NULL;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,6 +388,30 @@ void osd_messenger_t::parse_config(const json11::Json & config)
|
|||||||
if (!this->rdma_max_msg || this->rdma_max_msg > 128*1024*1024)
|
if (!this->rdma_max_msg || this->rdma_max_msg > 128*1024*1024)
|
||||||
this->rdma_max_msg = 129*1024;
|
this->rdma_max_msg = 129*1024;
|
||||||
#endif
|
#endif
|
||||||
|
this->max_aes_xts_pool_size = config["max_aes_xts_pool_size"].uint64_value();
|
||||||
|
if (!this->max_aes_xts_pool_size)
|
||||||
|
this->max_aes_xts_pool_size = 256;
|
||||||
|
if (config["proto_checksums"].is_null())
|
||||||
|
this->use_proto_checksums = MSGR_CSUM_PAYLOAD;
|
||||||
|
else if (config["proto_checksums"].is_bool())
|
||||||
|
this->use_proto_checksums = config["proto_checksums"].bool_value() ? MSGR_CSUM_FULL : 0;
|
||||||
|
else if (config["proto_checksums"].string_value() != "")
|
||||||
|
this->use_proto_checksums = config["proto_checksums"].string_value() == "full" ? MSGR_CSUM_FULL : MSGR_CSUM_PAYLOAD;
|
||||||
|
else
|
||||||
|
this->use_proto_checksums = 0;
|
||||||
|
if (!osd_num)
|
||||||
|
{
|
||||||
|
tls_cert = config["tls_cert"].string_value();
|
||||||
|
tls_key = config["tls_key"].string_value();
|
||||||
|
osd_tls_ca = config["osd_tls_ca"].string_value();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tls_cert = config["osd_tls_cert"].string_value();
|
||||||
|
tls_key = config["osd_tls_key"].string_value();
|
||||||
|
osd_tls_ca = config["osd_tls_ca"].string_value();
|
||||||
|
client_tls_ca = config["client_tls_ca"].string_value();
|
||||||
|
}
|
||||||
if (!osd_num)
|
if (!osd_num)
|
||||||
this->iothread_count = (uint32_t)config["client_iothread_count"].uint64_value();
|
this->iothread_count = (uint32_t)config["client_iothread_count"].uint64_value();
|
||||||
else
|
else
|
||||||
@@ -510,7 +593,7 @@ void osd_messenger_t::try_connect_peer_tcp(osd_num_t peer_osd, const char *peer_
|
|||||||
cl->peer_state = PEER_CONNECTING;
|
cl->peer_state = PEER_CONNECTING;
|
||||||
cl->connect_timeout_id = -1;
|
cl->connect_timeout_id = -1;
|
||||||
cl->osd_num = peer_osd;
|
cl->osd_num = peer_osd;
|
||||||
cl->in_buf = malloc_or_die(receive_buffer_size);
|
cl->in_buf = (uint8_t*)malloc_or_die(receive_buffer_size);
|
||||||
clients[client_id] = cl;
|
clients[client_id] = cl;
|
||||||
clients_by_fd[peer_fd] = cl;
|
clients_by_fd[peer_fd] = cl;
|
||||||
tfd->set_fd_handler(peer_fd, true, [this](int peer_fd, int epoll_events)
|
tfd->set_fd_handler(peer_fd, true, [this](int peer_fd, int epoll_events)
|
||||||
@@ -559,6 +642,10 @@ void osd_messenger_t::handle_connect_epoll(int peer_fd)
|
|||||||
handle_peer_epoll(peer_fd, epoll_events);
|
handle_peer_epoll(peer_fd, epoll_events);
|
||||||
});
|
});
|
||||||
// Check OSD number
|
// Check OSD number
|
||||||
|
if (!tls_cert.empty())
|
||||||
|
{
|
||||||
|
ssl_init(cl, false);
|
||||||
|
}
|
||||||
check_peer_config(cl);
|
check_peer_config(cl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -648,7 +735,12 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
|
|||||||
// Inform that we're OSD <osd_num>
|
// Inform that we're OSD <osd_num>
|
||||||
payload["osd_num"] = osd_num;
|
payload["osd_num"] = osd_num;
|
||||||
}
|
}
|
||||||
payload["features"] = json11::Json::object{ { "check_sequencing", true } };
|
auto features = json11::Json::object{ { "check_sequencing", true } };
|
||||||
|
if (use_proto_checksums)
|
||||||
|
{
|
||||||
|
features["proto_checksums"] = use_proto_checksums;
|
||||||
|
}
|
||||||
|
payload["features"] = features;
|
||||||
#ifdef WITH_RDMA
|
#ifdef WITH_RDMA
|
||||||
if (!use_rdmacm && rdma_contexts.size())
|
if (!use_rdmacm && rdma_contexts.size())
|
||||||
{
|
{
|
||||||
@@ -723,6 +815,14 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
|
|||||||
delete op;
|
delete op;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (use_proto_checksums)
|
||||||
|
{
|
||||||
|
auto peer_csums = config["features"]["proto_checksums"].uint64_value();
|
||||||
|
if (peer_csums == MSGR_CSUM_FULL && use_proto_checksums == MSGR_CSUM_FULL)
|
||||||
|
cl->proto_csum_status = MSGR_CSUM_FULL;
|
||||||
|
else if (peer_csums && use_proto_checksums)
|
||||||
|
cl->proto_csum_status = MSGR_CSUM_PAYLOAD;
|
||||||
|
}
|
||||||
#ifdef WITH_RDMA
|
#ifdef WITH_RDMA
|
||||||
if (!use_rdmacm && cl->rdma_conn && config["rdma_address"].is_string())
|
if (!use_rdmacm && cl->rdma_conn && config["rdma_address"].is_string())
|
||||||
{
|
{
|
||||||
@@ -786,7 +886,11 @@ void osd_messenger_t::accept_connections(int listen_fd)
|
|||||||
cl->peer_port = ntohs(((sockaddr_in*)&addr)->sin_port);
|
cl->peer_port = ntohs(((sockaddr_in*)&addr)->sin_port);
|
||||||
cl->peer_fd = peer_fd;
|
cl->peer_fd = peer_fd;
|
||||||
cl->peer_state = PEER_CONNECTED;
|
cl->peer_state = PEER_CONNECTED;
|
||||||
cl->in_buf = malloc_or_die(receive_buffer_size);
|
cl->in_buf = (uint8_t*)malloc_or_die(receive_buffer_size);
|
||||||
|
if (!tls_cert.empty())
|
||||||
|
{
|
||||||
|
ssl_init(cl, true);
|
||||||
|
}
|
||||||
// Add FD to epoll
|
// Add FD to epoll
|
||||||
tfd->set_fd_handler(peer_fd, false, [this](int peer_fd, int epoll_events)
|
tfd->set_fd_handler(peer_fd, false, [this](int peer_fd, int epoll_events)
|
||||||
{
|
{
|
||||||
@@ -801,6 +905,26 @@ void osd_messenger_t::accept_connections(int listen_fd)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void osd_messenger_t::ssl_init(osd_client_t *cl, bool server_mode)
|
||||||
|
{
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
cl->write_to_ssl = BIO_new(BIO_s_mem());
|
||||||
|
cl->read_from_ssl = BIO_new(BIO_s_mem());
|
||||||
|
cl->ssl_cli = SSL_new(ssl_ctx);
|
||||||
|
if (server_mode)
|
||||||
|
{
|
||||||
|
SSL_set_accept_state(cl->ssl_cli);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SSL_set_connect_state(cl->ssl_cli);
|
||||||
|
}
|
||||||
|
SSL_set_bio(cl->ssl_cli, cl->write_to_ssl, cl->read_from_ssl);
|
||||||
|
bool ok = ssl_do_handshake(cl);
|
||||||
|
assert(ok);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef WITH_RDMA
|
#ifdef WITH_RDMA
|
||||||
msgr_rdma_context_t* osd_messenger_t::choose_rdma_context(osd_client_t *cl)
|
msgr_rdma_context_t* osd_messenger_t::choose_rdma_context(osd_client_t *cl)
|
||||||
{
|
{
|
||||||
|
|||||||
+93
-15
@@ -12,6 +12,11 @@
|
|||||||
#include <deque>
|
#include <deque>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
#include <openssl/types.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "../util/xxh_x86dispatch.h"
|
||||||
#include "../util/robin_hood.h"
|
#include "../util/robin_hood.h"
|
||||||
#include "malloc_or_die.h"
|
#include "malloc_or_die.h"
|
||||||
#include "json11/json11.hpp"
|
#include "json11/json11.hpp"
|
||||||
@@ -31,13 +36,14 @@
|
|||||||
#define PEER_RDMA 4
|
#define PEER_RDMA 4
|
||||||
#define PEER_STOPPED 5
|
#define PEER_STOPPED 5
|
||||||
|
|
||||||
|
#define MSGR_CSUM_PAYLOAD 1
|
||||||
|
#define MSGR_CSUM_FULL 2
|
||||||
|
#define MSGR_CSUM_NEG 4
|
||||||
|
|
||||||
#define VITASTOR_CONFIG_PATH "/etc/vitastor/vitastor.conf"
|
#define VITASTOR_CONFIG_PATH "/etc/vitastor/vitastor.conf"
|
||||||
|
|
||||||
#define DEFAULT_MIN_ZEROCOPY_SEND_SIZE 32*1024
|
#define DEFAULT_MIN_ZEROCOPY_SEND_SIZE 32*1024
|
||||||
|
|
||||||
#define MSGR_SENDP_HDR 1
|
|
||||||
#define MSGR_SENDP_FREE 2
|
|
||||||
|
|
||||||
struct msgr_sendp_t
|
struct msgr_sendp_t
|
||||||
{
|
{
|
||||||
osd_op_t *op;
|
osd_op_t *op;
|
||||||
@@ -49,6 +55,17 @@ struct msgr_rdma_connection_t;
|
|||||||
struct msgr_rdma_context_t;
|
struct msgr_rdma_context_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct op_aes_xts_encrypt_t;
|
||||||
|
struct op_aes_xts_decrypt_t;
|
||||||
|
void destroy_aes_xts_encrypt(op_aes_xts_encrypt_t *encrypt_ctx);
|
||||||
|
void destroy_aes_xts_decrypt(op_aes_xts_decrypt_t *decrypt_ctx);
|
||||||
|
|
||||||
|
struct __attribute__((__packed__)) msgr_tls_record_hdr_t
|
||||||
|
{
|
||||||
|
uint8_t encrypted;
|
||||||
|
uint32_t size;
|
||||||
|
};
|
||||||
|
|
||||||
struct osd_client_t
|
struct osd_client_t
|
||||||
{
|
{
|
||||||
uint64_t client_id = 0;
|
uint64_t client_id = 0;
|
||||||
@@ -65,23 +82,40 @@ struct osd_client_t
|
|||||||
osd_num_t in_osd_num = 0;
|
osd_num_t in_osd_num = 0;
|
||||||
bool is_incoming = false;
|
bool is_incoming = false;
|
||||||
|
|
||||||
void *in_buf = NULL;
|
uint8_t *in_buf = NULL;
|
||||||
|
|
||||||
#ifdef WITH_RDMA
|
#ifdef WITH_RDMA
|
||||||
msgr_rdma_connection_t *rdma_conn = NULL;
|
msgr_rdma_connection_t *rdma_conn = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
SSL *ssl_cli = NULL;
|
||||||
|
BIO *write_to_ssl = NULL;
|
||||||
|
// FIXME: use custom bio to avoid 1 more memory copy?
|
||||||
|
BIO *read_from_ssl = NULL;
|
||||||
|
uint8_t *ssl_out_buf = NULL;
|
||||||
|
size_t ssl_out_buf_size = 0, ssl_out_buf_cap = 0;
|
||||||
|
bool ssl_handshake_done = false;
|
||||||
|
bool ssl_want_write = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Read state
|
// Read state
|
||||||
int read_ready = 0;
|
int read_ready = 0;
|
||||||
osd_op_t *read_op = NULL;
|
osd_op_t *read_op = NULL;
|
||||||
|
size_t read_op_size = 0;
|
||||||
|
size_t read_op_pos = 0;
|
||||||
iovec read_iov = { 0 };
|
iovec read_iov = { 0 };
|
||||||
msghdr read_msg = { 0 };
|
msghdr read_msg = { 0 };
|
||||||
int read_remaining = 0;
|
std::vector<iovec> recv_list;
|
||||||
int read_state = 0;
|
size_t recv_list_size = 0;
|
||||||
osd_op_buf_list_t recv_list;
|
|
||||||
uint64_t read_op_id = 1;
|
uint64_t read_op_id = 1;
|
||||||
bool check_sequencing = false;
|
bool check_sequencing = false;
|
||||||
bool enable_pg_locks = false;
|
bool enable_pg_locks = false;
|
||||||
|
op_aes_xts_decrypt_t *decrypt_ctx = NULL;
|
||||||
|
size_t read_op_inline_decrypt_pos = 0;
|
||||||
|
size_t read_op_inline_decrypt_in = 0;
|
||||||
|
int proto_csum_status = 0;
|
||||||
|
XXH3_state_t* read_csum_state = NULL;
|
||||||
|
|
||||||
// Incoming operations
|
// Incoming operations
|
||||||
std::vector<osd_op_t*> received_ops;
|
std::vector<osd_op_t*> received_ops;
|
||||||
@@ -94,11 +128,17 @@ struct osd_client_t
|
|||||||
std::set<pool_pg_num_t> dirty_pgs;
|
std::set<pool_pg_num_t> dirty_pgs;
|
||||||
|
|
||||||
// Write state
|
// Write state
|
||||||
|
std::deque<osd_op_t *> write_ops;
|
||||||
|
osd_op_t *write_op = NULL;
|
||||||
|
size_t write_op_pos = 0;
|
||||||
msghdr write_msg = { 0 };
|
msghdr write_msg = { 0 };
|
||||||
int write_state = 0;
|
int write_state = 0;
|
||||||
std::vector<iovec> send_list, next_send_list;
|
std::vector<iovec> send_list;
|
||||||
std::vector<msgr_sendp_t> outbox, next_outbox;
|
size_t send_list_size = 0;
|
||||||
|
std::deque<osd_op_t*> send_free_ops;
|
||||||
std::vector<osd_op_t*> zc_free_list;
|
std::vector<osd_op_t*> zc_free_list;
|
||||||
|
op_aes_xts_encrypt_t *encrypt_ctx = NULL;
|
||||||
|
XXH3_state_t* write_csum_state = NULL;
|
||||||
|
|
||||||
~osd_client_t();
|
~osd_client_t();
|
||||||
void cancel_ops();
|
void cancel_ops();
|
||||||
@@ -190,6 +230,12 @@ protected:
|
|||||||
bool use_sync_send_recv = false;
|
bool use_sync_send_recv = false;
|
||||||
int min_zerocopy_send_size = DEFAULT_MIN_ZEROCOPY_SEND_SIZE;
|
int min_zerocopy_send_size = DEFAULT_MIN_ZEROCOPY_SEND_SIZE;
|
||||||
int iothread_count = 0;
|
int iothread_count = 0;
|
||||||
|
int max_aes_xts_pool_size = 256;
|
||||||
|
|
||||||
|
std::string tls_cert;
|
||||||
|
std::string tls_key;
|
||||||
|
std::string osd_tls_ca;
|
||||||
|
std::string client_tls_ca;
|
||||||
|
|
||||||
#ifdef WITH_RDMA
|
#ifdef WITH_RDMA
|
||||||
bool use_rdma = true;
|
bool use_rdma = true;
|
||||||
@@ -207,12 +253,28 @@ protected:
|
|||||||
robin_hood::unordered_flat_map<rdma_cm_id*, rdmacm_connecting_t*> rdmacm_connecting;
|
robin_hood::unordered_flat_map<rdma_cm_id*, rdmacm_connecting_t*> rdmacm_connecting;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
SSL_CTX *ssl_ctx = NULL;
|
||||||
|
std::string tls_cn;
|
||||||
|
|
||||||
|
void ssl_init(osd_client_t *cl, bool server_mode);
|
||||||
|
bool ssl_do_handshake(osd_client_t *cl);
|
||||||
|
bool ssl_do_encrypt(osd_client_t *cl);
|
||||||
|
size_t ssl_do_encrypt_to(osd_client_t *cl, uint8_t *buf, size_t size);
|
||||||
|
bool ssl_op_write_buf(osd_client_t *cl, uint8_t *src, size_t src_len, bool skip_csum, size_t & from, size_t & done);
|
||||||
|
size_t ssl_op_copy_to(osd_client_t *cl, uint8_t *dst, size_t dst_len);
|
||||||
|
void ssl_op_get_write_buffers(osd_client_t *cl, std::vector<iovec> & lst);
|
||||||
|
#endif
|
||||||
|
|
||||||
std::vector<msgr_iothread_t*> iothreads;
|
std::vector<msgr_iothread_t*> iothreads;
|
||||||
std::vector<uint64_t> read_ready_clients;
|
std::vector<uint64_t> read_ready_clients;
|
||||||
std::vector<uint64_t> write_ready_clients;
|
std::vector<uint64_t> write_ready_clients;
|
||||||
// We don't use ringloop->set_immediate here because we may have no ringloop in client :)
|
// We don't use ringloop->set_immediate here because we may have no ringloop in client :)
|
||||||
std::deque<osd_op_t*> set_immediate_ops;
|
std::deque<osd_op_t*> set_immediate_ops;
|
||||||
|
|
||||||
|
std::vector<op_aes_xts_encrypt_t*> encrypt_ctx_pool;
|
||||||
|
std::vector<op_aes_xts_decrypt_t*> decrypt_ctx_pool;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
timerfd_manager_t *tfd = NULL;
|
timerfd_manager_t *tfd = NULL;
|
||||||
ring_loop_t *ringloop = NULL;
|
ring_loop_t *ringloop = NULL;
|
||||||
@@ -230,6 +292,7 @@ public:
|
|||||||
std::vector<addr_mask_t> osd_cluster_network_masks;
|
std::vector<addr_mask_t> osd_cluster_network_masks;
|
||||||
std::vector<std::string> all_osd_networks;
|
std::vector<std::string> all_osd_networks;
|
||||||
std::vector<addr_mask_t> all_osd_network_masks;
|
std::vector<addr_mask_t> all_osd_network_masks;
|
||||||
|
int use_proto_checksums = 0;
|
||||||
// op statistics
|
// op statistics
|
||||||
osd_op_stats_t stats, recovery_stats;
|
osd_op_stats_t stats, recovery_stats;
|
||||||
|
|
||||||
@@ -279,17 +342,32 @@ protected:
|
|||||||
|
|
||||||
bool try_send(osd_client_t *cl);
|
bool try_send(osd_client_t *cl);
|
||||||
void handle_send(int result, bool prev, bool more, osd_client_t *cl);
|
void handle_send(int result, bool prev, bool more, osd_client_t *cl);
|
||||||
|
size_t op_copy_to(osd_client_t *cl, uint8_t *dst, size_t dst_len);
|
||||||
|
void op_get_write_buffers(osd_client_t *cl, std::vector<iovec> & lst);
|
||||||
|
void next_write_op(osd_client_t *cl);
|
||||||
|
bool op_write_buf(osd_client_t *cl, uint8_t *src, size_t src_len, uint8_t *dst, size_t dst_len, bool skip_csum, size_t & from, size_t & done);
|
||||||
|
bool op_copy_data_to(osd_client_t *cl, uint8_t *dst, size_t dst_len, size_t & from, size_t & done);
|
||||||
|
|
||||||
bool handle_read(int result, osd_client_t *cl);
|
void handle_read(int result, osd_client_t *cl);
|
||||||
bool handle_read_buffer(osd_client_t *cl, void *curbuf, int remain);
|
bool handle_read_buffer(osd_client_t *cl, uint8_t *curbuf, size_t bufsize);
|
||||||
bool handle_finished_read(osd_client_t *cl);
|
bool handle_hdr(osd_client_t *cl);
|
||||||
void handle_op_hdr(osd_client_t *cl);
|
bool allocate_op_buffers(osd_client_t *cl);
|
||||||
bool handle_reply_hdr(osd_client_t *cl);
|
bool allocate_reply_buffers(osd_client_t *cl, osd_op_t *op);
|
||||||
void handle_reply_ready(osd_op_t *op);
|
bool op_copy_from(osd_client_t *cl, uint8_t *src, size_t src_len, size_t & done);
|
||||||
|
void op_get_read_buffers(osd_client_t *cl, std::vector<iovec> & lst);
|
||||||
|
void op_alloc_temp_buffers(osd_op_t *op, int i);
|
||||||
|
bool handle_finished_op(osd_client_t *cl);
|
||||||
void handle_immediate_ops();
|
void handle_immediate_ops();
|
||||||
|
|
||||||
|
bool op_encrypted_copy_data_to(osd_client_t* cl, uint8_t *buf, size_t len, size_t from, size_t & done);
|
||||||
|
bool op_decrypted_copy_data_from(osd_client_t* cl, uint8_t *buf, size_t len, size_t from, size_t & done);
|
||||||
|
void op_decrypt_start(osd_client_t* cl);
|
||||||
|
void op_decrypt_inline(osd_client_t* cl);
|
||||||
|
void op_decrypt_free(osd_client_t* cl);
|
||||||
|
|
||||||
#ifdef WITH_RDMA
|
#ifdef WITH_RDMA
|
||||||
void try_send_rdma(osd_client_t *cl);
|
void try_send_rdma(osd_client_t *cl);
|
||||||
|
int try_send_rdma_copy(osd_client_t *cl, uint8_t *dst, int dst_len);
|
||||||
bool init_recv_rdma(osd_client_t *cl);
|
bool init_recv_rdma(osd_client_t *cl);
|
||||||
void handle_rdma_events(msgr_rdma_context_t *rdma_context);
|
void handle_rdma_events(msgr_rdma_context_t *rdma_context);
|
||||||
msgr_rdma_context_t* choose_rdma_context(osd_client_t *cl);
|
msgr_rdma_context_t* choose_rdma_context(osd_client_t *cl);
|
||||||
|
|||||||
@@ -0,0 +1,484 @@
|
|||||||
|
// Copyright (c) Vitaliy Filippov, 2026+
|
||||||
|
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "etcd_state_client.h"
|
||||||
|
#include "messenger.h"
|
||||||
|
#include "msgr_encrypt.h"
|
||||||
|
|
||||||
|
op_aes_xts_encrypt_t::op_aes_xts_encrypt_t()
|
||||||
|
{
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
if (!(ctx = EVP_CIPHER_CTX_new()))
|
||||||
|
{
|
||||||
|
ERR_print_errors_fp(stderr);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
EVP_CIPHER_CTX_set_padding(ctx, 0);
|
||||||
|
if (EVP_EncryptInit_ex(ctx, EVP_aes_256_xts(), NULL, NULL, NULL) != 1)
|
||||||
|
{
|
||||||
|
ERR_print_errors_fp(stderr);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
fprintf(stderr, "Error: Vitastor is built without encryption support\n");
|
||||||
|
abort();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
op_aes_xts_encrypt_t::~op_aes_xts_encrypt_t()
|
||||||
|
{
|
||||||
|
assert(!encrypted);
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
#endif
|
||||||
|
if (tmp)
|
||||||
|
free(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_aes_xts_encrypt_t::start(uint8_t *key, uint64_t start_offset, size_t block_size)
|
||||||
|
{
|
||||||
|
assert(!encrypted);
|
||||||
|
this->start_offset = start_offset;
|
||||||
|
this->key = key;
|
||||||
|
this->block_size = block_size;
|
||||||
|
this->offset = 0;
|
||||||
|
this->encrypted = false;
|
||||||
|
this->tmp_pos = 0;
|
||||||
|
if (tmp && tmp_size != block_size)
|
||||||
|
{
|
||||||
|
free(tmp);
|
||||||
|
tmp = NULL;
|
||||||
|
tmp_size = 0;
|
||||||
|
}
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
if (EVP_EncryptInit_ex(ctx, NULL, NULL, key, NULL) != 1)
|
||||||
|
{
|
||||||
|
ERR_print_errors_fp(stderr);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_aes_xts_encrypt_t::encrypt_block(uint8_t *in, uint8_t *out)
|
||||||
|
{
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
uint8_t iv[16] = { 0 };
|
||||||
|
*((uint64_t*)iv) = start_offset + offset - offset%block_size;
|
||||||
|
if (EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, iv) != 1)
|
||||||
|
{
|
||||||
|
ERR_print_errors_fp(stderr);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
int actual_out = 0;
|
||||||
|
if (EVP_EncryptUpdate(ctx, out, &actual_out, in, block_size) != 1)
|
||||||
|
{
|
||||||
|
ERR_print_errors_fp(stderr);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
assert(actual_out == block_size);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_aes_xts_encrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size_t max_out, size_t & done_in, size_t & done_out)
|
||||||
|
{
|
||||||
|
// Fucking AES-XTS implementations (all of them) don't have streaming support,
|
||||||
|
// crafting IV to resume encryption is slow, so we have to accumulate a full block
|
||||||
|
// and encrypt it at once :-(
|
||||||
|
// And then we have to support consuming it in parts because it's simpler for the
|
||||||
|
// higher layers.
|
||||||
|
if (encrypted)
|
||||||
|
{
|
||||||
|
// Copy accumulated and encrypted output
|
||||||
|
assert(tmp);
|
||||||
|
if (max_out > block_size - tmp_pos)
|
||||||
|
max_out = block_size - tmp_pos;
|
||||||
|
memcpy(out, tmp + tmp_pos, max_out);
|
||||||
|
done_out += max_out;
|
||||||
|
tmp_pos += max_out;
|
||||||
|
if (tmp_pos >= block_size)
|
||||||
|
encrypted = false;
|
||||||
|
}
|
||||||
|
else if (max_in < block_size - offset%block_size)
|
||||||
|
{
|
||||||
|
// Just accumulate input
|
||||||
|
if (!tmp)
|
||||||
|
{
|
||||||
|
tmp = (uint8_t*)malloc_or_die(block_size);
|
||||||
|
tmp_size = block_size;
|
||||||
|
}
|
||||||
|
memcpy(tmp + offset%block_size, in, max_in);
|
||||||
|
done_in += max_in;
|
||||||
|
offset += max_in;
|
||||||
|
}
|
||||||
|
else if (max_out < block_size)
|
||||||
|
{
|
||||||
|
// Accumulate and encrypt input in <tmp>, then copy part of it to <out>
|
||||||
|
if (!tmp)
|
||||||
|
{
|
||||||
|
tmp = (uint8_t*)malloc_or_die(block_size);
|
||||||
|
tmp_size = block_size;
|
||||||
|
}
|
||||||
|
max_in = block_size - offset%block_size;
|
||||||
|
memcpy(tmp + offset%block_size, in, max_in);
|
||||||
|
encrypt_block(tmp, tmp);
|
||||||
|
encrypted = true;
|
||||||
|
memcpy(out, tmp, max_out);
|
||||||
|
tmp_pos = max_out;
|
||||||
|
done_in += max_in;
|
||||||
|
offset += max_in;
|
||||||
|
done_out += max_out;
|
||||||
|
}
|
||||||
|
else if (!(offset%block_size))
|
||||||
|
{
|
||||||
|
// Full block - simplest case
|
||||||
|
encrypt_block(in, out);
|
||||||
|
done_in += block_size;
|
||||||
|
offset += block_size;
|
||||||
|
done_out += block_size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Accumulate input and encrypt directly to <output>
|
||||||
|
assert(tmp);
|
||||||
|
max_in = block_size - offset%block_size;
|
||||||
|
memcpy(tmp + offset%block_size, in, max_in);
|
||||||
|
encrypt_block(tmp, out);
|
||||||
|
done_in += max_in;
|
||||||
|
offset += max_in;
|
||||||
|
done_out += block_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_aes_xts_encrypt(op_aes_xts_encrypt_t *encrypt_ctx)
|
||||||
|
{
|
||||||
|
delete encrypt_ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
op_aes_xts_decrypt_t::op_aes_xts_decrypt_t()
|
||||||
|
{
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
if (!(ctx = EVP_CIPHER_CTX_new()))
|
||||||
|
{
|
||||||
|
ERR_print_errors_fp(stderr);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
EVP_CIPHER_CTX_set_padding(ctx, 0);
|
||||||
|
if (EVP_DecryptInit_ex(ctx, EVP_aes_256_xts(), NULL, NULL, NULL) != 1)
|
||||||
|
{
|
||||||
|
ERR_print_errors_fp(stderr);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
fprintf(stderr, "Error: Vitastor is built without encryption support\n");
|
||||||
|
abort();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
op_aes_xts_decrypt_t::~op_aes_xts_decrypt_t()
|
||||||
|
{
|
||||||
|
assert(!decrypted);
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
#endif
|
||||||
|
if (tmp)
|
||||||
|
free(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_aes_xts_decrypt_t::start(uint8_t **key_chain, size_t chain_size, uint8_t *key_indexes, uint64_t start_offset, size_t block_size)
|
||||||
|
{
|
||||||
|
assert(!decrypted);
|
||||||
|
this->start_offset = start_offset;
|
||||||
|
this->key_chain = chain_size > 1 ? key_chain : 0;
|
||||||
|
this->chain_size = chain_size > 1 ? chain_size : 0;
|
||||||
|
this->key_indexes = chain_size > 1 ? key_indexes : NULL;
|
||||||
|
assert(chain_size <= 1 || key_indexes != NULL);
|
||||||
|
this->block_size = block_size;
|
||||||
|
this->offset = 0;
|
||||||
|
this->tmp_pos = 0;
|
||||||
|
if (tmp && tmp_size != block_size)
|
||||||
|
{
|
||||||
|
free(tmp);
|
||||||
|
tmp = NULL;
|
||||||
|
tmp_size = 0;
|
||||||
|
}
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
if (chain_size == 1 && key_chain[0] && EVP_DecryptInit_ex(ctx, NULL, NULL, key_chain[0], NULL) != 1)
|
||||||
|
{
|
||||||
|
ERR_print_errors_fp(stderr);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_aes_xts_decrypt_t::decrypt_block(uint8_t *in, uint8_t *out)
|
||||||
|
{
|
||||||
|
uint8_t *key = NULL;
|
||||||
|
if (chain_size)
|
||||||
|
{
|
||||||
|
assert(key_indexes[offset/block_size] < chain_size);
|
||||||
|
key = key_chain[key_indexes[offset/block_size]];
|
||||||
|
if (!key)
|
||||||
|
{
|
||||||
|
if (in != out)
|
||||||
|
memcpy(out, in, block_size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
uint8_t iv[16] = { 0 };
|
||||||
|
*((uint64_t*)iv) = start_offset + offset - offset%block_size;
|
||||||
|
if (EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv) != 1)
|
||||||
|
{
|
||||||
|
ERR_print_errors_fp(stderr);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
int actual_out = 0;
|
||||||
|
if (EVP_DecryptUpdate(ctx, out, &actual_out, in, block_size) != 1)
|
||||||
|
{
|
||||||
|
ERR_print_errors_fp(stderr);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
assert(actual_out == block_size);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// out may be NULL, in this case all input is still decrypted to calculate checksums,
|
||||||
|
// but part of it is skipped and not copied to out
|
||||||
|
void op_aes_xts_decrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size_t max_out, size_t & done_in, size_t & done_out)
|
||||||
|
{
|
||||||
|
// Fucking AES-XTS implementations (all of them) don't have streaming support,
|
||||||
|
// crafting IV to resume decryption is slow, so we have to accumulate a full block
|
||||||
|
// and decrypt it at once :-(
|
||||||
|
// And then we have to support consuming it in parts because clients sometimes need
|
||||||
|
// fragmented output.
|
||||||
|
if (decrypted)
|
||||||
|
{
|
||||||
|
// Copy accumulated and decrypted output
|
||||||
|
assert(tmp);
|
||||||
|
if (max_out > block_size - tmp_pos)
|
||||||
|
max_out = block_size - tmp_pos;
|
||||||
|
if (out)
|
||||||
|
memcpy(out, tmp + tmp_pos, max_out);
|
||||||
|
done_out += max_out;
|
||||||
|
tmp_pos += max_out;
|
||||||
|
if (tmp_pos >= block_size)
|
||||||
|
decrypted = false;
|
||||||
|
}
|
||||||
|
else if (max_in < block_size - offset%block_size)
|
||||||
|
{
|
||||||
|
// Just accumulate input
|
||||||
|
if (!tmp)
|
||||||
|
{
|
||||||
|
tmp = (uint8_t*)malloc_or_die(block_size);
|
||||||
|
tmp_size = block_size;
|
||||||
|
}
|
||||||
|
memcpy(tmp + offset%block_size, in, max_in);
|
||||||
|
done_in += max_in;
|
||||||
|
offset += max_in;
|
||||||
|
}
|
||||||
|
else if (max_out < block_size || !out)
|
||||||
|
{
|
||||||
|
// Accumulate and decrypt input in <tmp>, then copy part of it to <out>
|
||||||
|
if (!tmp)
|
||||||
|
{
|
||||||
|
tmp = (uint8_t*)malloc_or_die(block_size);
|
||||||
|
tmp_size = block_size;
|
||||||
|
}
|
||||||
|
max_in = block_size - offset%block_size;
|
||||||
|
memcpy(tmp + offset%block_size, in, max_in);
|
||||||
|
decrypt_block(tmp, tmp);
|
||||||
|
decrypted = true;
|
||||||
|
if (out)
|
||||||
|
memcpy(out, tmp, max_out);
|
||||||
|
tmp_pos = max_out;
|
||||||
|
done_in += max_in;
|
||||||
|
offset += max_in;
|
||||||
|
done_out += max_out;
|
||||||
|
}
|
||||||
|
else if (!(offset%block_size))
|
||||||
|
{
|
||||||
|
// Full block - simplest case
|
||||||
|
if (out)
|
||||||
|
decrypt_block(in, out);
|
||||||
|
done_in += block_size;
|
||||||
|
offset += block_size;
|
||||||
|
done_out += block_size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Accumulate input and decrypt directly to <output>
|
||||||
|
assert(tmp);
|
||||||
|
max_in = block_size - offset%block_size;
|
||||||
|
memcpy(tmp + offset%block_size, in, max_in);
|
||||||
|
assert(out);
|
||||||
|
decrypt_block(tmp, out);
|
||||||
|
done_in += max_in;
|
||||||
|
offset += max_in;
|
||||||
|
done_out += block_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy_aes_xts_decrypt(op_aes_xts_decrypt_t *decrypt_ctx)
|
||||||
|
{
|
||||||
|
delete decrypt_ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool osd_messenger_t::op_encrypted_copy_data_to(osd_client_t* cl, uint8_t *enc_buf, size_t enc_len, size_t from, size_t & done)
|
||||||
|
{
|
||||||
|
auto op = cl->write_op;
|
||||||
|
auto & op_pos = cl->write_op_pos;
|
||||||
|
assert(op->req.hdr.opcode == OSD_OP_WRITE);
|
||||||
|
if (!from)
|
||||||
|
{
|
||||||
|
if (!cl->encrypt_ctx)
|
||||||
|
{
|
||||||
|
if (encrypt_ctx_pool.size())
|
||||||
|
{
|
||||||
|
cl->encrypt_ctx = encrypt_ctx_pool.back();
|
||||||
|
encrypt_ctx_pool.pop_back();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cl->encrypt_ctx = new op_aes_xts_encrypt_t();
|
||||||
|
}
|
||||||
|
assert(op->enc->key_chain[0]);
|
||||||
|
cl->encrypt_ctx->start(op->enc->key_chain[0], op->req.rw.offset, op->enc->bitmap_granularity);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < op->iov.count; i++)
|
||||||
|
{
|
||||||
|
uint8_t *plain = (uint8_t*)op->iov.buf[i].iov_base;
|
||||||
|
size_t plain_len = op->iov.buf[i].iov_len;
|
||||||
|
while (from < plain_len || cl->encrypt_ctx->has_buffered())
|
||||||
|
{
|
||||||
|
if (done >= enc_len)
|
||||||
|
return false;
|
||||||
|
size_t done_in = 0;
|
||||||
|
size_t done_out = 0;
|
||||||
|
cl->encrypt_ctx->update(plain+from, plain_len-from, enc_buf+done, enc_len-done, done_in, done_out);
|
||||||
|
if (cl->write_csum_state && done_out > 0)
|
||||||
|
XXH3_64bits_update(cl->write_csum_state, enc_buf+done, done_out);
|
||||||
|
done += done_out;
|
||||||
|
op_pos += done_in;
|
||||||
|
from += done_in;
|
||||||
|
}
|
||||||
|
from -= plain_len;
|
||||||
|
}
|
||||||
|
if (cl->encrypt_ctx)
|
||||||
|
{
|
||||||
|
if (encrypt_ctx_pool.size() > max_aes_xts_pool_size)
|
||||||
|
delete cl->encrypt_ctx;
|
||||||
|
else
|
||||||
|
encrypt_ctx_pool.push_back(cl->encrypt_ctx);
|
||||||
|
cl->encrypt_ctx = NULL;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool osd_messenger_t::op_decrypted_copy_data_from(osd_client_t* cl, uint8_t *enc_buf, size_t enc_len, size_t from, size_t & done)
|
||||||
|
{
|
||||||
|
op_decrypt_start(cl);
|
||||||
|
auto op = cl->read_op;
|
||||||
|
assert(op->req.hdr.opcode == OSD_OP_READ);
|
||||||
|
for (int i = 0; i < op->iov.count; i++)
|
||||||
|
{
|
||||||
|
uint8_t *plain = (uint8_t*)op->iov.buf[i].iov_base;
|
||||||
|
size_t plain_len = op->iov.buf[i].iov_len;
|
||||||
|
while (from < plain_len)
|
||||||
|
{
|
||||||
|
if (done >= enc_len)
|
||||||
|
return false;
|
||||||
|
size_t done_in = 0;
|
||||||
|
size_t done_out = 0;
|
||||||
|
// plain == NULL means skip output
|
||||||
|
cl->decrypt_ctx->update(enc_buf+done, enc_len-done, plain ? plain+from : NULL, plain_len-from, done_in, done_out);
|
||||||
|
if (cl->read_csum_state && done_in > 0)
|
||||||
|
XXH3_64bits_update(cl->read_csum_state, enc_buf+done, done_in);
|
||||||
|
done += done_in;
|
||||||
|
cl->read_op_pos += done_out;
|
||||||
|
cl->read_op_inline_decrypt_in += done_in;
|
||||||
|
from += done_out;
|
||||||
|
if (!done_out)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
from -= plain_len;
|
||||||
|
}
|
||||||
|
op_decrypt_free(cl);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void osd_messenger_t::op_decrypt_start(osd_client_t* cl)
|
||||||
|
{
|
||||||
|
if (!cl->decrypt_ctx)
|
||||||
|
{
|
||||||
|
if (decrypt_ctx_pool.size())
|
||||||
|
{
|
||||||
|
cl->decrypt_ctx = decrypt_ctx_pool.back();
|
||||||
|
decrypt_ctx_pool.pop_back();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cl->decrypt_ctx = new op_aes_xts_decrypt_t();
|
||||||
|
auto & enc = cl->read_op->enc;
|
||||||
|
cl->decrypt_ctx->start(enc->key_chain, enc->chain_size,
|
||||||
|
(cl->read_op->req.rw.flags & OSD_OP_RETURN_CHAIN) ? (uint8_t*)cl->read_op->bitmap + enc->read_chain_bitmap_pos : 0,
|
||||||
|
cl->read_op->req.rw.offset, enc->bitmap_granularity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void osd_messenger_t::op_decrypt_inline(osd_client_t* cl)
|
||||||
|
{
|
||||||
|
op_decrypt_start(cl);
|
||||||
|
osd_op_t *op = cl->read_op;
|
||||||
|
size_t from_in = cl->read_op_inline_decrypt_in;
|
||||||
|
int i = 0;
|
||||||
|
while (i < op->iov.count && from_in >= op->iov.buf[i].iov_len)
|
||||||
|
{
|
||||||
|
from_in -= op->iov.buf[i].iov_len;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
size_t from_out = cl->read_op_inline_decrypt_pos - OSD_PACKET_SIZE - op->reply.rw.bitmap_len;
|
||||||
|
int j = 0;
|
||||||
|
while (j < op->iov.count && from_out >= op->iov.buf[j].iov_len)
|
||||||
|
{
|
||||||
|
from_out -= op->iov.buf[j].iov_len;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
while (i < op->iov.count && j < op->iov.count)
|
||||||
|
{
|
||||||
|
uint8_t *in = (uint8_t*)op->iov.buf[i].iov_base + from_in;
|
||||||
|
size_t in_len = op->iov.buf[i].iov_len - from_in;
|
||||||
|
uint8_t *out = (uint8_t*)op->iov.buf[j].iov_base + from_out;
|
||||||
|
size_t out_len = op->iov.buf[j].iov_len - from_out;
|
||||||
|
size_t done_in = 0;
|
||||||
|
size_t done_out = 0;
|
||||||
|
cl->decrypt_ctx->update(in, in_len, out, out_len, done_in, done_out);
|
||||||
|
if (done_in >= in_len)
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
from_in = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
from_in += done_in;
|
||||||
|
if (done_out >= out_len)
|
||||||
|
{
|
||||||
|
j++;
|
||||||
|
from_out = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
from_out += done_out;
|
||||||
|
}
|
||||||
|
assert(j >= op->iov.count);
|
||||||
|
op_decrypt_free(cl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void osd_messenger_t::op_decrypt_free(osd_client_t* cl)
|
||||||
|
{
|
||||||
|
if (cl->decrypt_ctx)
|
||||||
|
{
|
||||||
|
if (decrypt_ctx_pool.size() > max_aes_xts_pool_size)
|
||||||
|
delete cl->decrypt_ctx;
|
||||||
|
else
|
||||||
|
decrypt_ctx_pool.push_back(cl->decrypt_ctx);
|
||||||
|
cl->decrypt_ctx = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
// Copyright (c) Vitaliy Filippov, 2026+
|
||||||
|
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "../util/xxh_x86dispatch.h"
|
||||||
|
// WITH_OPENSSL is left to possibly support other crypto libraries
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
#include <openssl/conf.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class op_aes_xts_encrypt_t
|
||||||
|
{
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
EVP_CIPHER_CTX *ctx = NULL;
|
||||||
|
#endif
|
||||||
|
uint64_t start_offset = 0;
|
||||||
|
uint8_t *key = NULL;
|
||||||
|
size_t offset = 0;
|
||||||
|
size_t block_size = 0;
|
||||||
|
uint8_t *tmp = NULL;
|
||||||
|
size_t tmp_size = 0;
|
||||||
|
size_t tmp_pos = 0;
|
||||||
|
bool encrypted = false;
|
||||||
|
|
||||||
|
void encrypt_block(uint8_t *in, uint8_t *out);
|
||||||
|
|
||||||
|
public:
|
||||||
|
op_aes_xts_encrypt_t();
|
||||||
|
~op_aes_xts_encrypt_t();
|
||||||
|
|
||||||
|
inline bool has_buffered() { return encrypted; };
|
||||||
|
void start(uint8_t *key, uint64_t start_offset, size_t block_size);
|
||||||
|
void update(uint8_t *in, size_t max_in, uint8_t *out, size_t max_out, size_t & done_in, size_t & done_out);
|
||||||
|
};
|
||||||
|
|
||||||
|
void destroy_aes_xts_encrypt(op_aes_xts_encrypt_t *encrypt_ctx);
|
||||||
|
|
||||||
|
class op_aes_xts_decrypt_t
|
||||||
|
{
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
EVP_CIPHER_CTX *ctx = NULL;
|
||||||
|
#endif
|
||||||
|
uint64_t start_offset = 0;
|
||||||
|
uint8_t **key_chain = NULL;
|
||||||
|
size_t chain_size = 0;
|
||||||
|
uint8_t *key_indexes = NULL;
|
||||||
|
size_t offset = 0;
|
||||||
|
size_t block_size = 0;
|
||||||
|
uint8_t *tmp = NULL;
|
||||||
|
size_t tmp_size = 0;
|
||||||
|
size_t tmp_pos = 0;
|
||||||
|
bool decrypted = false;
|
||||||
|
|
||||||
|
void decrypt_block(uint8_t *in, uint8_t *out);
|
||||||
|
|
||||||
|
public:
|
||||||
|
op_aes_xts_decrypt_t();
|
||||||
|
~op_aes_xts_decrypt_t();
|
||||||
|
|
||||||
|
inline bool has_buffered() { return decrypted; };
|
||||||
|
void start(uint8_t **key_chain, size_t chain_size, uint8_t *key_indexes, uint64_t start_offset, size_t block_size);
|
||||||
|
void update(uint8_t *in, size_t max_in, uint8_t *out, size_t max_out, size_t & done_in, size_t & done_out);
|
||||||
|
};
|
||||||
|
|
||||||
|
void destroy_aes_xts_decrypt(op_aes_xts_decrypt_t *decrypt_ctx);
|
||||||
@@ -22,6 +22,10 @@ osd_op_t::~osd_op_t()
|
|||||||
// So we don't reuse it, but free it every time
|
// So we don't reuse it, but free it every time
|
||||||
free(buf);
|
free(buf);
|
||||||
}
|
}
|
||||||
|
if (enc_buf)
|
||||||
|
{
|
||||||
|
free(enc_buf);
|
||||||
|
}
|
||||||
if (op_data)
|
if (op_data)
|
||||||
{
|
{
|
||||||
free(op_data);
|
free(op_data);
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <sys/uio.h>
|
#include <sys/uio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -16,6 +18,8 @@
|
|||||||
|
|
||||||
#define OSD_OP_INLINE_BUF_COUNT 16
|
#define OSD_OP_INLINE_BUF_COUNT 16
|
||||||
|
|
||||||
|
#define AES_256_XTS_KEY_SIZE 64
|
||||||
|
|
||||||
// Kind of a vector with small-list-optimisation
|
// Kind of a vector with small-list-optimisation
|
||||||
struct osd_op_buf_list_t
|
struct osd_op_buf_list_t
|
||||||
{
|
{
|
||||||
@@ -152,6 +156,19 @@ struct blockstore_op_t;
|
|||||||
|
|
||||||
struct osd_primary_op_data_t;
|
struct osd_primary_op_data_t;
|
||||||
|
|
||||||
|
struct osd_op_enc_t
|
||||||
|
{
|
||||||
|
// Keys may contain more information in the future, like encryption algorithm and key ID
|
||||||
|
// In this case, key_chain will become inode_key_t* with inode_key_t also being a structure
|
||||||
|
// Currently all keys are required to be 512 bit (64 byte) long, for AES-256-XTS
|
||||||
|
// Raw pointers are convenient for messenger code; external users may use shared_ptr aliasing
|
||||||
|
// to implement complex freeing of osd_op_enc_t along with their external inode cache info
|
||||||
|
uint8_t** key_chain = NULL;
|
||||||
|
size_t chain_size = 0;
|
||||||
|
uint32_t read_chain_bitmap_pos = 0;
|
||||||
|
uint32_t bitmap_granularity = 0;
|
||||||
|
};
|
||||||
|
|
||||||
struct __attribute__((visibility("default"))) osd_op_t
|
struct __attribute__((visibility("default"))) osd_op_t
|
||||||
{
|
{
|
||||||
timespec tv_begin = { 0 }, tv_end = { 0 };
|
timespec tv_begin = { 0 }, tv_end = { 0 };
|
||||||
@@ -167,6 +184,9 @@ struct __attribute__((visibility("default"))) osd_op_t
|
|||||||
unsigned bmp_data = 0;
|
unsigned bmp_data = 0;
|
||||||
void *bitmap_buf = NULL;
|
void *bitmap_buf = NULL;
|
||||||
void *rmw_buf = NULL;
|
void *rmw_buf = NULL;
|
||||||
|
std::shared_ptr<osd_op_enc_t> enc;
|
||||||
|
uint8_t *enc_buf = NULL;
|
||||||
|
uint64_t csum = 0; // network layer checksum
|
||||||
osd_primary_op_data_t* op_data = NULL;
|
osd_primary_op_data_t* op_data = NULL;
|
||||||
std::function<void(osd_op_t*)> callback;
|
std::function<void(osd_op_t*)> callback;
|
||||||
|
|
||||||
@@ -176,4 +196,5 @@ struct __attribute__((visibility("default"))) osd_op_t
|
|||||||
void cancel();
|
void cancel();
|
||||||
|
|
||||||
bool is_recovery_related();
|
bool is_recovery_related();
|
||||||
|
uint64_t calc_data_checksum();
|
||||||
};
|
};
|
||||||
|
|||||||
+39
-53
@@ -187,6 +187,8 @@ std::vector<msgr_rdma_context_t*> msgr_rdma_context_t::create_all(const std::vec
|
|||||||
ibv_device **raw_dev_list = NULL;
|
ibv_device **raw_dev_list = NULL;
|
||||||
ibv_device **dev_list = NULL;
|
ibv_device **dev_list = NULL;
|
||||||
ibv_device *single_list[2] = {};
|
ibv_device *single_list[2] = {};
|
||||||
|
int up_ports = 0;
|
||||||
|
int single_port_num = 0;
|
||||||
|
|
||||||
raw_dev_list = dev_list = ibv_get_device_list(NULL);
|
raw_dev_list = dev_list = ibv_get_device_list(NULL);
|
||||||
if (!dev_list || !*dev_list)
|
if (!dev_list || !*dev_list)
|
||||||
@@ -221,6 +223,7 @@ std::vector<msgr_rdma_context_t*> msgr_rdma_context_t::create_all(const std::vec
|
|||||||
dev_list = single_list;
|
dev_list = single_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
retry:
|
||||||
for (int i = 0; dev_list[i]; ++i)
|
for (int i = 0; dev_list[i]; ++i)
|
||||||
{
|
{
|
||||||
auto dev = dev_list[i];
|
auto dev = dev_list[i];
|
||||||
@@ -258,6 +261,9 @@ std::vector<msgr_rdma_context_t*> msgr_rdma_context_t::create_all(const std::vec
|
|||||||
fprintf(stderr, "RDMA device %s port %d GID %d does not exist\n", ibv_get_device_name(dev), port_num, sel_gid_index);
|
fprintf(stderr, "RDMA device %s port %d GID %d does not exist\n", ibv_get_device_name(dev), port_num, sel_gid_index);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
up_ports++;
|
||||||
|
single_port_num = port_num;
|
||||||
|
single_list[0] = dev;
|
||||||
uint32_t port_mtu = sel_mtu ? sel_mtu : ibv_mtu_to_bytes(portinfo.active_mtu);
|
uint32_t port_mtu = sel_mtu ? sel_mtu : ibv_mtu_to_bytes(portinfo.active_mtu);
|
||||||
#ifdef IBV_ADVISE_MR_ADVICE_PREFETCH_NO_FAULT
|
#ifdef IBV_ADVISE_MR_ADVICE_PREFETCH_NO_FAULT
|
||||||
if (sel_gid_index < 0)
|
if (sel_gid_index < 0)
|
||||||
@@ -298,6 +304,14 @@ cleanup_dev:
|
|||||||
ibv_close_device(context);
|
ibv_close_device(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ret.size() && up_ports == 1 && dev_list != single_list)
|
||||||
|
{
|
||||||
|
// Auto-select the only available device/port if there is only one
|
||||||
|
dev_list = single_list;
|
||||||
|
sel_port_num = single_port_num;
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (raw_dev_list)
|
if (raw_dev_list)
|
||||||
ibv_free_device_list(raw_dev_list);
|
ibv_free_device_list(raw_dev_list);
|
||||||
@@ -554,23 +568,24 @@ static void try_send_rdma_wr(osd_client_t *cl, ibv_sge *sge, int op_sge)
|
|||||||
cl->rdma_conn->cur_send++;
|
cl->rdma_conn->cur_send++;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int try_send_rdma_copy(osd_client_t *cl, uint8_t *dst, int dst_len)
|
int osd_messenger_t::try_send_rdma_copy(osd_client_t *cl, uint8_t *dst, int dst_len)
|
||||||
{
|
{
|
||||||
auto rc = cl->rdma_conn;
|
|
||||||
int total_dst_len = dst_len;
|
int total_dst_len = dst_len;
|
||||||
while (dst_len > 0 && rc->send_pos < cl->send_list.size())
|
while (dst_len > 0 && (cl->write_op || cl->write_ops.size()))
|
||||||
{
|
{
|
||||||
iovec & iov = cl->send_list[rc->send_pos];
|
next_write_op(cl);
|
||||||
uint32_t len = (uint32_t)(iov.iov_len-rc->send_buf_pos < dst_len
|
osd_op_t *op = cl->write_op;
|
||||||
? iov.iov_len-rc->send_buf_pos : dst_len);
|
size_t copied = op_copy_to(cl, dst, dst_len);
|
||||||
memcpy(dst, (uint8_t*)iov.iov_base+rc->send_buf_pos, len);
|
if (!copied)
|
||||||
dst += len;
|
|
||||||
dst_len -= len;
|
|
||||||
rc->send_buf_pos += len;
|
|
||||||
if (rc->send_buf_pos >= iov.iov_len)
|
|
||||||
{
|
{
|
||||||
rc->send_pos++;
|
break;
|
||||||
rc->send_buf_pos = 0;
|
}
|
||||||
|
dst += copied;
|
||||||
|
dst_len -= copied;
|
||||||
|
if (!cl->write_op && op->op_type == OSD_OP_IN)
|
||||||
|
{
|
||||||
|
// this is a reply, free the op after sending it
|
||||||
|
cl->send_free_ops.push_back(op);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return total_dst_len-dst_len;
|
return total_dst_len-dst_len;
|
||||||
@@ -617,6 +632,7 @@ void osd_messenger_t::try_send_rdma(osd_client_t *cl)
|
|||||||
};
|
};
|
||||||
try_send_rdma_wr(cl, &sge, 1);
|
try_send_rdma_wr(cl, &sge, 1);
|
||||||
rc->send_sizes.push_back(copied);
|
rc->send_sizes.push_back(copied);
|
||||||
|
cl->send_free_ops.push_back(NULL); // end marker
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -713,9 +729,6 @@ void osd_messenger_t::handle_rdma_events(msgr_rdma_context_t *rdma_context)
|
|||||||
}
|
}
|
||||||
if (!is_send)
|
if (!is_send)
|
||||||
{
|
{
|
||||||
// Reset OSD ping state - client is obviously alive
|
|
||||||
cl->ping_time_remaining = 0;
|
|
||||||
cl->idle_time_remaining = osd_idle_timeout;
|
|
||||||
rc->cur_recv--;
|
rc->cur_recv--;
|
||||||
if (!handle_read_buffer(cl, rc->recv_buffers[rc->next_recv_buf], wc[i].byte_len))
|
if (!handle_read_buffer(cl, rc->recv_buffers[rc->next_recv_buf], wc[i].byte_len))
|
||||||
{
|
{
|
||||||
@@ -727,54 +740,27 @@ void osd_messenger_t::handle_rdma_events(msgr_rdma_context_t *rdma_context)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
rc->cur_send--;
|
rc->cur_send--;
|
||||||
uint64_t sent_size = rc->send_sizes.at(0);
|
// byte_len is not filled for send operations
|
||||||
rc->send_sizes.erase(rc->send_sizes.begin(), rc->send_sizes.begin()+1);
|
uint64_t sent_size = rc->send_sizes.front();
|
||||||
|
rc->send_sizes.pop_front();
|
||||||
rc->send_done_pos += sent_size;
|
rc->send_done_pos += sent_size;
|
||||||
rc->send_out_full = false;
|
rc->send_out_full = false;
|
||||||
if (rc->send_done_pos == rc->send_out_size)
|
if (rc->send_done_pos == rc->send_out_size)
|
||||||
rc->send_done_pos = 0;
|
rc->send_done_pos = 0;
|
||||||
assert(rc->send_done_pos < rc->send_out_size);
|
assert(rc->send_done_pos < rc->send_out_size);
|
||||||
int send_pos = 0, send_buf_pos = 0;
|
while (cl->send_free_ops.front())
|
||||||
while (sent_size > 0)
|
|
||||||
{
|
{
|
||||||
if (sent_size >= cl->send_list.at(send_pos).iov_len)
|
delete cl->send_free_ops.front();
|
||||||
{
|
cl->send_free_ops.pop_front();
|
||||||
sent_size -= cl->send_list[send_pos].iov_len;
|
|
||||||
send_pos++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
send_buf_pos = sent_size;
|
|
||||||
sent_size = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
assert(rc->send_pos >= send_pos);
|
cl->send_free_ops.pop_front();
|
||||||
if (rc->send_pos == send_pos)
|
if ((cl->proto_csum_status & MSGR_CSUM_NEG) && !cl->write_op && !cl->write_ops.size())
|
||||||
{
|
{
|
||||||
rc->send_buf_pos -= send_buf_pos;
|
// Checksums negotiated, enable
|
||||||
}
|
cl->proto_csum_status = cl->proto_csum_status & (~MSGR_CSUM_NEG);
|
||||||
rc->send_pos -= send_pos;
|
|
||||||
for (int i = 0; i < send_pos; i++)
|
|
||||||
{
|
|
||||||
if (cl->outbox[i].flags & MSGR_SENDP_FREE)
|
|
||||||
{
|
|
||||||
// Reply fully sent
|
|
||||||
delete cl->outbox[i].op;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (send_pos > 0)
|
|
||||||
{
|
|
||||||
cl->send_list.erase(cl->send_list.begin(), cl->send_list.begin()+send_pos);
|
|
||||||
cl->outbox.erase(cl->outbox.begin(), cl->outbox.begin()+send_pos);
|
|
||||||
}
|
|
||||||
if (send_buf_pos > 0)
|
|
||||||
{
|
|
||||||
cl->send_list[0].iov_base = (uint8_t*)cl->send_list[0].iov_base + send_buf_pos;
|
|
||||||
cl->send_list[0].iov_len -= send_buf_pos;
|
|
||||||
}
|
}
|
||||||
try_send_rdma(cl);
|
try_send_rdma(cl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (event_count > 0);
|
} while (event_count > 0);
|
||||||
handle_immediate_ops();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,11 @@
|
|||||||
#include <infiniband/verbs.h>
|
#include <infiniband/verbs.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <deque>
|
||||||
#include "addr_util.h"
|
#include "addr_util.h"
|
||||||
|
|
||||||
|
struct osd_op_t;
|
||||||
|
|
||||||
struct msgr_rdma_address_t
|
struct msgr_rdma_address_t
|
||||||
{
|
{
|
||||||
ibv_gid gid;
|
ibv_gid gid;
|
||||||
@@ -72,9 +75,9 @@ struct msgr_rdma_connection_t
|
|||||||
int cur_send = 0, cur_recv = 0;
|
int cur_send = 0, cur_recv = 0;
|
||||||
int send_pos = 0, send_buf_pos = 0;
|
int send_pos = 0, send_buf_pos = 0;
|
||||||
int next_recv_buf = 0;
|
int next_recv_buf = 0;
|
||||||
std::vector<void*> recv_buffers;
|
std::vector<uint8_t*> recv_buffers;
|
||||||
msgr_rdma_buf_t recv_buf;
|
msgr_rdma_buf_t recv_buf;
|
||||||
std::vector<uint64_t> send_sizes;
|
std::deque<uint64_t> send_sizes;
|
||||||
msgr_rdma_buf_t send_out;
|
msgr_rdma_buf_t send_out;
|
||||||
int send_out_pos = 0, send_done_pos = 0, send_out_size = 0;
|
int send_out_pos = 0, send_done_pos = 0, send_out_size = 0;
|
||||||
bool send_out_full = false;
|
bool send_out_full = false;
|
||||||
|
|||||||
@@ -495,7 +495,7 @@ void osd_messenger_t::rdmacm_established(rdma_cm_event *ev)
|
|||||||
cl->peer_state = PEER_RDMA;
|
cl->peer_state = PEER_RDMA;
|
||||||
cl->connect_timeout_id = -1;
|
cl->connect_timeout_id = -1;
|
||||||
cl->osd_num = peer_osd;
|
cl->osd_num = peer_osd;
|
||||||
cl->in_buf = malloc_or_die(receive_buffer_size);
|
cl->in_buf = (uint8_t*)malloc_or_die(receive_buffer_size);
|
||||||
cl->rdma_conn = rc;
|
cl->rdma_conn = rc;
|
||||||
clients[conn->client_id] = cl;
|
clients[conn->client_id] = cl;
|
||||||
if (conn->timeout_id >= 0)
|
if (conn->timeout_id >= 0)
|
||||||
|
|||||||
+474
-213
@@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) Vitaliy Filippov, 2019+
|
// Copyright (c) Vitaliy Filippov, 2019+
|
||||||
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
|
||||||
|
|
||||||
|
#define _XOPEN_SOURCE
|
||||||
|
#include <limits.h>
|
||||||
#include "messenger.h"
|
#include "messenger.h"
|
||||||
|
|
||||||
void osd_messenger_t::read_requests()
|
void osd_messenger_t::read_requests()
|
||||||
@@ -15,7 +17,11 @@ void osd_messenger_t::read_requests()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto cl = cl_it->second;
|
auto cl = cl_it->second;
|
||||||
if (cl->read_remaining < receive_buffer_size)
|
if (cl->read_op && cl->read_op_size-(cl->read_op_pos-OSD_PACKET_SIZE) >= receive_buffer_size)
|
||||||
|
{
|
||||||
|
op_get_read_buffers(cl, cl->recv_list);
|
||||||
|
}
|
||||||
|
if (!cl->recv_list.size())
|
||||||
{
|
{
|
||||||
cl->read_iov.iov_base = cl->in_buf;
|
cl->read_iov.iov_base = cl->in_buf;
|
||||||
cl->read_iov.iov_len = receive_buffer_size;
|
cl->read_iov.iov_len = receive_buffer_size;
|
||||||
@@ -25,10 +31,11 @@ void osd_messenger_t::read_requests()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
cl->read_iov.iov_base = 0;
|
cl->read_iov.iov_base = 0;
|
||||||
cl->read_iov.iov_len = cl->read_remaining;
|
cl->read_iov.iov_len = 0;
|
||||||
cl->read_msg.msg_iov = cl->recv_list.get_iovec();
|
cl->read_msg.msg_iov = cl->recv_list.data();
|
||||||
cl->read_msg.msg_iovlen = cl->recv_list.get_size();
|
cl->read_msg.msg_iovlen = cl->recv_list.size();
|
||||||
}
|
}
|
||||||
|
assert(!cl->read_op || cl->read_op_pos < OSD_PACKET_SIZE || cl->read_op_size >= (cl->read_op_pos-OSD_PACKET_SIZE));
|
||||||
cl->refs++;
|
cl->refs++;
|
||||||
if (ringloop && !use_sync_send_recv)
|
if (ringloop && !use_sync_send_recv)
|
||||||
{
|
{
|
||||||
@@ -50,7 +57,7 @@ void osd_messenger_t::read_requests()
|
|||||||
}
|
}
|
||||||
ring_data_t* data = ((ring_data_t*)sqe->user_data);
|
ring_data_t* data = ((ring_data_t*)sqe->user_data);
|
||||||
data->callback = [this, cl](ring_data_t *data) { handle_read(data->res, cl); };
|
data->callback = [this, cl](ring_data_t *data) { handle_read(data->res, cl); };
|
||||||
io_uring_prep_recvmsg(sqe, cl->peer_fd, &cl->read_msg, 0);
|
io_uring_prep_recvmsg(sqe, cl->peer_fd, &cl->read_msg, cl->recv_list.size() ? MSG_WAITALL : 0);
|
||||||
if (iothread)
|
if (iothread)
|
||||||
{
|
{
|
||||||
iothread->add_sqe(sqe_local);
|
iothread->add_sqe(sqe_local);
|
||||||
@@ -68,16 +75,15 @@ void osd_messenger_t::read_requests()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
read_ready_clients.clear();
|
read_ready_clients.clear();
|
||||||
|
handle_immediate_ops();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool osd_messenger_t::handle_read(int result, osd_client_t *cl)
|
void osd_messenger_t::handle_read(int result, osd_client_t *cl)
|
||||||
{
|
{
|
||||||
bool ret = false;
|
|
||||||
cl->read_msg.msg_iovlen = 0;
|
|
||||||
cl->refs--;
|
cl->refs--;
|
||||||
if (cl->peer_state == PEER_RDMA)
|
if (cl->peer_state == PEER_RDMA)
|
||||||
{
|
{
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
if (cl->peer_state == PEER_STOPPED)
|
if (cl->peer_state == PEER_STOPPED)
|
||||||
{
|
{
|
||||||
@@ -85,7 +91,7 @@ bool osd_messenger_t::handle_read(int result, osd_client_t *cl)
|
|||||||
{
|
{
|
||||||
destroy_client(cl);
|
destroy_client(cl);
|
||||||
}
|
}
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
if (result <= 0 && result != -EAGAIN && result != -EINTR)
|
if (result <= 0 && result != -EAGAIN && result != -EINTR)
|
||||||
{
|
{
|
||||||
@@ -95,9 +101,56 @@ bool osd_messenger_t::handle_read(int result, osd_client_t *cl)
|
|||||||
fprintf(stderr, "Client %ju socket read error: %d (%s). Disconnecting client\n", cl->client_id, -result, strerror(-result));
|
fprintf(stderr, "Client %ju socket read error: %d (%s). Disconnecting client\n", cl->client_id, -result, strerror(-result));
|
||||||
}
|
}
|
||||||
stop_client(cl->client_id);
|
stop_client(cl->client_id);
|
||||||
return false;
|
out_wakeup:
|
||||||
|
if (set_immediate_ops.size())
|
||||||
|
ringloop->wakeup();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (result == -EAGAIN || result == -EINTR || result < cl->read_iov.iov_len)
|
bool full_read = false;
|
||||||
|
if (result > 0)
|
||||||
|
{
|
||||||
|
if (cl->read_iov.iov_base == cl->in_buf)
|
||||||
|
{
|
||||||
|
full_read = result >= cl->read_iov.iov_len;
|
||||||
|
if (!handle_read_buffer(cl, cl->in_buf, result))
|
||||||
|
goto out_wakeup;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Reset OSD ping state
|
||||||
|
cl->ping_time_remaining = 0;
|
||||||
|
cl->idle_time_remaining = osd_idle_timeout;
|
||||||
|
// Long data
|
||||||
|
size_t i = 0;
|
||||||
|
while (i < cl->recv_list.size() && result >= cl->recv_list[i].iov_len)
|
||||||
|
{
|
||||||
|
if (cl->read_csum_state && cl->recv_list[i].iov_len > 0 &&
|
||||||
|
i != cl->recv_list.size()-1) // skip the checksum itself
|
||||||
|
{
|
||||||
|
XXH3_64bits_update(cl->read_csum_state, cl->recv_list[i].iov_base, cl->recv_list[i].iov_len);
|
||||||
|
}
|
||||||
|
result -= cl->recv_list[i].iov_len;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i < cl->recv_list.size())
|
||||||
|
{
|
||||||
|
cl->recv_list[i].iov_base += result;
|
||||||
|
cl->recv_list[i].iov_len -= result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
full_read = true;
|
||||||
|
}
|
||||||
|
cl->recv_list.erase(cl->recv_list.begin(), cl->recv_list.begin()+i);
|
||||||
|
if (!cl->recv_list.size())
|
||||||
|
{
|
||||||
|
if (!handle_finished_op(cl))
|
||||||
|
goto out_wakeup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cl->read_msg.msg_iovlen = 0;
|
||||||
|
if (result == -EAGAIN || result == -EINTR || !full_read)
|
||||||
{
|
{
|
||||||
cl->read_ready--;
|
cl->read_ready--;
|
||||||
if (cl->read_ready > 0)
|
if (cl->read_ready > 0)
|
||||||
@@ -107,37 +160,7 @@ bool osd_messenger_t::handle_read(int result, osd_client_t *cl)
|
|||||||
{
|
{
|
||||||
read_ready_clients.push_back(cl->client_id);
|
read_ready_clients.push_back(cl->client_id);
|
||||||
}
|
}
|
||||||
if (result > 0)
|
goto out_wakeup;
|
||||||
{
|
|
||||||
if (cl->read_iov.iov_base == cl->in_buf)
|
|
||||||
{
|
|
||||||
if (!handle_read_buffer(cl, cl->in_buf, result))
|
|
||||||
{
|
|
||||||
handle_immediate_ops();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Long data
|
|
||||||
cl->read_remaining -= result;
|
|
||||||
cl->recv_list.eat(result);
|
|
||||||
if (cl->recv_list.done >= cl->recv_list.count)
|
|
||||||
{
|
|
||||||
if (!handle_finished_read(cl))
|
|
||||||
{
|
|
||||||
handle_immediate_ops();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (result >= cl->read_iov.iov_len)
|
|
||||||
{
|
|
||||||
ret = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
handle_immediate_ops();
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void osd_messenger_t::handle_immediate_ops()
|
void osd_messenger_t::handle_immediate_ops()
|
||||||
@@ -162,113 +185,109 @@ void osd_messenger_t::handle_immediate_ops()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool osd_messenger_t::handle_read_buffer(osd_client_t *cl, void *curbuf, int remain)
|
bool osd_messenger_t::handle_read_buffer(osd_client_t *cl, uint8_t *curbuf, size_t bufsize)
|
||||||
{
|
{
|
||||||
|
// Reset OSD ping state
|
||||||
|
cl->ping_time_remaining = 0;
|
||||||
|
cl->idle_time_remaining = osd_idle_timeout;
|
||||||
// Compose operation(s) from the buffer
|
// Compose operation(s) from the buffer
|
||||||
while (remain > 0)
|
size_t done = 0;
|
||||||
|
while (done < bufsize)
|
||||||
{
|
{
|
||||||
if (!cl->read_op)
|
if (!cl->read_op)
|
||||||
{
|
{
|
||||||
cl->read_op = new osd_op_t;
|
cl->read_op = new osd_op_t;
|
||||||
cl->read_op->client_id = cl->client_id;
|
cl->read_op->client_id = cl->client_id;
|
||||||
cl->read_op->op_type = OSD_OP_IN;
|
cl->read_op->op_type = OSD_OP_IN;
|
||||||
cl->recv_list.push_back(cl->read_op->req.buf, OSD_PACKET_SIZE);
|
cl->read_op_pos = 0;
|
||||||
cl->read_remaining = OSD_PACKET_SIZE;
|
cl->read_op_size = 0;
|
||||||
cl->read_state = CL_READ_HDR;
|
cl->read_op_inline_decrypt_in = 0;
|
||||||
}
|
cl->read_op_inline_decrypt_pos = (size_t)-1;
|
||||||
while (cl->recv_list.done < cl->recv_list.count && remain > 0)
|
if (cl->proto_csum_status == MSGR_CSUM_FULL || cl->proto_csum_status == MSGR_CSUM_PAYLOAD)
|
||||||
{
|
|
||||||
iovec* cur = cl->recv_list.get_iovec();
|
|
||||||
if (cur->iov_len > remain)
|
|
||||||
{
|
{
|
||||||
memcpy(cur->iov_base, curbuf, remain);
|
if (!cl->read_csum_state)
|
||||||
cl->read_remaining -= remain;
|
cl->read_csum_state = XXH3_createState();
|
||||||
cur->iov_len -= remain;
|
XXH3_64bits_reset(cl->read_csum_state);
|
||||||
cur->iov_base = (uint8_t*)cur->iov_base + remain;
|
|
||||||
remain = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
memcpy(cur->iov_base, curbuf, cur->iov_len);
|
|
||||||
curbuf = (uint8_t*)curbuf + cur->iov_len;
|
|
||||||
cl->read_remaining -= cur->iov_len;
|
|
||||||
remain -= cur->iov_len;
|
|
||||||
cur->iov_len = 0;
|
|
||||||
cl->recv_list.done++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cl->recv_list.done >= cl->recv_list.count)
|
if (cl->read_op_pos < OSD_PACKET_SIZE)
|
||||||
{
|
{
|
||||||
if (!handle_finished_read(cl))
|
int len = OSD_PACKET_SIZE - cl->read_op_pos;
|
||||||
|
if (len > bufsize-done)
|
||||||
|
len = bufsize-done;
|
||||||
|
memcpy(cl->read_op->req.buf + cl->read_op_pos, curbuf+done, len);
|
||||||
|
done += len;
|
||||||
|
cl->read_op_pos += len;
|
||||||
|
if (cl->read_op_pos < OSD_PACKET_SIZE)
|
||||||
|
return true;
|
||||||
|
if (!handle_hdr(cl))
|
||||||
{
|
{
|
||||||
|
stop_client(cl->client_id);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (!op_copy_from(cl, curbuf, bufsize, done))
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool osd_messenger_t::handle_finished_read(osd_client_t *cl)
|
|
||||||
{
|
|
||||||
// Reset OSD ping state
|
|
||||||
cl->ping_time_remaining = 0;
|
|
||||||
cl->idle_time_remaining = osd_idle_timeout;
|
|
||||||
cl->recv_list.reset();
|
|
||||||
if (cl->read_state == CL_READ_HDR)
|
|
||||||
{
|
|
||||||
if (cl->read_op->req.hdr.magic == SECONDARY_OSD_REPLY_MAGIC)
|
|
||||||
return handle_reply_hdr(cl);
|
|
||||||
else if (cl->read_op->req.hdr.magic == SECONDARY_OSD_OP_MAGIC)
|
|
||||||
{
|
{
|
||||||
if (cl->check_sequencing)
|
|
||||||
{
|
|
||||||
if (cl->read_op->req.hdr.id != cl->read_op_id)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Warning: operation sequencing is broken on client %ju: expected num %ju, got %ju, stopping client\n", cl->client_id, cl->read_op_id, cl->read_op->req.hdr.id);
|
|
||||||
stop_client(cl->client_id);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
cl->read_op_id++;
|
|
||||||
}
|
|
||||||
handle_op_hdr(cl);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Received garbage: magic=%jx id=%ju opcode=%jx from client %ju\n", cl->read_op->req.hdr.magic, cl->read_op->req.hdr.id, cl->read_op->req.hdr.opcode, cl->client_id);
|
|
||||||
stop_client(cl->client_id);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (cl->read_state == CL_READ_DATA)
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool osd_messenger_t::handle_hdr(osd_client_t *cl)
|
||||||
|
{
|
||||||
|
if (cl->proto_csum_status == MSGR_CSUM_FULL)
|
||||||
{
|
{
|
||||||
// Operation is ready
|
XXH3_64bits_update(cl->read_csum_state, cl->read_op->req.buf, OSD_PACKET_SIZE);
|
||||||
cl->received_ops.push_back(cl->read_op);
|
|
||||||
set_immediate_ops.push_back(cl->read_op);
|
|
||||||
cl->read_op = NULL;
|
|
||||||
cl->read_state = 0;
|
|
||||||
}
|
}
|
||||||
else if (cl->read_state == CL_READ_REPLY_DATA)
|
if (cl->read_op->req.hdr.magic == SECONDARY_OSD_REPLY_MAGIC)
|
||||||
{
|
{
|
||||||
// Reply is ready
|
auto req_it = cl->sent_ops.find(cl->read_op->req.hdr.id);
|
||||||
handle_reply_ready(cl->read_op);
|
if (req_it == cl->sent_ops.end())
|
||||||
cl->read_op = NULL;
|
{
|
||||||
cl->read_state = 0;
|
// Command out of sync. Drop connection
|
||||||
|
fprintf(stderr, "Client %ju command out of sync: id %ju\n", cl->client_id, cl->read_op->req.hdr.id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
osd_op_t *op = req_it->second;
|
||||||
|
memcpy(op->reply.buf, cl->read_op->req.buf, OSD_PACKET_SIZE);
|
||||||
|
if (!allocate_reply_buffers(cl, op))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cl->sent_ops.erase(req_it);
|
||||||
|
delete cl->read_op;
|
||||||
|
cl->read_op = op;
|
||||||
|
}
|
||||||
|
else if (cl->read_op->req.hdr.magic == SECONDARY_OSD_OP_MAGIC)
|
||||||
|
{
|
||||||
|
if (cl->check_sequencing)
|
||||||
|
{
|
||||||
|
if (cl->read_op->req.hdr.id != cl->read_op_id)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Warning: operation sequencing is broken on client %d: expected num %ju, got %ju, stopping client\n", cl->peer_fd, cl->read_op_id, cl->read_op->req.hdr.id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cl->read_op_id++;
|
||||||
|
}
|
||||||
|
if (!allocate_op_buffers(cl))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
assert(0);
|
fprintf(stderr, "Received garbage: magic=%jx id=%ju opcode=%jx from client %ju\n", cl->read_op->req.hdr.magic, cl->read_op->req.hdr.id, cl->read_op->req.hdr.opcode, cl->client_id);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void osd_messenger_t::handle_op_hdr(osd_client_t *cl)
|
bool osd_messenger_t::allocate_op_buffers(osd_client_t *cl)
|
||||||
{
|
{
|
||||||
osd_op_t *cur_op = cl->read_op;
|
osd_op_t *cur_op = cl->read_op;
|
||||||
if (cur_op->req.hdr.opcode == OSD_OP_SEC_READ)
|
cl->read_op_size = 0;
|
||||||
{
|
if (cur_op->req.hdr.opcode == OSD_OP_SEC_WRITE ||
|
||||||
cl->read_remaining = 0;
|
|
||||||
}
|
|
||||||
else if (cur_op->req.hdr.opcode == OSD_OP_SEC_WRITE ||
|
|
||||||
cur_op->req.hdr.opcode == OSD_OP_SEC_WRITE_STABLE)
|
cur_op->req.hdr.opcode == OSD_OP_SEC_WRITE_STABLE)
|
||||||
{
|
{
|
||||||
if (cur_op->req.sec_rw.attr_len > 0)
|
if (cur_op->req.sec_rw.attr_len > 0)
|
||||||
@@ -277,14 +296,12 @@ void osd_messenger_t::handle_op_hdr(osd_client_t *cl)
|
|||||||
cur_op->bitmap = cur_op->rmw_buf = malloc_or_die(cur_op->req.sec_rw.attr_len);
|
cur_op->bitmap = cur_op->rmw_buf = malloc_or_die(cur_op->req.sec_rw.attr_len);
|
||||||
else
|
else
|
||||||
cur_op->bitmap = &cur_op->bmp_data;
|
cur_op->bitmap = &cur_op->bmp_data;
|
||||||
cl->recv_list.push_back(cur_op->bitmap, cur_op->req.sec_rw.attr_len);
|
|
||||||
}
|
}
|
||||||
if (cur_op->req.sec_rw.len > 0)
|
if (cur_op->req.sec_rw.len > 0)
|
||||||
{
|
{
|
||||||
cur_op->buf = memalign_or_die(MEM_ALIGNMENT, cur_op->req.sec_rw.len);
|
cur_op->buf = memalign_or_die(MEM_ALIGNMENT, cur_op->req.sec_rw.len);
|
||||||
cl->recv_list.push_back(cur_op->buf, cur_op->req.sec_rw.len);
|
|
||||||
}
|
}
|
||||||
cl->read_remaining = cur_op->req.sec_rw.len + cur_op->req.sec_rw.attr_len;
|
cl->read_op_size = cur_op->req.sec_rw.len + cur_op->req.sec_rw.attr_len;
|
||||||
}
|
}
|
||||||
else if (cur_op->req.hdr.opcode == OSD_OP_SEC_STABILIZE ||
|
else if (cur_op->req.hdr.opcode == OSD_OP_SEC_STABILIZE ||
|
||||||
cur_op->req.hdr.opcode == OSD_OP_SEC_ROLLBACK)
|
cur_op->req.hdr.opcode == OSD_OP_SEC_ROLLBACK)
|
||||||
@@ -292,27 +309,24 @@ void osd_messenger_t::handle_op_hdr(osd_client_t *cl)
|
|||||||
if (cur_op->req.sec_stab.len > 0)
|
if (cur_op->req.sec_stab.len > 0)
|
||||||
{
|
{
|
||||||
cur_op->buf = memalign_or_die(MEM_ALIGNMENT, cur_op->req.sec_stab.len);
|
cur_op->buf = memalign_or_die(MEM_ALIGNMENT, cur_op->req.sec_stab.len);
|
||||||
cl->recv_list.push_back(cur_op->buf, cur_op->req.sec_stab.len);
|
|
||||||
}
|
}
|
||||||
cl->read_remaining = cur_op->req.sec_stab.len;
|
cl->read_op_size = cur_op->req.sec_stab.len;
|
||||||
}
|
}
|
||||||
else if (cur_op->req.hdr.opcode == OSD_OP_SEC_READ_BMP)
|
else if (cur_op->req.hdr.opcode == OSD_OP_SEC_READ_BMP)
|
||||||
{
|
{
|
||||||
if (cur_op->req.sec_read_bmp.len > 0)
|
if (cur_op->req.sec_read_bmp.len > 0)
|
||||||
{
|
{
|
||||||
cur_op->buf = memalign_or_die(MEM_ALIGNMENT, cur_op->req.sec_read_bmp.len);
|
cur_op->buf = memalign_or_die(MEM_ALIGNMENT, cur_op->req.sec_read_bmp.len);
|
||||||
cl->recv_list.push_back(cur_op->buf, cur_op->req.sec_read_bmp.len);
|
|
||||||
}
|
}
|
||||||
cl->read_remaining = cur_op->req.sec_read_bmp.len;
|
cl->read_op_size = cur_op->req.sec_read_bmp.len;
|
||||||
}
|
}
|
||||||
else if (cur_op->req.hdr.opcode == OSD_OP_WRITE)
|
else if (cur_op->req.hdr.opcode == OSD_OP_WRITE)
|
||||||
{
|
{
|
||||||
if (cur_op->req.rw.len > 0)
|
if (cur_op->req.rw.len > 0)
|
||||||
{
|
{
|
||||||
cur_op->buf = memalign_or_die(MEM_ALIGNMENT, cur_op->req.rw.len);
|
cur_op->buf = memalign_or_die(MEM_ALIGNMENT, cur_op->req.rw.len);
|
||||||
cl->recv_list.push_back(cur_op->buf, cur_op->req.rw.len);
|
|
||||||
}
|
}
|
||||||
cl->read_remaining = cur_op->req.rw.len;
|
cl->read_op_size = cur_op->req.rw.len;
|
||||||
}
|
}
|
||||||
else if (cur_op->req.hdr.opcode == OSD_OP_SHOW_CONFIG)
|
else if (cur_op->req.hdr.opcode == OSD_OP_SHOW_CONFIG)
|
||||||
{
|
{
|
||||||
@@ -320,44 +334,20 @@ void osd_messenger_t::handle_op_hdr(osd_client_t *cl)
|
|||||||
{
|
{
|
||||||
cur_op->buf = malloc_or_die(cur_op->req.show_conf.json_len+1);
|
cur_op->buf = malloc_or_die(cur_op->req.show_conf.json_len+1);
|
||||||
((uint8_t*)cur_op->buf)[cur_op->req.show_conf.json_len] = 0;
|
((uint8_t*)cur_op->buf)[cur_op->req.show_conf.json_len] = 0;
|
||||||
cl->recv_list.push_back(cur_op->buf, cur_op->req.show_conf.json_len);
|
|
||||||
}
|
}
|
||||||
cl->read_remaining = cur_op->req.show_conf.json_len;
|
cl->read_op_size = cur_op->req.show_conf.json_len;
|
||||||
}
|
}
|
||||||
/*else if (cur_op->req.hdr.opcode == OSD_OP_READ ||
|
if (cl->proto_csum_status == MSGR_CSUM_FULL ||
|
||||||
cur_op->req.hdr.opcode == OSD_OP_SCRUB ||
|
cl->read_op_size > 0 && cl->proto_csum_status == MSGR_CSUM_PAYLOAD)
|
||||||
cur_op->req.hdr.opcode == OSD_OP_DESCRIBE)
|
|
||||||
{
|
{
|
||||||
cl->read_remaining = 0;
|
cl->read_op_size += 8;
|
||||||
}*/
|
|
||||||
if (cl->read_remaining > 0)
|
|
||||||
{
|
|
||||||
// Read data
|
|
||||||
cl->read_state = CL_READ_DATA;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Operation is ready
|
|
||||||
cl->received_ops.push_back(cur_op);
|
|
||||||
set_immediate_ops.push_back(cur_op);
|
|
||||||
cl->read_op = NULL;
|
|
||||||
cl->read_state = 0;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool osd_messenger_t::handle_reply_hdr(osd_client_t *cl)
|
bool osd_messenger_t::allocate_reply_buffers(osd_client_t *cl, osd_op_t *op)
|
||||||
{
|
{
|
||||||
auto req_it = cl->sent_ops.find(cl->read_op->req.hdr.id);
|
cl->read_op_size = 0;
|
||||||
if (req_it == cl->sent_ops.end())
|
|
||||||
{
|
|
||||||
// Command out of sync. Drop connection
|
|
||||||
fprintf(stderr, "Client %ju command out of sync: id %ju\n", cl->client_id, cl->read_op->req.hdr.id);
|
|
||||||
stop_client(cl->client_id);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
osd_op_t *op = req_it->second;
|
|
||||||
memcpy(op->reply.buf, cl->read_op->req.buf, OSD_PACKET_SIZE);
|
|
||||||
cl->sent_ops.erase(req_it);
|
|
||||||
if (op->reply.hdr.opcode == OSD_OP_SEC_READ || op->reply.hdr.opcode == OSD_OP_READ)
|
if (op->reply.hdr.opcode == OSD_OP_SEC_READ || op->reply.hdr.opcode == OSD_OP_READ)
|
||||||
{
|
{
|
||||||
// Read data. In this case we assume that the buffer is preallocated by the caller (!)
|
// Read data. In this case we assume that the buffer is preallocated by the caller (!)
|
||||||
@@ -368,97 +358,368 @@ bool osd_messenger_t::handle_reply_hdr(osd_client_t *cl)
|
|||||||
// Check reply length to not overflow the buffer
|
// Check reply length to not overflow the buffer
|
||||||
fprintf(stderr, "Client %ju read reply of different length: expected %u+%u, got %jd+%u\n",
|
fprintf(stderr, "Client %ju read reply of different length: expected %u+%u, got %jd+%u\n",
|
||||||
cl->client_id, expected_size, op->bitmap_len, op->reply.hdr.retval, bmp_len);
|
cl->client_id, expected_size, op->bitmap_len, op->reply.hdr.retval, bmp_len);
|
||||||
cl->sent_ops[op->req.hdr.id] = op;
|
|
||||||
stop_client(cl->client_id);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (bmp_len > 0)
|
if (bmp_len > 0)
|
||||||
{
|
{
|
||||||
assert(op->bitmap);
|
assert(op->bitmap);
|
||||||
cl->recv_list.push_back(op->bitmap, bmp_len);
|
cl->read_op_size += bmp_len;
|
||||||
cl->read_remaining += bmp_len;
|
|
||||||
}
|
}
|
||||||
if (op->reply.hdr.retval > 0)
|
if (op->reply.hdr.retval > 0)
|
||||||
{
|
{
|
||||||
assert(op->iov.count > 0);
|
assert(op->iov.count > 0);
|
||||||
cl->recv_list.append(op->iov);
|
cl->read_op_size += op->reply.hdr.retval;
|
||||||
cl->read_remaining += op->reply.hdr.retval;
|
|
||||||
}
|
}
|
||||||
if (cl->read_remaining == 0)
|
|
||||||
{
|
|
||||||
goto reuse;
|
|
||||||
}
|
|
||||||
delete cl->read_op;
|
|
||||||
cl->read_op = op;
|
|
||||||
cl->read_state = CL_READ_REPLY_DATA;
|
|
||||||
}
|
}
|
||||||
else if (op->reply.hdr.opcode == OSD_OP_SEC_LIST && op->reply.hdr.retval > 0)
|
else if (op->reply.hdr.opcode == OSD_OP_SEC_LIST && op->reply.hdr.retval > 0)
|
||||||
{
|
{
|
||||||
assert(!op->iov.count);
|
assert(!op->iov.count);
|
||||||
delete cl->read_op;
|
cl->read_op_size = sizeof(obj_ver_id) * op->reply.hdr.retval;
|
||||||
cl->read_op = op;
|
op->buf = memalign_or_die(MEM_ALIGNMENT, cl->read_op_size);
|
||||||
cl->read_state = CL_READ_REPLY_DATA;
|
|
||||||
cl->read_remaining = sizeof(obj_ver_id) * op->reply.hdr.retval;
|
|
||||||
op->buf = memalign_or_die(MEM_ALIGNMENT, cl->read_remaining);
|
|
||||||
cl->recv_list.push_back(op->buf, cl->read_remaining);
|
|
||||||
}
|
}
|
||||||
else if (op->reply.hdr.opcode == OSD_OP_SEC_READ_BMP && op->reply.hdr.retval > 0)
|
else if (op->reply.hdr.opcode == OSD_OP_SEC_READ_BMP && op->reply.hdr.retval > 0)
|
||||||
{
|
{
|
||||||
assert(!op->iov.count);
|
assert(!op->iov.count);
|
||||||
delete cl->read_op;
|
cl->read_op_size = op->reply.hdr.retval;
|
||||||
cl->read_op = op;
|
|
||||||
cl->read_state = CL_READ_REPLY_DATA;
|
|
||||||
cl->read_remaining = op->reply.hdr.retval;
|
|
||||||
free(op->buf);
|
free(op->buf);
|
||||||
op->buf = memalign_or_die(MEM_ALIGNMENT, cl->read_remaining);
|
op->buf = memalign_or_die(MEM_ALIGNMENT, cl->read_op_size);
|
||||||
cl->recv_list.push_back(op->buf, cl->read_remaining);
|
|
||||||
}
|
}
|
||||||
else if (op->reply.hdr.opcode == OSD_OP_SHOW_CONFIG && op->reply.hdr.retval > 0)
|
else if (op->reply.hdr.opcode == OSD_OP_SHOW_CONFIG && op->reply.hdr.retval > 0)
|
||||||
{
|
{
|
||||||
delete cl->read_op;
|
cl->read_op_size = op->reply.hdr.retval;
|
||||||
cl->read_op = op;
|
|
||||||
cl->read_state = CL_READ_REPLY_DATA;
|
|
||||||
cl->read_remaining = op->reply.hdr.retval;
|
|
||||||
free(op->buf);
|
free(op->buf);
|
||||||
op->buf = malloc_or_die(op->reply.hdr.retval);
|
op->buf = malloc_or_die(op->reply.hdr.retval);
|
||||||
cl->recv_list.push_back(op->buf, op->reply.hdr.retval);
|
|
||||||
}
|
}
|
||||||
else if (op->reply.hdr.opcode == OSD_OP_DESCRIBE && op->reply.describe.result_bytes > 0)
|
else if (op->reply.hdr.opcode == OSD_OP_DESCRIBE && op->reply.describe.result_bytes > 0)
|
||||||
{
|
{
|
||||||
delete cl->read_op;
|
cl->read_op_size = op->reply.describe.result_bytes;
|
||||||
cl->read_op = op;
|
|
||||||
cl->read_state = CL_READ_REPLY_DATA;
|
|
||||||
cl->read_remaining = op->reply.describe.result_bytes;
|
|
||||||
free(op->buf);
|
free(op->buf);
|
||||||
op->buf = malloc_or_die(op->reply.describe.result_bytes);
|
op->buf = malloc_or_die(op->reply.describe.result_bytes);
|
||||||
cl->recv_list.push_back(op->buf, op->reply.describe.result_bytes);
|
|
||||||
}
|
}
|
||||||
else
|
if (cl->proto_csum_status == MSGR_CSUM_FULL ||
|
||||||
|
cl->read_op_size > 0 && cl->proto_csum_status == MSGR_CSUM_PAYLOAD)
|
||||||
{
|
{
|
||||||
reuse:
|
cl->read_op_size += 8;
|
||||||
// It's fine to reuse cl->read_op for the next reply
|
|
||||||
handle_reply_ready(op);
|
|
||||||
cl->recv_list.push_back(cl->read_op->req.buf, OSD_PACKET_SIZE);
|
|
||||||
cl->read_remaining = OSD_PACKET_SIZE;
|
|
||||||
cl->read_state = CL_READ_HDR;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void osd_messenger_t::handle_reply_ready(osd_op_t *op)
|
bool osd_messenger_t::op_copy_from(osd_client_t *cl, uint8_t *src, size_t src_len, size_t & done)
|
||||||
{
|
{
|
||||||
// Measure subop latency
|
osd_op_t *op = cl->read_op;
|
||||||
timespec tv_end;
|
size_t from = cl->read_op_pos-OSD_PACKET_SIZE;
|
||||||
clock_gettime(CLOCK_REALTIME, &tv_end);
|
auto op_read_buf = [&](uint8_t *dst, size_t dst_len, bool skip_csum = false)
|
||||||
stats.subop_stat_count[op->req.hdr.opcode]++;
|
|
||||||
if (!stats.subop_stat_count[op->req.hdr.opcode])
|
|
||||||
{
|
{
|
||||||
stats.subop_stat_count[op->req.hdr.opcode]++;
|
if (from < dst_len)
|
||||||
stats.subop_stat_sum[op->req.hdr.opcode] = 0;
|
{
|
||||||
|
size_t n = dst_len-from;
|
||||||
|
if (n > src_len-done)
|
||||||
|
n = src_len-done;
|
||||||
|
if (cl->read_csum_state && !skip_csum)
|
||||||
|
{
|
||||||
|
// it may be skipped if !dst but checksum is still calculated
|
||||||
|
XXH3_64bits_update(cl->read_csum_state, src+done, n);
|
||||||
|
}
|
||||||
|
if (dst)
|
||||||
|
memcpy(dst+from, src+done, n);
|
||||||
|
else
|
||||||
|
assert(!this->osd_num); // NULL buffers are only used by clients
|
||||||
|
done += n;
|
||||||
|
cl->read_op_pos += n;
|
||||||
|
from += n;
|
||||||
|
if (from < dst_len)
|
||||||
|
return false;
|
||||||
|
from = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
from -= dst_len;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
if (op->op_type == OSD_OP_IN)
|
||||||
|
{
|
||||||
|
if (op->req.hdr.opcode == OSD_OP_SEC_WRITE ||
|
||||||
|
op->req.hdr.opcode == OSD_OP_SEC_WRITE_STABLE)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)op->bitmap, op->req.sec_rw.attr_len))
|
||||||
|
return true;
|
||||||
|
if (!op_read_buf((uint8_t*)op->buf, op->req.sec_rw.len))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (op->req.hdr.opcode == OSD_OP_SEC_STABILIZE ||
|
||||||
|
op->req.hdr.opcode == OSD_OP_SEC_ROLLBACK)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)op->buf, op->req.sec_stab.len))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (op->req.hdr.opcode == OSD_OP_SEC_READ_BMP)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)op->buf, op->req.sec_read_bmp.len))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (op->req.hdr.opcode == OSD_OP_WRITE)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)op->buf, op->req.rw.len))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (op->req.hdr.opcode == OSD_OP_SHOW_CONFIG)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)op->buf, op->req.show_conf.json_len))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
stats.subop_stat_sum[op->req.hdr.opcode] += (
|
else
|
||||||
(tv_end.tv_sec - op->tv_begin.tv_sec)*1000000 +
|
{
|
||||||
(tv_end.tv_nsec - op->tv_begin.tv_nsec)/1000
|
if (op->reply.hdr.opcode == OSD_OP_SEC_READ)
|
||||||
);
|
{
|
||||||
set_immediate_ops.push_back(op);
|
if (op->reply.sec_rw.attr_len > 0)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)op->bitmap, op->reply.sec_rw.attr_len))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (op->reply.hdr.retval > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < op->iov.count; i++)
|
||||||
|
if (!op_read_buf((uint8_t*)op->iov.buf[i].iov_base, op->iov.buf[i].iov_len))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (op->reply.hdr.opcode == OSD_OP_READ)
|
||||||
|
{
|
||||||
|
if (op->reply.rw.bitmap_len > 0)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)op->bitmap, op->reply.rw.bitmap_len))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (op->reply.hdr.retval > 0)
|
||||||
|
{
|
||||||
|
if (op->enc)
|
||||||
|
{
|
||||||
|
if (!op_decrypted_copy_data_from(cl, src, src_len, from, done))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < op->iov.count; i++)
|
||||||
|
if (!op_read_buf((uint8_t*)op->iov.buf[i].iov_base, op->iov.buf[i].iov_len))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (op->reply.hdr.opcode == OSD_OP_SEC_LIST && op->reply.hdr.retval > 0)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)op->buf, sizeof(obj_ver_id) * op->reply.hdr.retval))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if ((op->reply.hdr.opcode == OSD_OP_SEC_READ_BMP ||
|
||||||
|
op->reply.hdr.opcode == OSD_OP_SHOW_CONFIG) && op->reply.hdr.retval > 0)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)op->buf, op->reply.hdr.retval))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (op->reply.hdr.opcode == OSD_OP_DESCRIBE && op->reply.describe.result_bytes > 0)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)op->buf, op->reply.describe.result_bytes))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cl->proto_csum_status == MSGR_CSUM_FULL ||
|
||||||
|
cl->read_op_size > 0 && cl->proto_csum_status == MSGR_CSUM_PAYLOAD)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)&op->csum, 8, true))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return handle_finished_op(cl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void osd_messenger_t::op_get_read_buffers(osd_client_t *cl, std::vector<iovec> & lst)
|
||||||
|
{
|
||||||
|
osd_op_t *op = cl->read_op;
|
||||||
|
size_t from = cl->read_op_pos-OSD_PACKET_SIZE;
|
||||||
|
size_t done = 0;
|
||||||
|
auto op_read_buf = [&](uint8_t *dst, size_t dst_len)
|
||||||
|
{
|
||||||
|
if (lst.size() >= IOV_MAX)
|
||||||
|
return false;
|
||||||
|
if (from < dst_len)
|
||||||
|
{
|
||||||
|
lst.push_back((iovec){ .iov_base = dst+from, .iov_len = dst_len-from });
|
||||||
|
cl->read_op_pos += dst_len-from;
|
||||||
|
done += dst_len-from;
|
||||||
|
from = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
from -= dst_len;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
if (op->op_type == OSD_OP_IN)
|
||||||
|
{
|
||||||
|
if (op->req.hdr.opcode == OSD_OP_SEC_WRITE ||
|
||||||
|
op->req.hdr.opcode == OSD_OP_SEC_WRITE_STABLE)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)op->bitmap, op->req.sec_rw.attr_len))
|
||||||
|
return;
|
||||||
|
if (!op_read_buf((uint8_t*)op->buf, op->req.sec_rw.len))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (op->req.hdr.opcode == OSD_OP_SEC_STABILIZE ||
|
||||||
|
op->req.hdr.opcode == OSD_OP_SEC_ROLLBACK)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)op->buf, op->req.sec_stab.len))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (op->req.hdr.opcode == OSD_OP_SEC_READ_BMP)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)op->buf, op->req.sec_read_bmp.len))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (op->req.hdr.opcode == OSD_OP_WRITE)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)op->buf, op->req.rw.len))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (op->req.hdr.opcode == OSD_OP_SHOW_CONFIG)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)op->buf, op->req.show_conf.json_len))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (op->reply.hdr.opcode == OSD_OP_SEC_READ)
|
||||||
|
{
|
||||||
|
if (op->reply.sec_rw.attr_len > 0)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)op->bitmap, op->reply.sec_rw.attr_len))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (op->reply.hdr.retval > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < op->iov.count; i++)
|
||||||
|
if (!op_read_buf((uint8_t*)op->iov.buf[i].iov_base, op->iov.buf[i].iov_len))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (op->reply.hdr.opcode == OSD_OP_READ)
|
||||||
|
{
|
||||||
|
if (op->reply.rw.bitmap_len > 0)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)op->bitmap, op->reply.rw.bitmap_len))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (op->reply.hdr.retval > 0)
|
||||||
|
{
|
||||||
|
if (op->enc)
|
||||||
|
{
|
||||||
|
cl->read_op_inline_decrypt_pos = cl->read_op_pos;
|
||||||
|
cl->read_op_pos = cl->read_op_inline_decrypt_in + OSD_PACKET_SIZE + op->reply.rw.bitmap_len;
|
||||||
|
from = cl->read_op_inline_decrypt_in;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < op->iov.count; i++)
|
||||||
|
{
|
||||||
|
if (!op->iov.buf[i].iov_base)
|
||||||
|
{
|
||||||
|
// When we recvmsg directly into the operation without copying,
|
||||||
|
// we need some place for all buffers, so we allocate temporary
|
||||||
|
// buffers for all skipped parts
|
||||||
|
op_alloc_temp_buffers(op, i);
|
||||||
|
}
|
||||||
|
if (!op_read_buf((uint8_t*)op->iov.buf[i].iov_base, op->iov.buf[i].iov_len))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (op->reply.hdr.opcode == OSD_OP_SEC_LIST && op->reply.hdr.retval > 0)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)op->buf, sizeof(obj_ver_id) * op->reply.hdr.retval))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if ((op->reply.hdr.opcode == OSD_OP_SEC_READ_BMP ||
|
||||||
|
op->reply.hdr.opcode == OSD_OP_SHOW_CONFIG) && op->reply.hdr.retval > 0)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)op->buf, op->reply.hdr.retval))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (op->reply.hdr.opcode == OSD_OP_DESCRIBE && op->reply.describe.result_bytes > 0)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)op->buf, op->reply.describe.result_bytes))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cl->proto_csum_status == MSGR_CSUM_FULL ||
|
||||||
|
cl->read_op_size > 0 && cl->proto_csum_status == MSGR_CSUM_PAYLOAD)
|
||||||
|
{
|
||||||
|
if (!op_read_buf((uint8_t*)&op->csum, 8))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void osd_messenger_t::op_alloc_temp_buffers(osd_op_t *op, int i)
|
||||||
|
{
|
||||||
|
size_t total_skip = 0;
|
||||||
|
for (int j = i; j < op->iov.count; j++)
|
||||||
|
{
|
||||||
|
if (!op->iov.buf[j].iov_base)
|
||||||
|
{
|
||||||
|
total_skip += op->iov.buf[j].iov_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(total_skip);
|
||||||
|
assert(!op->rmw_buf);
|
||||||
|
op->rmw_buf = malloc_or_die(total_skip);
|
||||||
|
total_skip = 0;
|
||||||
|
for (int j = i; j < op->iov.count; j++)
|
||||||
|
{
|
||||||
|
if (!op->iov.buf[j].iov_base)
|
||||||
|
{
|
||||||
|
op->iov.buf[j].iov_base = (uint8_t*)op->rmw_buf + total_skip;
|
||||||
|
total_skip += op->iov.buf[j].iov_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool osd_messenger_t::handle_finished_op(osd_client_t *cl)
|
||||||
|
{
|
||||||
|
osd_op_t *op = cl->read_op;
|
||||||
|
if (cl->proto_csum_status == MSGR_CSUM_FULL ||
|
||||||
|
cl->read_op_size > 0 && cl->proto_csum_status == MSGR_CSUM_PAYLOAD)
|
||||||
|
{
|
||||||
|
uint64_t real_csum = XXH3_64bits_digest(cl->read_csum_state);
|
||||||
|
if (op->csum != real_csum)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Client %ju checksum mismatch for received data: expected %016jx, got %016jx, disconnecting client\n",
|
||||||
|
cl->client_id, op->csum, real_csum);
|
||||||
|
stop_client(cl->client_id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (op->op_type == OSD_OP_IN)
|
||||||
|
{
|
||||||
|
// Operation is ready
|
||||||
|
cl->received_ops.push_back(op);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Inline decryption
|
||||||
|
if (cl->read_op_inline_decrypt_pos != (size_t)-1)
|
||||||
|
{
|
||||||
|
op_decrypt_inline(cl);
|
||||||
|
cl->read_op_inline_decrypt_pos = (size_t)-1;
|
||||||
|
}
|
||||||
|
// Measure subop (outbound op) latency
|
||||||
|
timespec tv_end;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &tv_end);
|
||||||
|
stats.subop_stat_count[op->req.hdr.opcode]++;
|
||||||
|
if (!stats.subop_stat_count[op->req.hdr.opcode])
|
||||||
|
{
|
||||||
|
stats.subop_stat_count[op->req.hdr.opcode]++;
|
||||||
|
stats.subop_stat_sum[op->req.hdr.opcode] = 0;
|
||||||
|
}
|
||||||
|
stats.subop_stat_sum[op->req.hdr.opcode] += (
|
||||||
|
(tv_end.tv_sec - op->tv_begin.tv_sec)*1000000 +
|
||||||
|
(tv_end.tv_nsec - op->tv_begin.tv_nsec)/1000
|
||||||
|
);
|
||||||
|
}
|
||||||
|
set_immediate_ops.push_back(op);
|
||||||
|
cl->read_op = NULL;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+550
-120
@@ -7,6 +7,13 @@
|
|||||||
|
|
||||||
#include "messenger.h"
|
#include "messenger.h"
|
||||||
|
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
#include <openssl/bio.h>
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#include <openssl/pem.h>
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
void osd_messenger_t::outbox_push(osd_op_t *cur_op)
|
void osd_messenger_t::outbox_push(osd_op_t *cur_op)
|
||||||
{
|
{
|
||||||
assert(cur_op->client_id);
|
assert(cur_op->client_id);
|
||||||
@@ -21,6 +28,7 @@ void osd_messenger_t::outbox_push(osd_op_t *cur_op)
|
|||||||
{
|
{
|
||||||
clock_gettime(CLOCK_REALTIME, &cur_op->tv_begin);
|
clock_gettime(CLOCK_REALTIME, &cur_op->tv_begin);
|
||||||
cur_op->req.hdr.id = ++cl->send_op_id;
|
cur_op->req.hdr.id = ++cl->send_op_id;
|
||||||
|
cl->sent_ops[cur_op->req.hdr.id] = cur_op;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -37,77 +45,9 @@ void osd_messenger_t::outbox_push(osd_op_t *cur_op)
|
|||||||
}
|
}
|
||||||
// Can't be not found because client IDs are unique
|
// Can't be not found because client IDs are unique
|
||||||
assert(found);
|
assert(found);
|
||||||
}
|
|
||||||
auto & to_send_list = cl->write_msg.msg_iovlen ? cl->next_send_list : cl->send_list;
|
|
||||||
auto & to_outbox = cl->write_msg.msg_iovlen ? cl->next_outbox : cl->outbox;
|
|
||||||
if (cur_op->op_type == OSD_OP_IN)
|
|
||||||
{
|
|
||||||
measure_exec(cur_op);
|
measure_exec(cur_op);
|
||||||
to_send_list.push_back((iovec){ .iov_base = cur_op->reply.buf, .iov_len = OSD_PACKET_SIZE });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
to_send_list.push_back((iovec){ .iov_base = cur_op->req.buf, .iov_len = OSD_PACKET_SIZE });
|
|
||||||
cl->sent_ops[cur_op->req.hdr.id] = cur_op;
|
|
||||||
}
|
|
||||||
to_outbox.push_back((msgr_sendp_t){ .op = cur_op, .flags = MSGR_SENDP_HDR });
|
|
||||||
// Bitmap
|
|
||||||
if (cur_op->op_type == OSD_OP_IN &&
|
|
||||||
cur_op->req.hdr.opcode == OSD_OP_SEC_READ &&
|
|
||||||
cur_op->reply.sec_rw.attr_len > 0)
|
|
||||||
{
|
|
||||||
to_send_list.push_back((iovec){
|
|
||||||
.iov_base = cur_op->bitmap,
|
|
||||||
.iov_len = cur_op->reply.sec_rw.attr_len,
|
|
||||||
});
|
|
||||||
to_outbox.push_back((msgr_sendp_t){ .op = cur_op, .flags = 0 });
|
|
||||||
}
|
|
||||||
else if (cur_op->op_type == OSD_OP_OUT &&
|
|
||||||
(cur_op->req.hdr.opcode == OSD_OP_SEC_WRITE || cur_op->req.hdr.opcode == OSD_OP_SEC_WRITE_STABLE) &&
|
|
||||||
cur_op->req.sec_rw.attr_len > 0)
|
|
||||||
{
|
|
||||||
to_send_list.push_back((iovec){
|
|
||||||
.iov_base = cur_op->bitmap,
|
|
||||||
.iov_len = cur_op->req.sec_rw.attr_len,
|
|
||||||
});
|
|
||||||
to_outbox.push_back((msgr_sendp_t){ .op = cur_op, .flags = 0 });
|
|
||||||
}
|
|
||||||
// Operation data
|
|
||||||
if ((cur_op->op_type == OSD_OP_IN
|
|
||||||
? (cur_op->req.hdr.opcode == OSD_OP_READ ||
|
|
||||||
cur_op->req.hdr.opcode == OSD_OP_SEC_READ ||
|
|
||||||
cur_op->req.hdr.opcode == OSD_OP_SEC_LIST ||
|
|
||||||
cur_op->req.hdr.opcode == OSD_OP_SHOW_CONFIG ||
|
|
||||||
cur_op->req.hdr.opcode == OSD_OP_DESCRIBE)
|
|
||||||
: (cur_op->req.hdr.opcode == OSD_OP_WRITE ||
|
|
||||||
cur_op->req.hdr.opcode == OSD_OP_SEC_WRITE ||
|
|
||||||
cur_op->req.hdr.opcode == OSD_OP_SEC_WRITE_STABLE ||
|
|
||||||
cur_op->req.hdr.opcode == OSD_OP_SEC_STABILIZE ||
|
|
||||||
cur_op->req.hdr.opcode == OSD_OP_SEC_ROLLBACK ||
|
|
||||||
cur_op->req.hdr.opcode == OSD_OP_SHOW_CONFIG)) && cur_op->iov.count > 0)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < cur_op->iov.count; i++)
|
|
||||||
{
|
|
||||||
if (cur_op->iov.buf[i].iov_len > 0)
|
|
||||||
{
|
|
||||||
assert(cur_op->iov.buf[i].iov_base);
|
|
||||||
to_send_list.push_back(cur_op->iov.buf[i]);
|
|
||||||
to_outbox.push_back((msgr_sendp_t){ .op = cur_op, .flags = 0 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cur_op->req.hdr.opcode == OSD_OP_SEC_READ_BMP)
|
|
||||||
{
|
|
||||||
if (cur_op->op_type == OSD_OP_IN && cur_op->reply.hdr.retval > 0)
|
|
||||||
to_send_list.push_back((iovec){ .iov_base = cur_op->buf, .iov_len = (size_t)cur_op->reply.hdr.retval });
|
|
||||||
else if (cur_op->op_type == OSD_OP_OUT && cur_op->req.sec_read_bmp.len > 0)
|
|
||||||
to_send_list.push_back((iovec){ .iov_base = cur_op->buf, .iov_len = (size_t)cur_op->req.sec_read_bmp.len });
|
|
||||||
to_outbox.push_back((msgr_sendp_t){ .op = cur_op, .flags = 0 });
|
|
||||||
}
|
|
||||||
if (cur_op->op_type == OSD_OP_IN)
|
|
||||||
{
|
|
||||||
to_outbox[to_outbox.size()-1].flags |= MSGR_SENDP_FREE;
|
|
||||||
}
|
}
|
||||||
|
cl->write_ops.push_back(cur_op);
|
||||||
#ifdef WITH_RDMA
|
#ifdef WITH_RDMA
|
||||||
if (cl->peer_state == PEER_RDMA)
|
if (cl->peer_state == PEER_RDMA)
|
||||||
{
|
{
|
||||||
@@ -118,7 +58,7 @@ void osd_messenger_t::outbox_push(osd_op_t *cur_op)
|
|||||||
if (!ringloop)
|
if (!ringloop)
|
||||||
{
|
{
|
||||||
// FIXME: It's worse because it doesn't allow batching
|
// FIXME: It's worse because it doesn't allow batching
|
||||||
while (cl->outbox.size())
|
while (cl->write_op || cl->write_ops.size())
|
||||||
{
|
{
|
||||||
try_send(cl);
|
try_send(cl);
|
||||||
}
|
}
|
||||||
@@ -183,13 +123,105 @@ void osd_messenger_t::measure_exec(osd_op_t *cur_op)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool osd_messenger_t::ssl_do_handshake(osd_client_t *cl)
|
||||||
|
{
|
||||||
|
int r = SSL_do_handshake(cl->ssl_cli);
|
||||||
|
if (r > 0)
|
||||||
|
{
|
||||||
|
cl->ssl_handshake_done = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
r = SSL_get_error(cl->ssl_cli, r);
|
||||||
|
if (r != SSL_ERROR_WANT_WRITE && r != SSL_ERROR_WANT_READ)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Client %ju TLS handshake error: %s, stopping client\n", cl->client_id, ERR_error_string(ERR_get_error(), NULL));
|
||||||
|
stop_client(cl->client_id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (r == SSL_ERROR_WANT_WRITE)
|
||||||
|
{
|
||||||
|
cl->ssl_want_write = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool osd_messenger_t::ssl_do_encrypt(osd_client_t *cl)
|
||||||
|
{
|
||||||
|
if (cl->send_list.size() >= IOV_MAX || !cl->ssl_want_write)
|
||||||
|
return false;
|
||||||
|
size_t prev_size = cl->ssl_out_buf_size;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
size_t min_cap = cl->ssl_out_buf_size*2;
|
||||||
|
if (min_cap < 16384)
|
||||||
|
min_cap = 16384;
|
||||||
|
if (cl->ssl_out_buf_cap < min_cap)
|
||||||
|
{
|
||||||
|
uint8_t *old_buf = cl->ssl_out_buf;
|
||||||
|
uint8_t *old_end = old_buf + cl->ssl_out_buf_cap;
|
||||||
|
cl->ssl_out_buf = (uint8_t*)realloc_or_die(cl->ssl_out_buf, min_cap);
|
||||||
|
cl->ssl_out_buf_cap = min_cap;
|
||||||
|
for (auto & iov: cl->send_list)
|
||||||
|
{
|
||||||
|
if (iov.iov_base >= old_buf && iov.iov_base < old_end)
|
||||||
|
iov.iov_base = cl->ssl_out_buf + ((uint8_t*)iov.iov_base - old_buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool full_read = (ssl_do_encrypt_to(cl, cl->ssl_out_buf+cl->ssl_out_buf_size,
|
||||||
|
cl->ssl_out_buf_cap-cl->ssl_out_buf_size) == cl->ssl_out_buf_cap-cl->ssl_out_buf_size);
|
||||||
|
if (!full_read)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (cl->ssl_out_buf_size > prev_size)
|
||||||
|
cl->send_list.push_back((iovec){ .iov_base = cl->ssl_out_buf+prev_size, .iov_len = cl->ssl_out_buf_size-prev_size });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t osd_messenger_t::ssl_do_encrypt_to(osd_client_t *cl, uint8_t *buf, size_t size)
|
||||||
|
{
|
||||||
|
if (size < sizeof(msgr_tls_record_hdr_t))
|
||||||
|
return 0;
|
||||||
|
int r = BIO_read(cl->read_from_ssl, buf+sizeof(msgr_tls_record_hdr_t), size-sizeof(msgr_tls_record_hdr_t));
|
||||||
|
if (r > 0)
|
||||||
|
{
|
||||||
|
if (r < size-sizeof(msgr_tls_record_hdr_t))
|
||||||
|
cl->ssl_want_write = false;
|
||||||
|
msgr_tls_record_hdr_t *hdr = (msgr_tls_record_hdr_t*)buf;
|
||||||
|
hdr->encrypted = 1;
|
||||||
|
hdr->size = r;
|
||||||
|
return r+sizeof(msgr_tls_record_hdr_t);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool osd_messenger_t::try_send(osd_client_t *cl)
|
bool osd_messenger_t::try_send(osd_client_t *cl)
|
||||||
{
|
{
|
||||||
if (!cl->send_list.size() || cl->write_msg.msg_iovlen > 0 || cl->peer_state == PEER_STOPPED || cl->peer_fd < 0)
|
if (!cl->write_op && !cl->write_ops.size() && !cl->ssl_want_write ||
|
||||||
|
cl->write_msg.msg_iovlen > 0 || cl->peer_state == PEER_STOPPED || cl->peer_fd < 0)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
assert(cl->peer_state != PEER_RDMA);
|
assert(cl->peer_state != PEER_RDMA);
|
||||||
|
if (cl->ssl_cli && !cl->ssl_handshake_done)
|
||||||
|
{
|
||||||
|
bool ok = ssl_do_encrypt(cl);
|
||||||
|
assert(ok && cl->send_list.size() > 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while ((cl->write_op || cl->write_ops.size()) && cl->send_list.size() < IOV_MAX)
|
||||||
|
{
|
||||||
|
next_write_op(cl);
|
||||||
|
osd_op_t *op = cl->write_op;
|
||||||
|
op_get_write_buffers(cl, cl->send_list);
|
||||||
|
if (!cl->write_op && op->op_type == OSD_OP_IN)
|
||||||
|
{
|
||||||
|
cl->send_free_ops.push_back(op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (ringloop && !use_sync_send_recv)
|
if (ringloop && !use_sync_send_recv)
|
||||||
{
|
{
|
||||||
auto iothread = iothreads.size() ? iothreads[cl->peer_fd % iothreads.size()] : NULL;
|
auto iothread = iothreads.size() ? iothreads[cl->peer_fd % iothreads.size()] : NULL;
|
||||||
@@ -202,20 +234,24 @@ bool osd_messenger_t::try_send(osd_client_t *cl)
|
|||||||
data_local = {};
|
data_local = {};
|
||||||
}
|
}
|
||||||
if (!sqe)
|
if (!sqe)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
cl->send_list_size = 0;
|
||||||
|
for (auto & iov: cl->send_list)
|
||||||
|
{
|
||||||
|
cl->send_list_size += iov.iov_len;
|
||||||
|
}
|
||||||
cl->write_msg.msg_iov = cl->send_list.data();
|
cl->write_msg.msg_iov = cl->send_list.data();
|
||||||
cl->write_msg.msg_iovlen = cl->send_list.size() < IOV_MAX ? cl->send_list.size() : IOV_MAX;
|
cl->write_msg.msg_iovlen = cl->send_list.size() < IOV_MAX ? cl->send_list.size() : IOV_MAX;
|
||||||
cl->refs++;
|
cl->refs++;
|
||||||
ring_data_t* data = ((ring_data_t*)sqe->user_data);
|
ring_data_t* data = ((ring_data_t*)sqe->user_data);
|
||||||
data->callback = [this, cl](ring_data_t *data) { handle_send(data->res, data->prev, data->more, cl); };
|
data->callback = [this, cl](ring_data_t *data) { handle_send(data->res, data->prev, data->more, cl); };
|
||||||
bool use_zc = has_sendmsg_zc && min_zerocopy_send_size >= 0;
|
bool use_zc = has_sendmsg_zc && min_zerocopy_send_size >= 0;
|
||||||
if (use_zc && min_zerocopy_send_size > 0)
|
if (use_zc && min_zerocopy_send_size > 0 &&
|
||||||
|
cl->send_list_size/cl->write_msg.msg_iovlen < min_zerocopy_send_size)
|
||||||
{
|
{
|
||||||
size_t avg_size = 0;
|
use_zc = false;
|
||||||
for (size_t i = 0; i < cl->write_msg.msg_iovlen; i++)
|
|
||||||
avg_size += cl->write_msg.msg_iov[i].iov_len;
|
|
||||||
if (avg_size/cl->write_msg.msg_iovlen < min_zerocopy_send_size)
|
|
||||||
use_zc = false;
|
|
||||||
}
|
}
|
||||||
if (use_zc)
|
if (use_zc)
|
||||||
{
|
{
|
||||||
@@ -246,6 +282,21 @@ bool osd_messenger_t::try_send(osd_client_t *cl)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void osd_messenger_t::next_write_op(osd_client_t *cl)
|
||||||
|
{
|
||||||
|
if (!cl->write_op)
|
||||||
|
{
|
||||||
|
cl->write_op = cl->write_ops.front();
|
||||||
|
cl->write_ops.pop_front();
|
||||||
|
if (cl->proto_csum_status == MSGR_CSUM_FULL || cl->proto_csum_status == MSGR_CSUM_PAYLOAD)
|
||||||
|
{
|
||||||
|
if (!cl->write_csum_state)
|
||||||
|
cl->write_csum_state = XXH3_createState();
|
||||||
|
XXH3_64bits_reset(cl->write_csum_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void osd_messenger_t::send_replies()
|
void osd_messenger_t::send_replies()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < write_ready_clients.size(); i++)
|
for (int i = 0; i < write_ready_clients.size(); i++)
|
||||||
@@ -266,6 +317,7 @@ void osd_messenger_t::handle_send(int result, bool prev, bool more, osd_client_t
|
|||||||
if (!prev)
|
if (!prev)
|
||||||
{
|
{
|
||||||
cl->write_msg.msg_iovlen = 0;
|
cl->write_msg.msg_iovlen = 0;
|
||||||
|
cl->send_list.clear();
|
||||||
}
|
}
|
||||||
if (!more)
|
if (!more)
|
||||||
{
|
{
|
||||||
@@ -298,57 +350,32 @@ void osd_messenger_t::handle_send(int result, bool prev, bool more, osd_client_t
|
|||||||
cl->zc_free_list.erase(cl->zc_free_list.begin(), cl->zc_free_list.begin()+i+1);
|
cl->zc_free_list.erase(cl->zc_free_list.begin(), cl->zc_free_list.begin()+i+1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int done = 0;
|
if (cl->send_list_size > result)
|
||||||
while (result > 0 && done < cl->send_list.size())
|
|
||||||
{
|
{
|
||||||
iovec & iov = cl->send_list[done];
|
fprintf(stderr, "Client %ju socket write error: expected to send "
|
||||||
if (iov.iov_len <= result)
|
"%zu bytes with MSG_WAITALL but sent %u. Disconnecting client\n", cl->client_id, cl->send_list_size, result);
|
||||||
{
|
stop_client(cl->client_id);
|
||||||
if (cl->outbox[done].flags & MSGR_SENDP_FREE)
|
return;
|
||||||
{
|
}
|
||||||
// Reply fully sent
|
for (auto op: cl->send_free_ops)
|
||||||
if (more)
|
{
|
||||||
cl->zc_free_list.push_back(cl->outbox[done].op);
|
if (more)
|
||||||
else
|
cl->zc_free_list.push_back(op);
|
||||||
delete cl->outbox[done].op;
|
|
||||||
}
|
|
||||||
result -= iov.iov_len;
|
|
||||||
done++;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
delete op;
|
||||||
iov.iov_len -= result;
|
|
||||||
iov.iov_base = (uint8_t*)iov.iov_base + result;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
cl->ssl_out_buf_size = 0;
|
||||||
if (more)
|
if (more)
|
||||||
{
|
|
||||||
int expected = cl->send_list.size() < IOV_MAX ? cl->send_list.size() : IOV_MAX;
|
|
||||||
if (done != expected)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Client %ju socket write error: expected to send "
|
|
||||||
"%d iovecs with MSG_WAITALL but sent %d. Disconnecting client\n", cl->client_id, expected, done);
|
|
||||||
stop_client(cl->client_id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cl->zc_free_list.push_back(NULL); // end marker
|
cl->zc_free_list.push_back(NULL); // end marker
|
||||||
}
|
cl->send_free_ops.clear();
|
||||||
if (done > 0)
|
cl->write_state = cl->write_op || cl->write_ops.size() ? CL_WRITE_READY : 0;
|
||||||
|
if ((cl->proto_csum_status & MSGR_CSUM_NEG) && !cl->write_op && !cl->write_ops.size())
|
||||||
{
|
{
|
||||||
cl->send_list.erase(cl->send_list.begin(), cl->send_list.begin()+done);
|
// Checksums negotiated, enable
|
||||||
cl->outbox.erase(cl->outbox.begin(), cl->outbox.begin()+done);
|
cl->proto_csum_status = cl->proto_csum_status & (~MSGR_CSUM_NEG);
|
||||||
}
|
}
|
||||||
if (cl->next_send_list.size())
|
|
||||||
{
|
|
||||||
cl->send_list.insert(cl->send_list.end(), cl->next_send_list.begin(), cl->next_send_list.end());
|
|
||||||
cl->outbox.insert(cl->outbox.end(), cl->next_outbox.begin(), cl->next_outbox.end());
|
|
||||||
cl->next_send_list.clear();
|
|
||||||
cl->next_outbox.clear();
|
|
||||||
}
|
|
||||||
cl->write_state = cl->outbox.size() > 0 ? CL_WRITE_READY : 0;
|
|
||||||
#ifdef WITH_RDMA
|
#ifdef WITH_RDMA
|
||||||
if (cl->rdma_conn && !cl->outbox.size() && cl->peer_state == PEER_RDMA_CONNECTING)
|
if (cl->rdma_conn && !cl->write_op && !cl->write_ops.size() && cl->peer_state == PEER_RDMA_CONNECTING)
|
||||||
{
|
{
|
||||||
// FIXME: Ignore pings during RDMA state transition
|
// FIXME: Ignore pings during RDMA state transition
|
||||||
if (log_level > 0)
|
if (log_level > 0)
|
||||||
@@ -366,3 +393,406 @@ void osd_messenger_t::handle_send(int result, bool prev, bool more, osd_client_t
|
|||||||
write_ready_clients.push_back(cl->client_id);
|
write_ready_clients.push_back(cl->client_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool op_write_headers(osd_op_t *op, std::function<bool(uint8_t*, size_t, bool)> op_write_buf, bool skip_hdr_csum)
|
||||||
|
{
|
||||||
|
if (!op_write_buf((op->op_type == OSD_OP_IN ? op->reply.buf : op->req.buf), OSD_PACKET_SIZE, skip_hdr_csum))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Bitmap
|
||||||
|
if (op->op_type == OSD_OP_IN &&
|
||||||
|
op->req.hdr.opcode == OSD_OP_SEC_READ &&
|
||||||
|
op->reply.sec_rw.attr_len > 0)
|
||||||
|
{
|
||||||
|
if (!op_write_buf((uint8_t*)op->bitmap, op->reply.sec_rw.attr_len, false))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (op->op_type == OSD_OP_OUT &&
|
||||||
|
(op->req.hdr.opcode == OSD_OP_SEC_WRITE || op->req.hdr.opcode == OSD_OP_SEC_WRITE_STABLE) &&
|
||||||
|
op->req.sec_rw.attr_len > 0)
|
||||||
|
{
|
||||||
|
if (!op_write_buf((uint8_t*)op->bitmap, op->req.sec_rw.attr_len, false))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (op->req.hdr.opcode == OSD_OP_SEC_READ_BMP)
|
||||||
|
{
|
||||||
|
if (op->op_type == OSD_OP_IN && op->reply.hdr.retval > 0)
|
||||||
|
{
|
||||||
|
if (!op_write_buf((uint8_t*)op->buf, (size_t)op->reply.hdr.retval, false))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (op->op_type == OSD_OP_OUT && op->req.sec_read_bmp.len > 0)
|
||||||
|
{
|
||||||
|
if (!op_write_buf((uint8_t*)op->buf, (size_t)op->req.sec_read_bmp.len, false))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool op_has_data(osd_op_t *op)
|
||||||
|
{
|
||||||
|
return (op->op_type == OSD_OP_IN
|
||||||
|
? (op->req.hdr.opcode == OSD_OP_READ ||
|
||||||
|
op->req.hdr.opcode == OSD_OP_SEC_READ ||
|
||||||
|
op->req.hdr.opcode == OSD_OP_SEC_LIST ||
|
||||||
|
op->req.hdr.opcode == OSD_OP_SHOW_CONFIG ||
|
||||||
|
op->req.hdr.opcode == OSD_OP_DESCRIBE)
|
||||||
|
: (op->req.hdr.opcode == OSD_OP_WRITE ||
|
||||||
|
op->req.hdr.opcode == OSD_OP_SEC_WRITE ||
|
||||||
|
op->req.hdr.opcode == OSD_OP_SEC_WRITE_STABLE ||
|
||||||
|
op->req.hdr.opcode == OSD_OP_SEC_STABILIZE ||
|
||||||
|
op->req.hdr.opcode == OSD_OP_SEC_ROLLBACK ||
|
||||||
|
op->req.hdr.opcode == OSD_OP_SHOW_CONFIG)) && op->iov.count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool op_has_data_for_ssl(osd_op_t *op)
|
||||||
|
{
|
||||||
|
return (op->op_type == OSD_OP_IN
|
||||||
|
? (op->req.hdr.opcode == OSD_OP_SEC_LIST ||
|
||||||
|
op->req.hdr.opcode == OSD_OP_SHOW_CONFIG ||
|
||||||
|
op->req.hdr.opcode == OSD_OP_DESCRIBE)
|
||||||
|
: (op->req.hdr.opcode == OSD_OP_SEC_STABILIZE ||
|
||||||
|
op->req.hdr.opcode == OSD_OP_SEC_ROLLBACK ||
|
||||||
|
op->req.hdr.opcode == OSD_OP_SHOW_CONFIG)) && op->iov.count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool op_has_data_for_nonssl(osd_op_t *op)
|
||||||
|
{
|
||||||
|
return (op->op_type == OSD_OP_IN
|
||||||
|
? (op->req.hdr.opcode == OSD_OP_READ ||
|
||||||
|
op->req.hdr.opcode == OSD_OP_SEC_READ)
|
||||||
|
: (op->req.hdr.opcode == OSD_OP_WRITE ||
|
||||||
|
op->req.hdr.opcode == OSD_OP_SEC_WRITE ||
|
||||||
|
op->req.hdr.opcode == OSD_OP_SEC_WRITE_STABLE)) && op->iov.count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool osd_messenger_t::ssl_op_write_buf(osd_client_t *cl, uint8_t *src, size_t src_len, bool skip_csum, size_t & from, size_t & done)
|
||||||
|
{
|
||||||
|
if (from < src_len)
|
||||||
|
{
|
||||||
|
size_t n = src_len-from;
|
||||||
|
int ok = SSL_write_ex(cl->ssl_cli, src+from, n, &n);
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
cl->ssl_want_write = true;
|
||||||
|
if (cl->write_csum_state && !skip_csum)
|
||||||
|
XXH3_64bits_update(cl->write_csum_state, src+from, n);
|
||||||
|
done += n;
|
||||||
|
cl->write_op_pos += n;
|
||||||
|
from += n;
|
||||||
|
if (from < src_len)
|
||||||
|
return false;
|
||||||
|
from = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int res = SSL_get_error(cl->ssl_cli, ok);
|
||||||
|
if (res == SSL_ERROR_WANT_WRITE || res == 0)
|
||||||
|
cl->ssl_want_write = true;
|
||||||
|
else if (res == SSL_ERROR_ZERO_RETURN)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Client %ju TLS disconnected\n", cl->client_id);
|
||||||
|
stop_client(cl->client_id);
|
||||||
|
}
|
||||||
|
else if (res != SSL_ERROR_WANT_READ)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Client %ju TLS write error: %s. Disconnecting client\n", cl->client_id, ERR_error_string(ERR_get_error(), NULL));
|
||||||
|
stop_client(cl->client_id);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
from -= src_len;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool osd_messenger_t::op_write_buf(osd_client_t *cl, uint8_t *src, size_t src_len, uint8_t *dst, size_t dst_len, bool skip_csum, size_t & from, size_t & done)
|
||||||
|
{
|
||||||
|
if (from < src_len)
|
||||||
|
{
|
||||||
|
size_t n = src_len-from;
|
||||||
|
if (n > dst_len-done)
|
||||||
|
n = dst_len-done;
|
||||||
|
if (cl->write_csum_state && !skip_csum)
|
||||||
|
XXH3_64bits_update(cl->write_csum_state, src+from, n);
|
||||||
|
memcpy(dst+done, src+from, n);
|
||||||
|
done += n;
|
||||||
|
cl->write_op_pos += n;
|
||||||
|
from += n;
|
||||||
|
if (from < src_len)
|
||||||
|
return false;
|
||||||
|
from = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
from -= src_len;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t osd_messenger_t::ssl_op_copy_to(osd_client_t *cl, uint8_t *dst, size_t dst_len)
|
||||||
|
{
|
||||||
|
size_t done = 0;
|
||||||
|
size_t from = cl->write_op_pos;
|
||||||
|
// Encrypt headers and data except read/write data
|
||||||
|
auto to_ssl = [&](uint8_t *src, size_t src_len, bool skip_csum)
|
||||||
|
{
|
||||||
|
return ssl_op_write_buf(cl, src, src_len, skip_csum, from, done);
|
||||||
|
};
|
||||||
|
int i = 0;
|
||||||
|
bool full_hdr = false;
|
||||||
|
bool full_op = false;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (!full_hdr)
|
||||||
|
full_hdr = op_write_headers(cl->write_op, to_ssl, cl->proto_csum_status != MSGR_CSUM_FULL);
|
||||||
|
if (full_hdr && op_has_data_for_ssl(cl->write_op))
|
||||||
|
{
|
||||||
|
for (; i < cl->write_op->iov.count; i++)
|
||||||
|
if (!ssl_op_write_buf(cl, (uint8_t*)cl->write_op->iov.buf[i].iov_base, cl->write_op->iov.buf[i].iov_len, false, from, done))
|
||||||
|
break;
|
||||||
|
full_op = true;
|
||||||
|
}
|
||||||
|
if (cl->peer_state == PEER_STOPPED)
|
||||||
|
return 0;
|
||||||
|
if (cl->ssl_want_write)
|
||||||
|
{
|
||||||
|
auto ssl_written = ssl_do_encrypt_to(cl, dst+done, dst_len-done);
|
||||||
|
if (!ssl_written)
|
||||||
|
return done;
|
||||||
|
done += ssl_written;
|
||||||
|
}
|
||||||
|
} while (!full_op);
|
||||||
|
// Non-TLS-encrypted operation data
|
||||||
|
if (op_has_data_for_nonssl(cl->write_op))
|
||||||
|
{
|
||||||
|
if (!op_copy_data_to(cl, dst, dst_len, from, done))
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
// TLS-encrypted checksum (uh oh...)
|
||||||
|
if (cl->proto_csum_status == MSGR_CSUM_FULL ||
|
||||||
|
cl->proto_csum_status == MSGR_CSUM_PAYLOAD && cl->write_op_pos > OSD_PACKET_SIZE)
|
||||||
|
{
|
||||||
|
if (!from)
|
||||||
|
cl->write_op->csum = XXH3_64bits_digest(cl->write_csum_state);
|
||||||
|
if (!ssl_op_write_buf(cl, (uint8_t*)&cl->write_op->csum, 8, true, from, done))
|
||||||
|
return done;
|
||||||
|
if (cl->ssl_want_write)
|
||||||
|
{
|
||||||
|
auto ssl_written = ssl_do_encrypt_to(cl, dst+done, dst_len-done);
|
||||||
|
if (!ssl_written)
|
||||||
|
return done;
|
||||||
|
done += ssl_written;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cl->write_op = NULL;
|
||||||
|
cl->write_op_pos = 0;
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t osd_messenger_t::op_copy_to(osd_client_t *cl, uint8_t *dst, size_t dst_len)
|
||||||
|
{
|
||||||
|
if (cl->ssl_cli)
|
||||||
|
{
|
||||||
|
return ssl_op_copy_to(cl, dst, dst_len);
|
||||||
|
}
|
||||||
|
size_t done = 0;
|
||||||
|
size_t from = cl->write_op_pos;
|
||||||
|
auto op_write_buf = [&](uint8_t *src, size_t src_len, bool skip_csum)
|
||||||
|
{
|
||||||
|
return this->op_write_buf(cl, src, src_len, dst, dst_len, skip_csum, from, done);
|
||||||
|
};
|
||||||
|
// Header
|
||||||
|
if (!op_write_headers(cl->write_op, op_write_buf, cl->proto_csum_status != MSGR_CSUM_FULL))
|
||||||
|
{
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
// Operation data
|
||||||
|
if (op_has_data(cl->write_op))
|
||||||
|
{
|
||||||
|
if (!op_copy_data_to(cl, dst, dst_len, from, done))
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
if (cl->proto_csum_status == MSGR_CSUM_FULL ||
|
||||||
|
cl->proto_csum_status == MSGR_CSUM_PAYLOAD && cl->write_op_pos > OSD_PACKET_SIZE)
|
||||||
|
{
|
||||||
|
if (!from)
|
||||||
|
cl->write_op->csum = XXH3_64bits_digest(cl->write_csum_state);
|
||||||
|
if (!op_write_buf((uint8_t*)&cl->write_op->csum, 8, true))
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
cl->write_op = NULL;
|
||||||
|
cl->write_op_pos = 0;
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool osd_messenger_t::op_copy_data_to(osd_client_t *cl, uint8_t *dst, size_t dst_len, size_t & from, size_t & done)
|
||||||
|
{
|
||||||
|
if (cl->write_op->enc)
|
||||||
|
{
|
||||||
|
if (!op_encrypted_copy_data_to(cl, dst, dst_len, from, done))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < cl->write_op->iov.count; i++)
|
||||||
|
{
|
||||||
|
auto & iov = cl->write_op->iov.buf[i];
|
||||||
|
if (!op_write_buf(cl, (uint8_t*)iov.iov_base, iov.iov_len, dst, dst_len, false, from, done))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void osd_messenger_t::op_get_write_buffers(osd_client_t *cl, std::vector<iovec> & lst)
|
||||||
|
{
|
||||||
|
size_t from = cl->write_op_pos;
|
||||||
|
auto op_write_buf = [&](uint8_t *src, size_t src_len, bool skip_csum)
|
||||||
|
{
|
||||||
|
if (lst.size() >= IOV_MAX)
|
||||||
|
return false;
|
||||||
|
if (from < src_len)
|
||||||
|
{
|
||||||
|
if (cl->write_csum_state && !skip_csum)
|
||||||
|
XXH3_64bits_update(cl->write_csum_state, src+from, src_len-from);
|
||||||
|
lst.push_back((iovec){ .iov_base = src+from, .iov_len = src_len-from });
|
||||||
|
cl->write_op_pos += src_len-from;
|
||||||
|
from = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
from -= src_len;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
// Header
|
||||||
|
if (!op_write_headers(cl->write_op, op_write_buf, cl->proto_csum_status != MSGR_CSUM_FULL))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Operation data
|
||||||
|
if (op_has_data(cl->write_op))
|
||||||
|
{
|
||||||
|
if (cl->write_op->enc)
|
||||||
|
{
|
||||||
|
if (lst.size() >= IOV_MAX)
|
||||||
|
return;
|
||||||
|
// No way except to allocate a temporary buffer and encrypt data to it
|
||||||
|
assert(cl->write_op->req.hdr.opcode == OSD_OP_WRITE);
|
||||||
|
size_t remsize = cl->write_op->req.rw.len - from + (from % 16);
|
||||||
|
assert(remsize > 0);
|
||||||
|
assert(!cl->write_op->enc_buf);
|
||||||
|
cl->write_op->enc_buf = (uint8_t*)malloc_or_die(remsize);
|
||||||
|
size_t done = 0;
|
||||||
|
bool end = op_encrypted_copy_data_to(cl, cl->write_op->enc_buf, remsize, from, done);
|
||||||
|
assert(end);
|
||||||
|
lst.push_back((iovec){ .iov_base = cl->write_op->enc_buf, .iov_len = remsize });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < cl->write_op->iov.count; i++)
|
||||||
|
{
|
||||||
|
if (!op_write_buf((uint8_t*)cl->write_op->iov.buf[i].iov_base, cl->write_op->iov.buf[i].iov_len, false))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cl->proto_csum_status == MSGR_CSUM_FULL ||
|
||||||
|
cl->proto_csum_status == MSGR_CSUM_PAYLOAD && cl->write_op_pos > OSD_PACKET_SIZE)
|
||||||
|
{
|
||||||
|
if (!from)
|
||||||
|
cl->write_op->csum = XXH3_64bits_digest(cl->write_csum_state);
|
||||||
|
if (!op_write_buf((uint8_t*)&cl->write_op->csum, 8, true))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cl->write_op = NULL;
|
||||||
|
cl->write_op_pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void osd_messenger_t::ssl_op_get_write_buffers(osd_client_t *cl, std::vector<iovec> & lst)
|
||||||
|
{
|
||||||
|
size_t done = 0;
|
||||||
|
size_t from = cl->write_op_pos;
|
||||||
|
auto op_write_buf = [&](uint8_t *src, size_t src_len, bool skip_csum)
|
||||||
|
{
|
||||||
|
if (lst.size() >= IOV_MAX)
|
||||||
|
return false;
|
||||||
|
if (from < src_len)
|
||||||
|
{
|
||||||
|
if (cl->write_csum_state && !skip_csum)
|
||||||
|
XXH3_64bits_update(cl->write_csum_state, src+from, src_len-from);
|
||||||
|
lst.push_back((iovec){ .iov_base = src+from, .iov_len = src_len-from });
|
||||||
|
cl->write_op_pos += src_len-from;
|
||||||
|
from = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
from -= src_len;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
// Encrypt headers and data except read/write data
|
||||||
|
auto to_ssl = [&](uint8_t *src, size_t src_len, bool skip_csum)
|
||||||
|
{
|
||||||
|
return ssl_op_write_buf(cl, src, src_len, skip_csum, from, done);
|
||||||
|
};
|
||||||
|
int i = 0;
|
||||||
|
bool full_hdr = false;
|
||||||
|
bool full_op = false;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (!full_hdr)
|
||||||
|
full_hdr = op_write_headers(cl->write_op, to_ssl, cl->proto_csum_status != MSGR_CSUM_FULL);
|
||||||
|
if (full_hdr && op_has_data_for_ssl(cl->write_op))
|
||||||
|
{
|
||||||
|
for (; i < cl->write_op->iov.count; i++)
|
||||||
|
if (!ssl_op_write_buf(cl, (uint8_t*)cl->write_op->iov.buf[i].iov_base, cl->write_op->iov.buf[i].iov_len, false, from, done))
|
||||||
|
break;
|
||||||
|
full_op = true;
|
||||||
|
}
|
||||||
|
if (cl->peer_state == PEER_STOPPED)
|
||||||
|
return;
|
||||||
|
if (!ssl_do_encrypt(cl))
|
||||||
|
return;
|
||||||
|
} while (!full_op);
|
||||||
|
// Non-TLS-encrypted operation data
|
||||||
|
if (op_has_data_for_nonssl(cl->write_op))
|
||||||
|
{
|
||||||
|
if (cl->write_op->enc)
|
||||||
|
{
|
||||||
|
if (lst.size() >= IOV_MAX)
|
||||||
|
return;
|
||||||
|
// No way except to allocate a temporary buffer and encrypt data to it
|
||||||
|
assert(cl->write_op->req.hdr.opcode == OSD_OP_WRITE);
|
||||||
|
size_t remsize = cl->write_op->req.rw.len - from + (from % 16);
|
||||||
|
assert(remsize > 0);
|
||||||
|
assert(!cl->write_op->enc_buf);
|
||||||
|
cl->write_op->enc_buf = (uint8_t*)malloc_or_die(remsize);
|
||||||
|
size_t done = 0;
|
||||||
|
bool end = op_encrypted_copy_data_to(cl, cl->write_op->enc_buf, remsize, from, done);
|
||||||
|
assert(end);
|
||||||
|
lst.push_back((iovec){ .iov_base = cl->write_op->enc_buf, .iov_len = remsize });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < cl->write_op->iov.count; i++)
|
||||||
|
{
|
||||||
|
if (!op_write_buf((uint8_t*)cl->write_op->iov.buf[i].iov_base, cl->write_op->iov.buf[i].iov_len, false))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TLS-encrypted checksum (uh oh...)
|
||||||
|
if (cl->proto_csum_status == MSGR_CSUM_FULL ||
|
||||||
|
cl->proto_csum_status == MSGR_CSUM_PAYLOAD && cl->write_op_pos > OSD_PACKET_SIZE)
|
||||||
|
{
|
||||||
|
if (!from)
|
||||||
|
cl->write_op->csum = XXH3_64bits_digest(cl->write_csum_state);
|
||||||
|
if (!ssl_op_write_buf(cl, (uint8_t*)&cl->write_op->csum, 8, true, from, done))
|
||||||
|
return;
|
||||||
|
if (!ssl_do_encrypt(cl))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cl->write_op = NULL;
|
||||||
|
cl->write_op_pos = 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,9 +5,16 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#include "messenger.h"
|
#include "messenger.h"
|
||||||
|
#include "../util/xxh_x86dispatch.h"
|
||||||
#ifdef WITH_RDMA
|
#ifdef WITH_RDMA
|
||||||
#include "msgr_rdma.h"
|
#include "msgr_rdma.h"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
#include <openssl/bio.h>
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#include <openssl/pem.h>
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
void osd_client_t::cancel_ops()
|
void osd_client_t::cancel_ops()
|
||||||
{
|
{
|
||||||
@@ -79,6 +86,22 @@ void osd_messenger_t::stop_client(uint64_t client_id, bool force_delete)
|
|||||||
fprintf(stderr, "[OSD %ju] Stopping client %ju (regular client)\n", osd_num, client_id);
|
fprintf(stderr, "[OSD %ju] Stopping client %ju (regular client)\n", osd_num, client_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (cl->encrypt_ctx)
|
||||||
|
{
|
||||||
|
if (encrypt_ctx_pool.size() > max_aes_xts_pool_size)
|
||||||
|
destroy_aes_xts_encrypt(cl->encrypt_ctx);
|
||||||
|
else
|
||||||
|
encrypt_ctx_pool.push_back(cl->encrypt_ctx);
|
||||||
|
cl->encrypt_ctx = NULL;
|
||||||
|
}
|
||||||
|
if (cl->decrypt_ctx)
|
||||||
|
{
|
||||||
|
if (decrypt_ctx_pool.size() > max_aes_xts_pool_size)
|
||||||
|
destroy_aes_xts_decrypt(cl->decrypt_ctx);
|
||||||
|
else
|
||||||
|
decrypt_ctx_pool.push_back(cl->decrypt_ctx);
|
||||||
|
cl->decrypt_ctx = NULL;
|
||||||
|
}
|
||||||
// First set state to STOPPED so another stop_client() call doesn't try to free it again
|
// First set state to STOPPED so another stop_client() call doesn't try to free it again
|
||||||
cl->refs++;
|
cl->refs++;
|
||||||
int prev_state = cl->peer_state;
|
int prev_state = cl->peer_state;
|
||||||
@@ -189,6 +212,13 @@ osd_client_t::~osd_client_t()
|
|||||||
}
|
}
|
||||||
// Cancel outbound ops
|
// Cancel outbound ops
|
||||||
cancel_ops();
|
cancel_ops();
|
||||||
|
for (osd_op_t *op: send_free_ops)
|
||||||
|
{
|
||||||
|
if (op)
|
||||||
|
{
|
||||||
|
delete op;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (osd_op_t *op: zc_free_list)
|
for (osd_op_t *op: zc_free_list)
|
||||||
{
|
{
|
||||||
if (op)
|
if (op)
|
||||||
@@ -204,5 +234,29 @@ osd_client_t::~osd_client_t()
|
|||||||
rdma_conn = NULL;
|
rdma_conn = NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
if (read_csum_state)
|
||||||
|
{
|
||||||
|
XXH3_freeState(read_csum_state);
|
||||||
|
read_csum_state = NULL;
|
||||||
|
}
|
||||||
|
if (write_csum_state)
|
||||||
|
{
|
||||||
|
XXH3_freeState(write_csum_state);
|
||||||
|
write_csum_state = NULL;
|
||||||
|
}
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
if (ssl_cli)
|
||||||
|
{
|
||||||
|
SSL_free(ssl_cli);
|
||||||
|
ssl_cli = NULL;
|
||||||
|
write_to_ssl = NULL;
|
||||||
|
read_from_ssl = NULL;
|
||||||
|
}
|
||||||
|
if (ssl_out_buf)
|
||||||
|
{
|
||||||
|
free(ssl_out_buf);
|
||||||
|
ssl_out_buf = NULL;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
|
|
||||||
#define OSD_OP_RECOVERY_RELATED (uint32_t)1
|
#define OSD_OP_RECOVERY_RELATED (uint32_t)1
|
||||||
#define OSD_OP_IGNORE_PG_LOCK (uint32_t)2
|
#define OSD_OP_IGNORE_PG_LOCK (uint32_t)2
|
||||||
|
#define OSD_OP_RETURN_CHAIN (uint32_t)4
|
||||||
|
|
||||||
// Memory alignment for direct I/O (usually 512 bytes)
|
// Memory alignment for direct I/O (usually 512 bytes)
|
||||||
#ifndef DIRECT_IO_ALIGNMENT
|
#ifndef DIRECT_IO_ALIGNMENT
|
||||||
@@ -228,9 +229,10 @@ struct __attribute__((__packed__)) osd_op_rw_t
|
|||||||
uint64_t offset;
|
uint64_t offset;
|
||||||
// length. 0 means to read all bitmaps of the specified range, but no data.
|
// length. 0 means to read all bitmaps of the specified range, but no data.
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
// flags (for future)
|
// flags
|
||||||
|
// OSD_OP_RETURN_CHAIN for chained reads: return parent number in chain for each block
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
// inode metadata revision
|
// inode metadata revision for chained reads
|
||||||
uint64_t meta_revision;
|
uint64_t meta_revision;
|
||||||
// object version for atomic "CAS" (compare-and-set) writes
|
// object version for atomic "CAS" (compare-and-set) writes
|
||||||
// writes and deletes fail with -EINTR if object version differs from (version-1)
|
// writes and deletes fail with -EINTR if object version differs from (version-1)
|
||||||
|
|||||||
+15
-1
@@ -2,11 +2,19 @@ cmake_minimum_required(VERSION 2.8...3.30)
|
|||||||
|
|
||||||
project(vitastor)
|
project(vitastor)
|
||||||
|
|
||||||
|
set(OPENAPI_JSON_H "${CMAKE_CURRENT_BINARY_DIR}/openapi.json.h")
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${OPENAPI_JSON_H}
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E echo const char* openapi_description = R\\\"json\\\( > ${OPENAPI_JSON_H}
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E cat ${CMAKE_CURRENT_SOURCE_DIR}/openapi.json >> ${OPENAPI_JSON_H}
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E echo "\\)json\\\"\\;" >> ${OPENAPI_JSON_H}
|
||||||
|
DEPENDS openapi.json
|
||||||
|
)
|
||||||
|
|
||||||
# libvitastor_cli.a
|
# libvitastor_cli.a
|
||||||
add_library(vitastor_cli STATIC
|
add_library(vitastor_cli STATIC
|
||||||
cli_common.cpp
|
cli_common.cpp
|
||||||
cli_alloc_osd.cpp
|
cli_alloc_osd.cpp
|
||||||
cli_status.cpp
|
|
||||||
cli_describe.cpp
|
cli_describe.cpp
|
||||||
cli_fix.cpp
|
cli_fix.cpp
|
||||||
cli_ls.cpp
|
cli_ls.cpp
|
||||||
@@ -14,6 +22,7 @@ add_library(vitastor_cli STATIC
|
|||||||
cli_dd.cpp
|
cli_dd.cpp
|
||||||
cli_modify.cpp
|
cli_modify.cpp
|
||||||
cli_modify_osd.cpp
|
cli_modify_osd.cpp
|
||||||
|
cli_modify_user.cpp
|
||||||
cli_osd_tree.cpp
|
cli_osd_tree.cpp
|
||||||
cli_pg_ls.cpp
|
cli_pg_ls.cpp
|
||||||
cli_flatten.cpp
|
cli_flatten.cpp
|
||||||
@@ -27,8 +36,13 @@ add_library(vitastor_cli STATIC
|
|||||||
cli_pool_ls.cpp
|
cli_pool_ls.cpp
|
||||||
cli_pool_modify.cpp
|
cli_pool_modify.cpp
|
||||||
cli_pool_rm.cpp
|
cli_pool_rm.cpp
|
||||||
|
cli_serve.cpp
|
||||||
|
cli_status.cpp
|
||||||
|
cli_user_ls.cpp
|
||||||
|
${OPENAPI_JSON_H}
|
||||||
)
|
)
|
||||||
target_compile_options(vitastor_cli PUBLIC -fPIC)
|
target_compile_options(vitastor_cli PUBLIC -fPIC)
|
||||||
|
target_include_directories(vitastor_cli PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
# vitastor-cli
|
# vitastor-cli
|
||||||
add_executable(vitastor-cli
|
add_executable(vitastor-cli
|
||||||
|
|||||||
+139
-45
@@ -37,24 +37,42 @@ static const char* help_text =
|
|||||||
" --sort FIELD Sort by specified field (name, size, used_size, <read|write|delete>_<iops|bps|lat|queue>)\n"
|
" --sort FIELD Sort by specified field (name, size, used_size, <read|write|delete>_<iops|bps|lat|queue>)\n"
|
||||||
" -r|--reverse Sort in descending order\n"
|
" -r|--reverse Sort in descending order\n"
|
||||||
" -n|--count N Only list first N items\n"
|
" -n|--count N Only list first N items\n"
|
||||||
|
" --ids ID1,ID2 Only list images with specified full IDs\n"
|
||||||
" --tree Show image snapshot/clone tree\n"
|
" --tree Show image snapshot/clone tree\n"
|
||||||
"\n"
|
"\n"
|
||||||
"vitastor-cli create -s|--size <size> [-p|--pool <id|name>] [--parent <parent_name>[@<snapshot>]] <name>\n"
|
"vitastor-cli create -s|--size SIZE [OPTIONS] <name>\n"
|
||||||
" Create an image. You may use K/M/G/T suffixes for <size>. If --parent is specified,\n"
|
" Create an image. Options:\n"
|
||||||
" a copy-on-write image clone is created. Parent must be a snapshot (readonly image).\n"
|
" -s|--size SIZE New image size in bytes or with a K/M/G/T unit suffix.\n"
|
||||||
" Pool must be specified if there is more than one pool.\n"
|
" -p|--pool POOL Specify pool for the new image (may be omitted if there is only 1 pool).\n"
|
||||||
|
" --parent PARENT Create a copy-on-write image clone based on PARENT (or PARENT@SNAPSHOT).\n"
|
||||||
|
" If parent is not a snapshot, it must be a read-only image.\n"
|
||||||
|
" --enc-key random Generate a new random AES-256-XTS encryption key for the new image.\n"
|
||||||
|
" --enc-key HEX Set a specified AES-256-XTS key (64 bytes in hex) for the new image.\n"
|
||||||
|
" --enc-key vault:ID Use an encryption key from an external Vault secret with specified ID.\n"
|
||||||
|
" --owner username Set owner (default is the current user from TLS certificate).\n"
|
||||||
|
" --owner_group name Set owner group name.\n"
|
||||||
|
" --reader_group rdr Set reader group name.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"vitastor-cli create --snapshot <snapshot> [-p|--pool <id|name>] <image>\n"
|
"vitastor-cli create --snapshot <snapshot> [OPTIONS] <image>\n"
|
||||||
"vitastor-cli snap-create [-p|--pool <id|name>] <image>@<snapshot>\n"
|
"vitastor-cli snap-create [OPTIONS] <image>@<snapshot>\n"
|
||||||
" Create a snapshot of image <name>. May be used live if only a single writer is active.\n"
|
" Create a snapshot of image <image>. May be used live if only a single writer is active.\n"
|
||||||
|
" Options:\n"
|
||||||
|
" -p|--pool POOL Move image to pool POOL, leaving the snapshot in the old pool.\n"
|
||||||
|
" --enc-key random Change image encryption key to a new random AES-256-XTS key.\n"
|
||||||
|
" --enc-key KEY Change image encryption key to a specified key, Vault key or to an empty key.\n"
|
||||||
|
" By default, the image retains its old key when taking a snapshot.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"vitastor-cli modify <name> [--rename <new-name>] [--resize <size>] [--readonly | --readwrite] [-f|--force] [--down-ok]\n"
|
"vitastor-cli modify <name> [--rename <new-name>] [--resize <size>] [--readonly | --readwrite] [-f|--force] [--down-ok]\n"
|
||||||
" Rename, resize image or change its readonly status. Images with children can't be made read-write.\n"
|
" Rename, resize image or change its readonly status. Images with children can't be made read-write.\n"
|
||||||
" If the new size is smaller than the old size, extra data will be purged.\n"
|
" If the new size is smaller than the old size, extra data will be purged.\n"
|
||||||
" You should resize file system in the image, if present, before shrinking it.\n"
|
" You should resize file system in the image, if present, before shrinking it.\n"
|
||||||
" --deleted 1|0 Set/clear 'deleted image' flag (set automatically during unfinished deletes).\n"
|
" --deleted 1|0 Set/clear 'deleted image' flag (set automatically during unfinished deletes).\n"
|
||||||
" -f|--force Proceed with shrinking or setting readwrite flag even if the image has children.\n"
|
" -f|--force Proceed with shrinking or setting readwrite flag even if the image has children.\n"
|
||||||
" --down-ok Proceed with shrinking even if some data will be left on unavailable OSDs.\n"
|
" --down-ok Proceed with shrinking even if some data will be left on unavailable OSDs.\n"
|
||||||
|
" --enc-key HEX Change image encryption key (allowed only with --force).\n"
|
||||||
|
" --owner username Change image owner.\n"
|
||||||
|
" --owner_group name Change image owner group name.\n"
|
||||||
|
" --reader_group rdr Change image reader group name.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"vitastor-cli dd [iimg=<image> | if=<file>] [oimg=<image> | of=<file>] [bs=1M]\n"
|
"vitastor-cli dd [iimg=<image> | if=<file>] [oimg=<image> | of=<file>] [bs=1M]\n"
|
||||||
" [count=N] [seek/oseek=N] [skip/iseek=M] [iodepth=N] [status=progress]\n"
|
" [count=N] [seek/oseek=N] [skip/iseek=M] [iodepth=N] [status=progress]\n"
|
||||||
@@ -193,6 +211,7 @@ static const char* help_text =
|
|||||||
" --used_for_app s3:<name> Mark pool as used for S3 location with name <name>\n"
|
" --used_for_app s3:<name> Mark pool as used for S3 location with name <name>\n"
|
||||||
" --pg_stripe_size <number> Increase object grouping stripe\n"
|
" --pg_stripe_size <number> Increase object grouping stripe\n"
|
||||||
" --max_osd_combinations 10000 Maximum number of random combinations for LP solver input\n"
|
" --max_osd_combinations 10000 Maximum number of random combinations for LP solver input\n"
|
||||||
|
" --creator_group <group> User group allowed to create images in this pool.\n"
|
||||||
" --wait Wait for the new pool to come online\n"
|
" --wait Wait for the new pool to come online\n"
|
||||||
" -f|--force Do not check that cluster has enough OSDs to create the pool\n"
|
" -f|--force Do not check that cluster has enough OSDs to create the pool\n"
|
||||||
" Examples:\n"
|
" Examples:\n"
|
||||||
@@ -204,7 +223,7 @@ static const char* help_text =
|
|||||||
" [-s|--pg_size <number>] [--pg_minsize <number>] [-n|--pg_count <count>]\n"
|
" [-s|--pg_size <number>] [--pg_minsize <number>] [-n|--pg_count <count>]\n"
|
||||||
" [--failure_domain <level>] [--root_node <node>] [--osd_tags <tags>] [--used_for_app <type>:<name>]\n"
|
" [--failure_domain <level>] [--root_node <node>] [--osd_tags <tags>] [--used_for_app <type>:<name>]\n"
|
||||||
" [--max_osd_combinations <number>] [--primary_affinity_tags <tags>] [--scrub_interval <time>]\n"
|
" [--max_osd_combinations <number>] [--primary_affinity_tags <tags>] [--scrub_interval <time>]\n"
|
||||||
" [--level_placement <rules>] [--raw_placement <rules>]\n"
|
" [--level_placement <rules>] [--raw_placement <rules>] [--creator_group <group>]\n"
|
||||||
" Non-modifiable parameters (changing them WILL lead to data loss):\n"
|
" Non-modifiable parameters (changing them WILL lead to data loss):\n"
|
||||||
" [--block_size <size>] [--bitmap_granularity <size>]\n"
|
" [--block_size <size>] [--bitmap_granularity <size>]\n"
|
||||||
" [--immediate_commit <all|small|none>] [--pg_stripe_size <size>]\n"
|
" [--immediate_commit <all|small|none>] [--pg_stripe_size <size>]\n"
|
||||||
@@ -217,7 +236,7 @@ static const char* help_text =
|
|||||||
"vitastor-cli rm-pool|pool-rm [--force] <id|name>\n"
|
"vitastor-cli rm-pool|pool-rm [--force] <id|name>\n"
|
||||||
" Remove a pool. Refuses to remove pools with images without --force.\n"
|
" Remove a pool. Refuses to remove pools with images without --force.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"vitastor-cli ls-pools|pool-ls|ls-pool|pools [-l] [--detail] [--sort FIELD] [-r] [-n N] [--stats] [<glob> ...]\n"
|
"vitastor-cli ls-pools|pool-ls|ls-pool|pools [-l] [--detail] [--sort FIELD] [-r] [-n N] [<glob> ...]\n"
|
||||||
" List pools (only matching <glob> patterns if passed).\n"
|
" List pools (only matching <glob> patterns if passed).\n"
|
||||||
" -l|--long Also report I/O statistics\n"
|
" -l|--long Also report I/O statistics\n"
|
||||||
" --detail Use list format (not table), show all details\n"
|
" --detail Use list format (not table), show all details\n"
|
||||||
@@ -225,6 +244,25 @@ static const char* help_text =
|
|||||||
" -r|--reverse Sort in descending order\n"
|
" -r|--reverse Sort in descending order\n"
|
||||||
" -n|--count N Only list first N items\n"
|
" -n|--count N Only list first N items\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
"vitastor-cli ls-users|user-ls|ls-user|list-users [<name> ...]\n"
|
||||||
|
" List users (only with specified names if passed).\n"
|
||||||
|
"\n"
|
||||||
|
"vitastor-cli modify-user --type <type> --groups group1,group2,... <username>\n"
|
||||||
|
" Create or update user permissions. User names match CN of their certificates.\n"
|
||||||
|
" --type TYPE Set user type: client, admin, mon or osd. Default is client.\n"
|
||||||
|
" --groups GROUPS Set user's groups.\n"
|
||||||
|
"\n"
|
||||||
|
"vitastor-cli rm-user|remove-user|delete-user <username>\n"
|
||||||
|
" Remove a user.\n"
|
||||||
|
"\n"
|
||||||
|
"vitastor-cli serve\n"
|
||||||
|
" Start HTTP server able to handle CLI commands over a REST API. Options:\n"
|
||||||
|
" --bind_address ADDR Specify server IP address or addresses, separated by space. Default is 127.0.0.1.\n"
|
||||||
|
" --port 8080 Specify server port.\n"
|
||||||
|
" --ssl_cert FILE Path to server SSL certificate file (PEM format).\n"
|
||||||
|
" --ssl_key FILE Path to server SSL private key file.\n"
|
||||||
|
" --ssl_ca FILE Path to file with SSL CA certificates used to validate client connections.\n"
|
||||||
|
"\n"
|
||||||
"Use vitastor-cli --help <command> for command details or vitastor-cli --help --all for all details.\n"
|
"Use vitastor-cli --help <command> for command details or vitastor-cli --help --all for all details.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"GLOBAL OPTIONS:\n"
|
"GLOBAL OPTIONS:\n"
|
||||||
@@ -319,27 +357,24 @@ static json11::Json::object parse_args(int narg, const char *args[])
|
|||||||
return cfg;
|
return cfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int run(cli_tool_t *p, json11::Json::object cfg)
|
std::function<bool(cli_result_t &)> cli_tool_t::start(json11::Json::object cfg, cli_result_t & result)
|
||||||
{
|
{
|
||||||
cli_result_t result = {};
|
|
||||||
p->is_command_line = true;
|
|
||||||
p->parse_config(cfg);
|
|
||||||
json11::Json::array cmd = cfg["command"].array_items();
|
json11::Json::array cmd = cfg["command"].array_items();
|
||||||
cfg.erase("command");
|
cfg.erase("command");
|
||||||
std::function<bool(cli_result_t &)> action_cb;
|
std::function<bool(cli_result_t &)> action_cb;
|
||||||
if (!cmd.size())
|
if (!cmd.size())
|
||||||
{
|
{
|
||||||
result = { .err = EINVAL, .text = "command is missing" };
|
result = { .err = EOPNOTSUPP, .text = "command is missing" };
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "status")
|
else if (cmd[0] == "status")
|
||||||
{
|
{
|
||||||
// Show cluster status
|
// Show cluster status
|
||||||
action_cb = p->start_status(cfg);
|
action_cb = start_status(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "df")
|
else if (cmd[0] == "df")
|
||||||
{
|
{
|
||||||
// Show pool space stats
|
// Show pool space stats
|
||||||
action_cb = p->start_pool_ls(cfg);
|
action_cb = start_pool_ls(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "ls")
|
else if (cmd[0] == "ls")
|
||||||
{
|
{
|
||||||
@@ -349,7 +384,7 @@ static int run(cli_tool_t *p, json11::Json::object cfg)
|
|||||||
cmd.erase(cmd.begin(), cmd.begin()+1);
|
cmd.erase(cmd.begin(), cmd.begin()+1);
|
||||||
cfg["names"] = cmd;
|
cfg["names"] = cmd;
|
||||||
}
|
}
|
||||||
action_cb = p->start_ls(cfg);
|
action_cb = start_ls(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "snap-create")
|
else if (cmd[0] == "snap-create")
|
||||||
{
|
{
|
||||||
@@ -364,7 +399,7 @@ static int run(cli_tool_t *p, json11::Json::object cfg)
|
|||||||
{
|
{
|
||||||
cfg["image"] = name.substr(0, pos);
|
cfg["image"] = name.substr(0, pos);
|
||||||
cfg["snapshot"] = name.substr(pos + 1);
|
cfg["snapshot"] = name.substr(pos + 1);
|
||||||
action_cb = p->start_create(cfg);
|
action_cb = start_create(cfg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "create")
|
else if (cmd[0] == "create")
|
||||||
@@ -374,7 +409,7 @@ static int run(cli_tool_t *p, json11::Json::object cfg)
|
|||||||
{
|
{
|
||||||
cfg["image"] = cmd[1];
|
cfg["image"] = cmd[1];
|
||||||
}
|
}
|
||||||
action_cb = p->start_create(cfg);
|
action_cb = start_create(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "modify")
|
else if (cmd[0] == "modify")
|
||||||
{
|
{
|
||||||
@@ -383,12 +418,12 @@ static int run(cli_tool_t *p, json11::Json::object cfg)
|
|||||||
{
|
{
|
||||||
cfg["image"] = cmd[1];
|
cfg["image"] = cmd[1];
|
||||||
}
|
}
|
||||||
action_cb = p->start_modify(cfg);
|
action_cb = start_modify(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "rm-data")
|
else if (cmd[0] == "rm-data")
|
||||||
{
|
{
|
||||||
// Delete inode data
|
// Delete inode data
|
||||||
action_cb = p->start_rm_data(cfg);
|
action_cb = start_rm_data(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "rm-osd")
|
else if (cmd[0] == "rm-osd")
|
||||||
{
|
{
|
||||||
@@ -398,7 +433,7 @@ static int run(cli_tool_t *p, json11::Json::object cfg)
|
|||||||
cmd.erase(cmd.begin(), cmd.begin()+1);
|
cmd.erase(cmd.begin(), cmd.begin()+1);
|
||||||
cfg["osd_id"] = cmd;
|
cfg["osd_id"] = cmd;
|
||||||
}
|
}
|
||||||
action_cb = p->start_rm_osd(cfg);
|
action_cb = start_rm_osd(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "merge-data")
|
else if (cmd[0] == "merge-data")
|
||||||
{
|
{
|
||||||
@@ -409,7 +444,7 @@ static int run(cli_tool_t *p, json11::Json::object cfg)
|
|||||||
if (cmd.size() > 2)
|
if (cmd.size() > 2)
|
||||||
cfg["to"] = cmd[2];
|
cfg["to"] = cmd[2];
|
||||||
}
|
}
|
||||||
action_cb = p->start_merge(cfg);
|
action_cb = start_merge(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "flatten")
|
else if (cmd[0] == "flatten")
|
||||||
{
|
{
|
||||||
@@ -418,7 +453,7 @@ static int run(cli_tool_t *p, json11::Json::object cfg)
|
|||||||
{
|
{
|
||||||
cfg["image"] = cmd[1];
|
cfg["image"] = cmd[1];
|
||||||
}
|
}
|
||||||
action_cb = p->start_flatten(cfg);
|
action_cb = start_flatten(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "dd")
|
else if (cmd[0] == "dd")
|
||||||
{
|
{
|
||||||
@@ -432,16 +467,31 @@ static int run(cli_tool_t *p, json11::Json::object cfg)
|
|||||||
cfg[arg.substr(0, p)] = arg.substr(p+1);
|
cfg[arg.substr(0, p)] = arg.substr(p+1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
action_cb = p->start_dd(cfg);
|
action_cb = start_dd(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "rm")
|
else if (cmd[0] == "rm")
|
||||||
{
|
{
|
||||||
// Remove multiple snapshots and rebase their children
|
// Remove multiple snapshots and rebase their children
|
||||||
if (cfg["exact"].bool_value() || cfg["matching"].bool_value())
|
if (cfg["names"].is_array())
|
||||||
|
{
|
||||||
|
cfg["globs"] = cfg["names"];
|
||||||
|
cfg.erase("names");
|
||||||
|
cfg["exact"] = true;
|
||||||
|
cfg["matching"] = false;
|
||||||
|
action_cb = start_rm_wildcard(cfg);
|
||||||
|
}
|
||||||
|
else if (cfg["matching"].is_array())
|
||||||
|
{
|
||||||
|
cfg["globs"] = cfg["matching"];
|
||||||
|
cfg["exact"] = false;
|
||||||
|
cfg["matching"] = true;
|
||||||
|
action_cb = start_rm_wildcard(cfg);
|
||||||
|
}
|
||||||
|
else if (cfg["exact"].bool_value() || cfg["matching"].bool_value())
|
||||||
{
|
{
|
||||||
cmd.erase(cmd.begin(), cmd.begin()+1);
|
cmd.erase(cmd.begin(), cmd.begin()+1);
|
||||||
cfg["globs"] = cmd;
|
cfg["globs"] = cmd;
|
||||||
action_cb = p->start_rm_wildcard(cfg);
|
action_cb = start_rm_wildcard(cfg);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -451,41 +501,41 @@ static int run(cli_tool_t *p, json11::Json::object cfg)
|
|||||||
if (cmd.size() > 2)
|
if (cmd.size() > 2)
|
||||||
cfg["to"] = cmd[2];
|
cfg["to"] = cmd[2];
|
||||||
}
|
}
|
||||||
action_cb = p->start_rm(cfg);
|
action_cb = start_rm(cfg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "describe")
|
else if (cmd[0] == "describe")
|
||||||
{
|
{
|
||||||
// Describe unclean objects
|
// Describe unclean objects
|
||||||
action_cb = p->start_describe(cfg);
|
action_cb = start_describe(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "fix")
|
else if (cmd[0] == "fix")
|
||||||
{
|
{
|
||||||
// Fix inconsistent objects (by deleting some copies)
|
// Fix inconsistent objects (by deleting some copies)
|
||||||
action_cb = p->start_fix(cfg);
|
action_cb = start_fix(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "alloc-osd")
|
else if (cmd[0] == "alloc-osd")
|
||||||
{
|
{
|
||||||
// Allocate a new OSD number
|
// Allocate a new OSD number
|
||||||
action_cb = p->start_alloc_osd(cfg);
|
action_cb = start_alloc_osd(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "osd-tree")
|
else if (cmd[0] == "osd-tree")
|
||||||
{
|
{
|
||||||
// Print OSD tree
|
// Print OSD tree
|
||||||
action_cb = p->start_osd_tree(cfg);
|
cfg["as_tree"] = true;
|
||||||
|
action_cb = start_osd_tree(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "osds" || cmd[0] == "ls-osds" || cmd[0] == "ls-osd" || cmd[0] == "osd-ls")
|
else if (cmd[0] == "osds" || cmd[0] == "ls-osds" || cmd[0] == "ls-osd" || cmd[0] == "osd-ls")
|
||||||
{
|
{
|
||||||
// Print OSD list
|
// Print OSD list
|
||||||
cfg["flat"] = true;
|
action_cb = start_osd_tree(cfg);
|
||||||
action_cb = p->start_osd_tree(cfg);
|
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "modify-osd")
|
else if (cmd[0] == "modify-osd")
|
||||||
{
|
{
|
||||||
// Modify OSD configuration
|
// Modify OSD configuration
|
||||||
if (cmd.size() > 1)
|
if (cmd.size() > 1)
|
||||||
cfg["osd_num"] = cmd[1];
|
cfg["osd_num"] = cmd[1];
|
||||||
action_cb = p->start_modify_osd(cfg);
|
action_cb = start_modify_osd(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "pg-list" || cmd[0] == "pg-ls" || cmd[0] == "list-pg" || cmd[0] == "ls-pg" || cmd[0] == "ls-pgs" || cmd[0] == "pgs")
|
else if (cmd[0] == "pg-list" || cmd[0] == "pg-ls" || cmd[0] == "list-pg" || cmd[0] == "ls-pg" || cmd[0] == "ls-pgs" || cmd[0] == "pgs")
|
||||||
{
|
{
|
||||||
@@ -495,7 +545,7 @@ static int run(cli_tool_t *p, json11::Json::object cfg)
|
|||||||
cmd.erase(cmd.begin(), cmd.begin()+1);
|
cmd.erase(cmd.begin(), cmd.begin()+1);
|
||||||
cfg["pg_state"] = cmd;
|
cfg["pg_state"] = cmd;
|
||||||
}
|
}
|
||||||
action_cb = p->start_pg_list(cfg);
|
action_cb = start_pg_list(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "create-pool" || cmd[0] == "pool-create")
|
else if (cmd[0] == "create-pool" || cmd[0] == "pool-create")
|
||||||
{
|
{
|
||||||
@@ -504,16 +554,16 @@ static int run(cli_tool_t *p, json11::Json::object cfg)
|
|||||||
{
|
{
|
||||||
cfg["name"] = cmd[1];
|
cfg["name"] = cmd[1];
|
||||||
}
|
}
|
||||||
action_cb = p->start_pool_create(cfg);
|
action_cb = start_pool_create(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "modify-pool" || cmd[0] == "pool-modify")
|
else if (cmd[0] == "modify-pool" || cmd[0] == "pool-modify")
|
||||||
{
|
{
|
||||||
// Modify existing pool
|
// Modify existing pool
|
||||||
if (cmd.size() > 1)
|
if (cmd.size() > 1)
|
||||||
{
|
{
|
||||||
cfg["old_name"] = cmd[1];
|
cfg["pool"] = cmd[1];
|
||||||
}
|
}
|
||||||
action_cb = p->start_pool_modify(cfg);
|
action_cb = start_pool_modify(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "rm-pool" || cmd[0] == "pool-rm")
|
else if (cmd[0] == "rm-pool" || cmd[0] == "pool-rm")
|
||||||
{
|
{
|
||||||
@@ -522,7 +572,7 @@ static int run(cli_tool_t *p, json11::Json::object cfg)
|
|||||||
{
|
{
|
||||||
cfg["pool"] = cmd[1];
|
cfg["pool"] = cmd[1];
|
||||||
}
|
}
|
||||||
action_cb = p->start_pool_rm(cfg);
|
action_cb = start_pool_rm(cfg);
|
||||||
}
|
}
|
||||||
else if (cmd[0] == "ls-pool" || cmd[0] == "pool-ls" || cmd[0] == "ls-pools" || cmd[0] == "pools")
|
else if (cmd[0] == "ls-pool" || cmd[0] == "pool-ls" || cmd[0] == "ls-pools" || cmd[0] == "pools")
|
||||||
{
|
{
|
||||||
@@ -533,12 +583,55 @@ static int run(cli_tool_t *p, json11::Json::object cfg)
|
|||||||
cmd.erase(cmd.begin(), cmd.begin()+1);
|
cmd.erase(cmd.begin(), cmd.begin()+1);
|
||||||
cfg["names"] = cmd;
|
cfg["names"] = cmd;
|
||||||
}
|
}
|
||||||
action_cb = p->start_pool_ls(cfg);
|
action_cb = start_pool_ls(cfg);
|
||||||
|
}
|
||||||
|
else if (cmd[0] == "user-ls" || cmd[0] == "ls-user" || cmd[0] == "ls-users" || cmd[0] == "list-users")
|
||||||
|
{
|
||||||
|
// List users
|
||||||
|
if (cmd.size() > 1)
|
||||||
|
{
|
||||||
|
cmd.erase(cmd.begin(), cmd.begin()+1);
|
||||||
|
cfg["names"] = cmd;
|
||||||
|
}
|
||||||
|
action_cb = start_user_ls(cfg);
|
||||||
|
}
|
||||||
|
else if (cmd[0] == "modify-user" || cmd[0] == "user-modify")
|
||||||
|
{
|
||||||
|
// Create/update user
|
||||||
|
if (cmd.size() > 1)
|
||||||
|
{
|
||||||
|
cfg["name"] = cmd[1];
|
||||||
|
}
|
||||||
|
action_cb = start_modify_user(cfg);
|
||||||
|
}
|
||||||
|
else if (cmd[0] == "rm-user" || cmd[0] == "remove-user" || cmd[0] == "delete-user")
|
||||||
|
{
|
||||||
|
// Remove user
|
||||||
|
if (cmd.size() > 1)
|
||||||
|
{
|
||||||
|
cfg["name"] = cmd[1];
|
||||||
|
}
|
||||||
|
cfg["remove"] = true;
|
||||||
|
action_cb = start_modify_user(cfg);
|
||||||
|
}
|
||||||
|
else if (cmd[0] == "serve")
|
||||||
|
{
|
||||||
|
// Start HTTP server
|
||||||
|
action_cb = start_serve(cfg);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = { .err = EINVAL, .text = "unknown command: "+cmd[0].string_value() };
|
result = { .err = EOPNOTSUPP, .text = "unknown command: "+cmd[0].string_value() };
|
||||||
}
|
}
|
||||||
|
return action_cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int run(cli_tool_t *p, json11::Json::object cfg)
|
||||||
|
{
|
||||||
|
cli_result_t result = {};
|
||||||
|
p->is_command_line = true;
|
||||||
|
p->parse_config(cfg);
|
||||||
|
auto action_cb = p->start(cfg, result);
|
||||||
if (action_cb != NULL)
|
if (action_cb != NULL)
|
||||||
{
|
{
|
||||||
// Create client
|
// Create client
|
||||||
@@ -550,6 +643,7 @@ static int run(cli_tool_t *p, json11::Json::object cfg)
|
|||||||
{
|
{
|
||||||
result = r;
|
result = r;
|
||||||
action_cb = NULL;
|
action_cb = NULL;
|
||||||
|
p->ringloop->submit();
|
||||||
});
|
});
|
||||||
// Loop until it completes
|
// Loop until it completes
|
||||||
while (action_cb != NULL)
|
while (action_cb != NULL)
|
||||||
|
|||||||
+19
-1
@@ -9,6 +9,7 @@
|
|||||||
#include "object_id.h"
|
#include "object_id.h"
|
||||||
#include "ringloop.h"
|
#include "ringloop.h"
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
struct rm_inode_t;
|
struct rm_inode_t;
|
||||||
struct snap_merger_t;
|
struct snap_merger_t;
|
||||||
@@ -26,6 +27,13 @@ struct cli_result_t
|
|||||||
json11::Json data;
|
json11::Json data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct cli_user_t
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
std::string type;
|
||||||
|
std::set<std::string> groups;
|
||||||
|
};
|
||||||
|
|
||||||
class cli_tool_t
|
class cli_tool_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -37,6 +45,8 @@ public:
|
|||||||
bool is_command_line = false;
|
bool is_command_line = false;
|
||||||
bool color = false;
|
bool color = false;
|
||||||
|
|
||||||
|
std::unique_ptr<cli_user_t> user; // for http mode
|
||||||
|
|
||||||
ring_loop_t *ringloop = NULL;
|
ring_loop_t *ringloop = NULL;
|
||||||
epoll_manager_t *epmgr = NULL;
|
epoll_manager_t *epmgr = NULL;
|
||||||
cluster_client_t *cli = NULL;
|
cluster_client_t *cli = NULL;
|
||||||
@@ -46,18 +56,24 @@ public:
|
|||||||
json11::Json etcd_result;
|
json11::Json etcd_result;
|
||||||
|
|
||||||
void parse_config(json11::Json::object & cfg);
|
void parse_config(json11::Json::object & cfg);
|
||||||
|
void parse_api_opts(json11::Json::object & cfg);
|
||||||
json11::Json parse_tags(std::string tags);
|
json11::Json parse_tags(std::string tags);
|
||||||
|
|
||||||
|
json11::Json::object format_image(const inode_config_t & cfg);
|
||||||
void change_parent(inode_t cur, inode_t new_parent, cli_result_t *result);
|
void change_parent(inode_t cur, inode_t new_parent, cli_result_t *result);
|
||||||
inode_config_t* get_inode_cfg(const std::string & name);
|
inode_config_t* get_inode_cfg(const std::string & name);
|
||||||
|
|
||||||
|
bool check_image_perm(const inode_config_t & cfg, bool write);
|
||||||
|
|
||||||
friend struct rm_inode_t;
|
friend struct rm_inode_t;
|
||||||
friend struct snap_merger_t;
|
friend struct snap_merger_t;
|
||||||
friend struct snap_flattener_t;
|
friend struct snap_flattener_t;
|
||||||
friend struct snap_remover_t;
|
friend struct snap_remover_t;
|
||||||
|
|
||||||
|
std::function<bool(cli_result_t &)> start(json11::Json::object cfg, cli_result_t & result);
|
||||||
std::function<bool(cli_result_t &)> start_alloc_osd(json11::Json);
|
std::function<bool(cli_result_t &)> start_alloc_osd(json11::Json);
|
||||||
std::function<bool(cli_result_t &)> start_create(json11::Json);
|
std::function<bool(cli_result_t &)> start_create(json11::Json);
|
||||||
|
std::function<bool(cli_result_t &)> start_dd(json11::Json);
|
||||||
std::function<bool(cli_result_t &)> start_describe(json11::Json);
|
std::function<bool(cli_result_t &)> start_describe(json11::Json);
|
||||||
std::function<bool(cli_result_t &)> start_fix(json11::Json);
|
std::function<bool(cli_result_t &)> start_fix(json11::Json);
|
||||||
std::function<bool(cli_result_t &)> start_flatten(json11::Json);
|
std::function<bool(cli_result_t &)> start_flatten(json11::Json);
|
||||||
@@ -65,6 +81,7 @@ public:
|
|||||||
std::function<bool(cli_result_t &)> start_merge(json11::Json);
|
std::function<bool(cli_result_t &)> start_merge(json11::Json);
|
||||||
std::function<bool(cli_result_t &)> start_modify(json11::Json);
|
std::function<bool(cli_result_t &)> start_modify(json11::Json);
|
||||||
std::function<bool(cli_result_t &)> start_modify_osd(json11::Json);
|
std::function<bool(cli_result_t &)> start_modify_osd(json11::Json);
|
||||||
|
std::function<bool(cli_result_t &)> start_modify_user(json11::Json);
|
||||||
std::function<bool(cli_result_t &)> start_osd_tree(json11::Json);
|
std::function<bool(cli_result_t &)> start_osd_tree(json11::Json);
|
||||||
std::function<bool(cli_result_t &)> start_pg_list(json11::Json);
|
std::function<bool(cli_result_t &)> start_pg_list(json11::Json);
|
||||||
std::function<bool(cli_result_t &)> start_pool_create(json11::Json);
|
std::function<bool(cli_result_t &)> start_pool_create(json11::Json);
|
||||||
@@ -75,8 +92,9 @@ public:
|
|||||||
std::function<bool(cli_result_t &)> start_rm_data(json11::Json);
|
std::function<bool(cli_result_t &)> start_rm_data(json11::Json);
|
||||||
std::function<bool(cli_result_t &)> start_rm_osd(json11::Json);
|
std::function<bool(cli_result_t &)> start_rm_osd(json11::Json);
|
||||||
std::function<bool(cli_result_t &)> start_rm_wildcard(json11::Json);
|
std::function<bool(cli_result_t &)> start_rm_wildcard(json11::Json);
|
||||||
|
std::function<bool(cli_result_t &)> start_serve(json11::Json);
|
||||||
std::function<bool(cli_result_t &)> start_status(json11::Json);
|
std::function<bool(cli_result_t &)> start_status(json11::Json);
|
||||||
std::function<bool(cli_result_t &)> start_dd(json11::Json);
|
std::function<bool(cli_result_t &)> start_user_ls(json11::Json);
|
||||||
|
|
||||||
// Should be called like loop_and_wait(start_status(), <completion callback>)
|
// Should be called like loop_and_wait(start_status(), <completion callback>)
|
||||||
void loop_and_wait(std::function<bool(cli_result_t &)> loop_cb, std::function<void(const cli_result_t &)> complete_cb);
|
void loop_and_wait(std::function<bool(cli_result_t &)> loop_cb, std::function<void(const cli_result_t &)> complete_cb);
|
||||||
|
|||||||
+67
-7
@@ -6,6 +6,62 @@
|
|||||||
#include "cluster_client.h"
|
#include "cluster_client.h"
|
||||||
#include "cli.h"
|
#include "cli.h"
|
||||||
|
|
||||||
|
bool cli_tool_t::check_image_perm(const inode_config_t & cfg, bool write)
|
||||||
|
{
|
||||||
|
return !user ||
|
||||||
|
user->type == "admin" ||
|
||||||
|
user->name == cfg.owner ||
|
||||||
|
cfg.owner_group != "" && user->groups.find(cfg.owner_group) != user->groups.end() ||
|
||||||
|
!write && cfg.reader_group != "" && user->groups.find(cfg.reader_group) != user->groups.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
json11::Json::object cli_tool_t::format_image(const inode_config_t & cfg)
|
||||||
|
{
|
||||||
|
auto pool_it = cli->st_cli.pool_config.find(INODE_POOL(cfg.num));
|
||||||
|
bool good_pool = pool_it != cli->st_cli.pool_config.end();
|
||||||
|
auto img = json11::Json::object {
|
||||||
|
{ "name", cfg.name },
|
||||||
|
{ "size", cfg.size },
|
||||||
|
{ "inode_id", cfg.num },
|
||||||
|
{ "inode_num", INODE_NO_POOL(cfg.num) },
|
||||||
|
{ "pool_id", (uint64_t)INODE_POOL(cfg.num) },
|
||||||
|
{ "pool_name", good_pool ? pool_it->second.name : "? (ID:"+std::to_string(INODE_POOL(cfg.num))+")" },
|
||||||
|
{ "readonly", cfg.readonly },
|
||||||
|
{ "deleted", cfg.deleted },
|
||||||
|
};
|
||||||
|
if (cfg.owner != "")
|
||||||
|
{
|
||||||
|
img["owner"] = cfg.owner;
|
||||||
|
}
|
||||||
|
if (cfg.owner_group != "")
|
||||||
|
{
|
||||||
|
img["owner_group"] = cfg.owner_group;
|
||||||
|
}
|
||||||
|
if (cfg.reader_group != "")
|
||||||
|
{
|
||||||
|
img["reader_group"] = cfg.reader_group;
|
||||||
|
}
|
||||||
|
if (!cfg.enc_key.empty())
|
||||||
|
{
|
||||||
|
img["encrypted"] = true;
|
||||||
|
// Only show Vault key IDs
|
||||||
|
if (cfg.enc_key.substr(0, strlen(VAULT_KEY_PREFIX)) == VAULT_KEY_PREFIX)
|
||||||
|
img["enc_key_id"] = cfg.enc_key;
|
||||||
|
}
|
||||||
|
if (cfg.parent_id)
|
||||||
|
{
|
||||||
|
auto parent_it = cli->st_cli.inode_config.find(cfg.parent_id);
|
||||||
|
if (parent_it != cli->st_cli.inode_config.end())
|
||||||
|
{
|
||||||
|
img["parent_name"] = parent_it->second.name;
|
||||||
|
}
|
||||||
|
img["parent_inode_id"] = cfg.parent_id;
|
||||||
|
img["parent_inode_num"] = INODE_NO_POOL(cfg.parent_id);
|
||||||
|
img["parent_pool_id"] = (uint64_t)INODE_POOL(cfg.parent_id);
|
||||||
|
}
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
void cli_tool_t::change_parent(inode_t cur, inode_t new_parent, cli_result_t *result)
|
void cli_tool_t::change_parent(inode_t cur, inode_t new_parent, cli_result_t *result)
|
||||||
{
|
{
|
||||||
auto cur_cfg_it = cli->st_cli.inode_config.find(cur);
|
auto cur_cfg_it = cli->st_cli.inode_config.find(cur);
|
||||||
@@ -101,6 +157,16 @@ inode_config_t* cli_tool_t::get_inode_cfg(const std::string & name)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cli_tool_t::parse_api_opts(json11::Json::object & cfg)
|
||||||
|
{
|
||||||
|
iodepth = cfg["iodepth"].uint64_value();
|
||||||
|
if (!iodepth)
|
||||||
|
iodepth = 32;
|
||||||
|
parallel_osds = cfg["parallel_osds"].uint64_value();
|
||||||
|
if (!parallel_osds)
|
||||||
|
parallel_osds = 4;
|
||||||
|
}
|
||||||
|
|
||||||
void cli_tool_t::parse_config(json11::Json::object & cfg)
|
void cli_tool_t::parse_config(json11::Json::object & cfg)
|
||||||
{
|
{
|
||||||
for (auto kv_it = cfg.begin(); kv_it != cfg.end();)
|
for (auto kv_it = cfg.begin(); kv_it != cfg.end();)
|
||||||
@@ -121,15 +187,10 @@ void cli_tool_t::parse_config(json11::Json::object & cfg)
|
|||||||
else
|
else
|
||||||
color = isatty(1);
|
color = isatty(1);
|
||||||
json_output = cfg["json"].bool_value();
|
json_output = cfg["json"].bool_value();
|
||||||
iodepth = cfg["iodepth"].uint64_value();
|
|
||||||
if (!iodepth)
|
|
||||||
iodepth = 32;
|
|
||||||
parallel_osds = cfg["parallel_osds"].uint64_value();
|
|
||||||
if (!parallel_osds)
|
|
||||||
parallel_osds = 4;
|
|
||||||
log_level = cfg["log_level"].int64_value();
|
log_level = cfg["log_level"].int64_value();
|
||||||
progress = cfg["progress"].uint64_value() ? true : false;
|
progress = cfg["progress"].uint64_value() ? true : false;
|
||||||
list_first = cfg["wait_list"].uint64_value() ? true : false;
|
list_first = cfg["wait_list"].uint64_value() ? true : false;
|
||||||
|
parse_api_opts(cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct cli_result_looper_t
|
struct cli_result_looper_t
|
||||||
@@ -153,7 +214,6 @@ void cli_tool_t::loop_and_wait(std::function<bool(cli_result_t &)> loop_cb, std:
|
|||||||
ringloop->unregister_consumer(&looper->consumer);
|
ringloop->unregister_consumer(&looper->consumer);
|
||||||
looper->loop_cb = NULL;
|
looper->loop_cb = NULL;
|
||||||
looper->complete_cb(looper->result);
|
looper->complete_cb(looper->result);
|
||||||
ringloop->submit();
|
|
||||||
delete looper;
|
delete looper;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
+132
-44
@@ -1,8 +1,13 @@
|
|||||||
// Copyright (c) Vitaliy Filippov, 2019+
|
// Copyright (c) Vitaliy Filippov, 2019+
|
||||||
// License: VNPL-1.1 (see README.md for details)
|
// License: VNPL-1.1 (see README.md for details)
|
||||||
|
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include "cli.h"
|
#include "cli.h"
|
||||||
|
#include "http_client.h"
|
||||||
#include "cluster_client.h"
|
#include "cluster_client.h"
|
||||||
#include "str_util.h"
|
#include "str_util.h"
|
||||||
|
|
||||||
@@ -29,11 +34,15 @@ struct image_creator_t
|
|||||||
uint64_t size = 0;
|
uint64_t size = 0;
|
||||||
bool force = false;
|
bool force = false;
|
||||||
bool force_size = false;
|
bool force_size = false;
|
||||||
|
std::string enc_key;
|
||||||
|
bool set_key = false;
|
||||||
|
std::string new_owner, new_owner_group, new_reader_group;
|
||||||
|
|
||||||
pool_id_t old_pool_id = 0;
|
pool_id_t old_pool_id = 0;
|
||||||
inode_t new_parent_id = 0;
|
inode_t new_parent_id = 0;
|
||||||
inode_t new_id = 0, old_id = 0;
|
inode_t new_id = 0, old_id = 0;
|
||||||
uint64_t max_id_mod_rev = 0, cfg_mod_rev = 0, idx_mod_rev = 0;
|
uint64_t max_id_mod_rev = 0, idx_mod_rev = 0;
|
||||||
|
inode_config_t cur_cfg;
|
||||||
inode_config_t new_cfg;
|
inode_config_t new_cfg;
|
||||||
|
|
||||||
int state = 0;
|
int state = 0;
|
||||||
@@ -64,7 +73,8 @@ struct image_creator_t
|
|||||||
}
|
}
|
||||||
if (new_pool_id)
|
if (new_pool_id)
|
||||||
{
|
{
|
||||||
if (pools.find(new_pool_id) == pools.end())
|
auto pool_it = pools.find(new_pool_id);
|
||||||
|
if (pool_it == pools.end())
|
||||||
{
|
{
|
||||||
result = (cli_result_t){ .err = ENOENT, .text = "Pool "+std::to_string(new_pool_id)+" does not exist" };
|
result = (cli_result_t){ .err = ENOENT, .text = "Pool "+std::to_string(new_pool_id)+" does not exist" };
|
||||||
state = 100;
|
state = 100;
|
||||||
@@ -111,6 +121,23 @@ struct image_creator_t
|
|||||||
create_snapshot();
|
create_snapshot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool check_pool_permission()
|
||||||
|
{
|
||||||
|
if (!parent->user || parent->user->type == "admin")
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
auto pool_it = parent->cli->st_cli.pool_config.find(new_pool_id);
|
||||||
|
if (pool_it == parent->cli->st_cli.pool_config.end() ||
|
||||||
|
(pool_it->second.creator_group == "" || parent->user->groups.find(pool_it->second.creator_group) == parent->user->groups.end()))
|
||||||
|
{
|
||||||
|
result = (cli_result_t){ .err = EACCES, .text = "Pool image create permission denied" };
|
||||||
|
state = 100;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void create_image()
|
void create_image()
|
||||||
{
|
{
|
||||||
if (state == 2)
|
if (state == 2)
|
||||||
@@ -150,6 +177,10 @@ struct image_creator_t
|
|||||||
state = 100;
|
state = 100;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!check_pool_permission())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!size && !force_size)
|
if (!size && !force_size)
|
||||||
{
|
{
|
||||||
result = (cli_result_t){ .err = EINVAL, .text = "Image size is missing" };
|
result = (cli_result_t){ .err = EINVAL, .text = "Image size is missing" };
|
||||||
@@ -194,15 +225,11 @@ resume_3:
|
|||||||
// Save into inode_config for library users to be able to take it from there immediately
|
// Save into inode_config for library users to be able to take it from there immediately
|
||||||
new_cfg.mod_revision = parent->etcd_result["header"]["revision"].uint64_value();
|
new_cfg.mod_revision = parent->etcd_result["header"]["revision"].uint64_value();
|
||||||
parent->cli->st_cli.insert_inode_config(new_cfg);
|
parent->cli->st_cli.insert_inode_config(new_cfg);
|
||||||
|
auto img = parent->format_image(new_cfg);
|
||||||
result = (cli_result_t){
|
result = (cli_result_t){
|
||||||
.err = 0,
|
.err = 0,
|
||||||
.text = "Image "+image_name+" created",
|
.text = "Image "+image_name+" created",
|
||||||
.data = json11::Json::object {
|
.data = img,
|
||||||
{ "name", image_name },
|
|
||||||
{ "pool", new_pool_name },
|
|
||||||
{ "parent", new_parent },
|
|
||||||
{ "size", size },
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
state = 100;
|
state = 100;
|
||||||
}
|
}
|
||||||
@@ -233,7 +260,7 @@ resume_3:
|
|||||||
}
|
}
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
// In addition to next_id, get: size, old_id, old_pool_id, new_parent, cfg_mod_rev, idx_mod_rev
|
// In addition to next_id, get: cur_cfg, old_id, old_pool_id, size, idx_mod_rev
|
||||||
resume_2:
|
resume_2:
|
||||||
resume_3:
|
resume_3:
|
||||||
get_image_details();
|
get_image_details();
|
||||||
@@ -245,11 +272,22 @@ resume_3:
|
|||||||
state = 100;
|
state = 100;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!parent->check_image_perm(cur_cfg, true))
|
||||||
|
{
|
||||||
|
result = (cli_result_t){ .err = EACCES, .text = "Image permission denied" };
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!new_pool_id)
|
if (!new_pool_id)
|
||||||
{
|
{
|
||||||
// Create snapshot in the same pool by default
|
// Create snapshot in the same pool by default
|
||||||
new_pool_id = old_pool_id;
|
new_pool_id = old_pool_id;
|
||||||
}
|
}
|
||||||
|
// Verify pool permissions if the pool is different from the original
|
||||||
|
if (new_pool_id != old_pool_id && !check_pool_permission())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
attempt_create();
|
attempt_create();
|
||||||
state = 4;
|
state = 4;
|
||||||
resume_4:
|
resume_4:
|
||||||
@@ -272,13 +310,23 @@ resume_4:
|
|||||||
// Save into inode_config for library users to be able to take it from there immediately
|
// Save into inode_config for library users to be able to take it from there immediately
|
||||||
new_cfg.mod_revision = parent->etcd_result["header"]["revision"].uint64_value();
|
new_cfg.mod_revision = parent->etcd_result["header"]["revision"].uint64_value();
|
||||||
parent->cli->st_cli.insert_inode_config(new_cfg);
|
parent->cli->st_cli.insert_inode_config(new_cfg);
|
||||||
|
{
|
||||||
|
auto new_pool_it = parent->cli->st_cli.pool_config.find(new_pool_id);
|
||||||
|
new_pool_name = new_pool_it != parent->cli->st_cli.pool_config.end() ? new_pool_it->second.name : "";
|
||||||
|
}
|
||||||
result = (cli_result_t){
|
result = (cli_result_t){
|
||||||
.err = 0,
|
.err = 0,
|
||||||
.text = "Snapshot "+image_name+"@"+new_snap+" created",
|
.text = "Snapshot "+image_name+"@"+new_snap+" created",
|
||||||
.data = json11::Json::object {
|
.data = json11::Json::object {
|
||||||
{ "name", image_name+"@"+new_snap },
|
{ "inode_id", INODE_WITH_POOL(new_pool_id, new_id) },
|
||||||
{ "pool", (uint64_t)new_pool_id },
|
{ "inode_num", new_id },
|
||||||
{ "parent", new_parent },
|
{ "name", image_name },
|
||||||
|
{ "pool_id", (uint64_t)new_pool_id },
|
||||||
|
{ "pool_name", new_pool_name },
|
||||||
|
{ "parent_name", image_name+"@"+new_snap },
|
||||||
|
{ "parent_inode_id", INODE_WITH_POOL(old_pool_id, old_id) },
|
||||||
|
{ "parent_inode_num", old_id },
|
||||||
|
{ "parent_pool_id", (uint64_t)old_pool_id },
|
||||||
{ "size", size },
|
{ "size", size },
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -323,17 +371,6 @@ resume_4:
|
|||||||
goto resume_2;
|
goto resume_2;
|
||||||
else if (state == 3)
|
else if (state == 3)
|
||||||
goto resume_3;
|
goto resume_3;
|
||||||
if (!new_pool_id)
|
|
||||||
{
|
|
||||||
for (auto & ic: parent->cli->st_cli.inode_config)
|
|
||||||
{
|
|
||||||
if (ic.second.name == image_name)
|
|
||||||
{
|
|
||||||
new_pool_id = INODE_POOL(ic.first);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parent->etcd_txn(json11::Json::object { { "success", json11::Json::array {
|
parent->etcd_txn(json11::Json::object { { "success", json11::Json::array {
|
||||||
get_next_id(),
|
get_next_id(),
|
||||||
json11::Json::object {
|
json11::Json::object {
|
||||||
@@ -357,7 +394,7 @@ resume_2:
|
|||||||
extract_next_id(parent->etcd_result["responses"][0]);
|
extract_next_id(parent->etcd_result["responses"][0]);
|
||||||
old_id = 0;
|
old_id = 0;
|
||||||
old_pool_id = 0;
|
old_pool_id = 0;
|
||||||
cfg_mod_rev = idx_mod_rev = 0;
|
idx_mod_rev = 0;
|
||||||
if (parent->etcd_result["responses"][1]["response_range"]["kvs"].array_items().size() == 0)
|
if (parent->etcd_result["responses"][1]["response_range"]["kvs"].array_items().size() == 0)
|
||||||
{
|
{
|
||||||
for (auto & ic: parent->cli->st_cli.inode_config)
|
for (auto & ic: parent->cli->st_cli.inode_config)
|
||||||
@@ -366,9 +403,8 @@ resume_2:
|
|||||||
{
|
{
|
||||||
old_id = INODE_NO_POOL(ic.first);
|
old_id = INODE_NO_POOL(ic.first);
|
||||||
old_pool_id = INODE_POOL(ic.first);
|
old_pool_id = INODE_POOL(ic.first);
|
||||||
|
cur_cfg = ic.second;
|
||||||
size = ic.second.size;
|
size = ic.second.size;
|
||||||
new_parent_id = ic.second.parent_id;
|
|
||||||
cfg_mod_rev = ic.second.mod_revision;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -412,16 +448,14 @@ resume_3:
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto kv = parent->cli->st_cli.parse_etcd_kv(parent->etcd_result["responses"][0]["response_range"]["kvs"][0]);
|
auto kv = parent->cli->st_cli.parse_etcd_kv(parent->etcd_result["responses"][0]["response_range"]["kvs"][0]);
|
||||||
size = kv.value["size"].uint64_value();
|
cur_cfg = parent->cli->st_cli.deserialize_inode_cfg(INODE_WITH_POOL(old_pool_id, old_id), kv.value, kv.mod_revision);
|
||||||
new_parent_id = kv.value["parent_id"].uint64_value();
|
size = cur_cfg.size;
|
||||||
uint64_t parent_pool_id = kv.value["parent_pool"].uint64_value();
|
|
||||||
if (new_parent_id)
|
|
||||||
{
|
|
||||||
new_parent_id = INODE_WITH_POOL(parent_pool_id ? parent_pool_id : old_pool_id, new_parent_id);
|
|
||||||
}
|
|
||||||
cfg_mod_rev = kv.mod_revision;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!new_pool_id)
|
||||||
|
{
|
||||||
|
new_pool_id = old_pool_id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void attempt_create()
|
void attempt_create()
|
||||||
@@ -434,6 +468,27 @@ resume_3:
|
|||||||
.readonly = false,
|
.readonly = false,
|
||||||
.meta = new_meta,
|
.meta = new_meta,
|
||||||
};
|
};
|
||||||
|
if (set_key)
|
||||||
|
{
|
||||||
|
new_cfg.enc_key = enc_key;
|
||||||
|
}
|
||||||
|
else if (new_snap != "")
|
||||||
|
{
|
||||||
|
new_cfg.enc_key = cur_cfg.enc_key;
|
||||||
|
}
|
||||||
|
new_cfg.owner = http_context_get_ssl_cn(parent->cli->st_cli.get_http_ctx());
|
||||||
|
if (!new_owner.empty())
|
||||||
|
{
|
||||||
|
new_cfg.owner = new_owner;
|
||||||
|
}
|
||||||
|
if (!new_owner_group.empty())
|
||||||
|
{
|
||||||
|
new_cfg.owner_group = new_owner_group;
|
||||||
|
}
|
||||||
|
if (!new_reader_group.empty())
|
||||||
|
{
|
||||||
|
new_cfg.reader_group = new_reader_group;
|
||||||
|
}
|
||||||
json11::Json::array checks = json11::Json::array {
|
json11::Json::array checks = json11::Json::array {
|
||||||
json11::Json::object {
|
json11::Json::object {
|
||||||
{ "target", "VERSION" },
|
{ "target", "VERSION" },
|
||||||
@@ -500,16 +555,12 @@ resume_3:
|
|||||||
};
|
};
|
||||||
if (new_snap != "")
|
if (new_snap != "")
|
||||||
{
|
{
|
||||||
inode_config_t snap_cfg = {
|
inode_config_t snap_cfg = cur_cfg;
|
||||||
.num = INODE_WITH_POOL(old_pool_id, old_id),
|
snap_cfg.name = image_name+"@"+new_snap;
|
||||||
.name = image_name+"@"+new_snap,
|
snap_cfg.readonly = true;
|
||||||
.size = size,
|
|
||||||
.parent_id = new_parent_id,
|
|
||||||
.readonly = true,
|
|
||||||
};
|
|
||||||
checks.push_back(json11::Json::object {
|
checks.push_back(json11::Json::object {
|
||||||
{ "target", "MOD" },
|
{ "target", "MOD" },
|
||||||
{ "mod_revision", cfg_mod_rev },
|
{ "mod_revision", cur_cfg.mod_revision },
|
||||||
{ "key", base64_encode(
|
{ "key", base64_encode(
|
||||||
parent->cli->st_cli.etcd_prefix+"/config/inode/"+
|
parent->cli->st_cli.etcd_prefix+"/config/inode/"+
|
||||||
std::to_string(old_pool_id)+"/"+std::to_string(old_id)
|
std::to_string(old_pool_id)+"/"+std::to_string(old_id)
|
||||||
@@ -554,8 +605,16 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_create(json11::Json cfg)
|
|||||||
auto image_creator = new image_creator_t();
|
auto image_creator = new image_creator_t();
|
||||||
image_creator->parent = this;
|
image_creator->parent = this;
|
||||||
image_creator->image_name = cfg["image"].string_value();
|
image_creator->image_name = cfg["image"].string_value();
|
||||||
image_creator->new_pool_id = cfg["pool"].uint64_value();
|
if (!cfg["pool"].is_null())
|
||||||
image_creator->new_pool_name = cfg["pool"].string_value();
|
{
|
||||||
|
image_creator->new_pool_id = cfg["pool"].uint64_value();
|
||||||
|
image_creator->new_pool_name = cfg["pool"].string_value();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
image_creator->new_pool_id = cfg["pool_id"].uint64_value();
|
||||||
|
image_creator->new_pool_name = cfg["pool_name"].string_value();
|
||||||
|
}
|
||||||
image_creator->force = cfg["force"].bool_value();
|
image_creator->force = cfg["force"].bool_value();
|
||||||
image_creator->force_size = cfg["force_size"].bool_value();
|
image_creator->force_size = cfg["force_size"].bool_value();
|
||||||
if (cfg["image_meta"].is_object())
|
if (cfg["image_meta"].is_object())
|
||||||
@@ -566,6 +625,35 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_create(json11::Json cfg)
|
|||||||
{
|
{
|
||||||
image_creator->new_snap = cfg["snapshot"].string_value();
|
image_creator->new_snap = cfg["snapshot"].string_value();
|
||||||
}
|
}
|
||||||
|
if (!cfg["enc_key"].is_null())
|
||||||
|
{
|
||||||
|
image_creator->set_key = true;
|
||||||
|
#ifdef WITH_OPENSSL
|
||||||
|
if (image_creator->enc_key == "random")
|
||||||
|
{
|
||||||
|
uint8_t newkey[64];
|
||||||
|
RAND_bytes(newkey, 64);
|
||||||
|
image_creator->enc_key = tohexstr(newkey, 64);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
else
|
||||||
|
{
|
||||||
|
image_creator->enc_key = cfg["enc_key"].string_value();
|
||||||
|
if (image_creator->enc_key != "" &&
|
||||||
|
image_creator->enc_key.substr(0, strlen(VAULT_KEY_PREFIX)) != VAULT_KEY_PREFIX &&
|
||||||
|
(!ishexstr(image_creator->enc_key) || image_creator->enc_key.size() != 128))
|
||||||
|
{
|
||||||
|
return [](cli_result_t & result)
|
||||||
|
{
|
||||||
|
result = (cli_result_t){ .err = EINVAL, .text = "Encryption key is not a 512-bit hex string, not \"\" and not \"random\"" };
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
image_creator->new_owner = cfg["owner"].string_value();
|
||||||
|
image_creator->new_owner_group = cfg["owner_group"].string_value();
|
||||||
|
image_creator->new_reader_group = cfg["reader_group"].string_value();
|
||||||
image_creator->new_parent = cfg["parent"].string_value();
|
image_creator->new_parent = cfg["parent"].string_value();
|
||||||
if (!cfg["size"].is_null())
|
if (!cfg["size"].is_null())
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-1
@@ -864,7 +864,7 @@ resume_2:
|
|||||||
// Copy data
|
// Copy data
|
||||||
if (iinfo.in_seekable && iseek >= iinfo.in_size)
|
if (iinfo.in_seekable && iseek >= iinfo.in_size)
|
||||||
{
|
{
|
||||||
result = (cli_result_t){ .err = -EINVAL, .text = "Input seek position is beyond end of input" };
|
result = (cli_result_t){ .err = EINVAL, .text = "Input seek position is beyond end of input" };
|
||||||
goto close_end;
|
goto close_end;
|
||||||
}
|
}
|
||||||
if (!iinfo.iwatch && !iinfo.in_seekable && iseek)
|
if (!iinfo.iwatch && !iinfo.in_seekable && iseek)
|
||||||
|
|||||||
@@ -57,12 +57,24 @@ struct cli_describe_t
|
|||||||
|
|
||||||
void parse_options(json11::Json cfg)
|
void parse_options(json11::Json cfg)
|
||||||
{
|
{
|
||||||
only_pool = cfg["pool"].uint64_value();
|
uint64_t pool_id;
|
||||||
if (!only_pool && cfg["pool"].is_string())
|
std::string pool_name;
|
||||||
|
if (!cfg["pool"].is_null())
|
||||||
|
{
|
||||||
|
pool_id = cfg["pool"].uint64_value();
|
||||||
|
pool_name = pool_id ? "" : cfg["pool"].string_value();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pool_id = cfg["pool_id"].uint64_value();
|
||||||
|
pool_name = pool_id ? "" : cfg["pool_name"].string_value();
|
||||||
|
}
|
||||||
|
only_pool = pool_id;
|
||||||
|
if (!only_pool && pool_name != "")
|
||||||
{
|
{
|
||||||
for (auto & pp: parent->cli->st_cli.pool_config)
|
for (auto & pp: parent->cli->st_cli.pool_config)
|
||||||
{
|
{
|
||||||
if (pp.second.name == cfg["pool"].string_value())
|
if (pp.second.name == pool_name)
|
||||||
{
|
{
|
||||||
only_pool = pp.first;
|
only_pool = pp.first;
|
||||||
break;
|
break;
|
||||||
@@ -106,6 +118,22 @@ struct cli_describe_t
|
|||||||
if (cfg["object_state"].string_value().find("misplaced") != std::string::npos)
|
if (cfg["object_state"].string_value().find("misplaced") != std::string::npos)
|
||||||
object_state |= OBJ_MISPLACED;
|
object_state |= OBJ_MISPLACED;
|
||||||
}
|
}
|
||||||
|
else if (!object_state && cfg["object_state"].is_array())
|
||||||
|
{
|
||||||
|
for (auto & st: cfg["object_state"].array_items())
|
||||||
|
{
|
||||||
|
if (st == "inconsistent")
|
||||||
|
object_state |= OBJ_INCONSISTENT;
|
||||||
|
else if (st == "corrupted")
|
||||||
|
object_state |= OBJ_CORRUPTED;
|
||||||
|
else if (st == "incomplete")
|
||||||
|
object_state |= OBJ_INCOMPLETE;
|
||||||
|
else if (st == "degraded")
|
||||||
|
object_state |= OBJ_DEGRADED;
|
||||||
|
else if (st == "misplaced")
|
||||||
|
object_state |= OBJ_MISPLACED;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
|
|||||||
@@ -35,6 +35,12 @@ struct snap_flattener_t
|
|||||||
state = 100;
|
state = 100;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!parent->check_image_perm(*target_cfg, true))
|
||||||
|
{
|
||||||
|
result = (cli_result_t){ .err = EACCES, .text = "Image permission denied" };
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
target_id = target_cfg->num;
|
target_id = target_cfg->num;
|
||||||
std::vector<inode_t> chain_list;
|
std::vector<inode_t> chain_list;
|
||||||
inode_config_t *cur = target_cfg;
|
inode_config_t *cur = target_cfg;
|
||||||
|
|||||||
+65
-43
@@ -17,6 +17,7 @@ struct image_lister_t
|
|||||||
std::string list_pool_name;
|
std::string list_pool_name;
|
||||||
std::string sort_field;
|
std::string sort_field;
|
||||||
std::set<std::string> only_names;
|
std::set<std::string> only_names;
|
||||||
|
std::vector<uint64_t> only_ids;
|
||||||
bool reverse = false;
|
bool reverse = false;
|
||||||
bool exact = false;
|
bool exact = false;
|
||||||
bool tree = false;
|
bool tree = false;
|
||||||
@@ -52,35 +53,19 @@ struct image_lister_t
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto & ic: parent->cli->st_cli.inode_config)
|
auto begin_it = list_pool_id
|
||||||
|
? parent->cli->st_cli.inode_config.lower_bound(INODE_WITH_POOL(list_pool_id, 0))
|
||||||
|
: parent->cli->st_cli.inode_config.begin();
|
||||||
|
auto end_it = list_pool_id
|
||||||
|
? parent->cli->st_cli.inode_config.lower_bound(INODE_WITH_POOL(list_pool_id+1, 0))
|
||||||
|
: parent->cli->st_cli.inode_config.end();
|
||||||
|
for (auto it = begin_it; it != end_it; it++)
|
||||||
{
|
{
|
||||||
if (list_pool_id && INODE_POOL(ic.second.num) != list_pool_id)
|
if (!parent->check_image_perm(it->second, false))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto pool_it = parent->cli->st_cli.pool_config.find(INODE_POOL(ic.second.num));
|
stats[it->second.num] = parent->format_image(it->second);
|
||||||
bool good_pool = pool_it != parent->cli->st_cli.pool_config.end();
|
|
||||||
auto item = json11::Json::object {
|
|
||||||
{ "name", ic.second.name },
|
|
||||||
{ "size", ic.second.size },
|
|
||||||
{ "used_size", 0 },
|
|
||||||
{ "readonly", ic.second.readonly },
|
|
||||||
{ "pool_id", (uint64_t)INODE_POOL(ic.second.num) },
|
|
||||||
{ "pool_name", good_pool ? pool_it->second.name : "? (ID:"+std::to_string(INODE_POOL(ic.second.num))+")" },
|
|
||||||
{ "inode_num", INODE_NO_POOL(ic.second.num) },
|
|
||||||
{ "inode_id", ic.second.num },
|
|
||||||
{ "deleted", ic.second.deleted },
|
|
||||||
};
|
|
||||||
if (ic.second.parent_id)
|
|
||||||
{
|
|
||||||
auto p_it = parent->cli->st_cli.inode_config.find(ic.second.parent_id);
|
|
||||||
item["parent_name"] = p_it != parent->cli->st_cli.inode_config.end()
|
|
||||||
? p_it->second.name : "";
|
|
||||||
item["parent_pool_id"] = (uint64_t)INODE_POOL(ic.second.parent_id);
|
|
||||||
item["parent_inode_num"] = INODE_NO_POOL(ic.second.parent_id);
|
|
||||||
item["parent_inode_id"] = ic.second.parent_id;
|
|
||||||
}
|
|
||||||
stats[ic.second.num] = item;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,6 +112,7 @@ resume_1:
|
|||||||
state = 100;
|
state = 100;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// FIXME: Do not always read everything
|
||||||
space_info = parent->etcd_result;
|
space_info = parent->etcd_result;
|
||||||
std::map<pool_id_t, uint64_t> pool_pg_real_size;
|
std::map<pool_id_t, uint64_t> pool_pg_real_size;
|
||||||
for (auto & kv_item: space_info["responses"][0]["response_range"]["kvs"].array_items())
|
for (auto & kv_item: space_info["responses"][0]["response_range"]["kvs"].array_items())
|
||||||
@@ -160,6 +146,11 @@ resume_1:
|
|||||||
}
|
}
|
||||||
inode_t inode_num = INODE_WITH_POOL(pool_id, only_inode_num);
|
inode_t inode_num = INODE_WITH_POOL(pool_id, only_inode_num);
|
||||||
uint64_t used_size = kv.value["raw_used"].uint64_value();
|
uint64_t used_size = kv.value["raw_used"].uint64_value();
|
||||||
|
auto stat_it = stats.find(inode_num);
|
||||||
|
if (parent->user && parent->user->type != "admin" && stat_it == stats.end())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// save stats
|
// save stats
|
||||||
auto pool_it = parent->cli->st_cli.pool_config.find(pool_id);
|
auto pool_it = parent->cli->st_cli.pool_config.find(pool_id);
|
||||||
if (pool_it != parent->cli->st_cli.pool_config.end())
|
if (pool_it != parent->cli->st_cli.pool_config.end())
|
||||||
@@ -168,7 +159,6 @@ resume_1:
|
|||||||
used_size = used_size / (pool_pg_real_size[pool_id] ? pool_pg_real_size[pool_id] : 1)
|
used_size = used_size / (pool_pg_real_size[pool_id] ? pool_pg_real_size[pool_id] : 1)
|
||||||
* (pool_cfg.scheme == POOL_SCHEME_REPLICATED ? 1 : pool_cfg.pg_size-pool_cfg.parity_chunks);
|
* (pool_cfg.scheme == POOL_SCHEME_REPLICATED ? 1 : pool_cfg.pg_size-pool_cfg.parity_chunks);
|
||||||
}
|
}
|
||||||
auto stat_it = stats.find(inode_num);
|
|
||||||
if (stat_it == stats.end())
|
if (stat_it == stats.end())
|
||||||
{
|
{
|
||||||
stats[inode_num] = json11::Json::object {
|
stats[inode_num] = json11::Json::object {
|
||||||
@@ -202,20 +192,33 @@ resume_1:
|
|||||||
json11::Json::array to_list()
|
json11::Json::array to_list()
|
||||||
{
|
{
|
||||||
json11::Json::array list;
|
json11::Json::array list;
|
||||||
for (auto & kv: stats)
|
if (only_ids.size())
|
||||||
{
|
{
|
||||||
if (!only_names.size())
|
for (auto & id: only_ids)
|
||||||
{
|
{
|
||||||
list.push_back(kv.second);
|
if (stats.find(id) != stats.end())
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (auto & glob: only_names)
|
|
||||||
{
|
{
|
||||||
if (exact ? (kv.second["name"].string_value() == glob) : stupid_glob(kv.second["name"].string_value(), glob))
|
list.push_back(stats[id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (auto & kv: stats)
|
||||||
|
{
|
||||||
|
if (!only_names.size())
|
||||||
|
{
|
||||||
|
list.push_back(kv.second);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (auto & glob: only_names)
|
||||||
{
|
{
|
||||||
list.push_back(kv.second);
|
if (exact ? (kv.second["name"].string_value() == glob) : stupid_glob(kv.second["name"].string_value(), glob))
|
||||||
break;
|
{
|
||||||
|
list.push_back(kv.second);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -371,7 +374,7 @@ resume_1:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
cols.push_back(json11::Json::object{
|
cols.push_back(json11::Json::object{
|
||||||
{ "key", "ro" },
|
{ "key", "flags" },
|
||||||
{ "title", "FLAGS" },
|
{ "title", "FLAGS" },
|
||||||
{ "right", true },
|
{ "right", true },
|
||||||
});
|
});
|
||||||
@@ -399,8 +402,14 @@ resume_1:
|
|||||||
kv.second["delete_q"] = format_q(kv.second["delete_queue"].number_value());
|
kv.second["delete_q"] = format_q(kv.second["delete_queue"].number_value());
|
||||||
}
|
}
|
||||||
kv.second["size_fmt"] = format_size(kv.second["size"].uint64_value());
|
kv.second["size_fmt"] = format_size(kv.second["size"].uint64_value());
|
||||||
kv.second["ro"] = kv.second["deleted"].bool_value() ? "DEL" :
|
std::string flags;
|
||||||
(kv.second["readonly"].bool_value() ? "RO" : "-");
|
if (kv.second["deleted"].bool_value())
|
||||||
|
flags += "DEL";
|
||||||
|
if (kv.second["readonly"].bool_value())
|
||||||
|
flags += (flags.empty() ? "RO" : ",RO");
|
||||||
|
if (kv.second["encrypted"].bool_value())
|
||||||
|
flags += (flags.empty() ? "ENC" : ",ENC");
|
||||||
|
kv.second["flags"] = flags;
|
||||||
}
|
}
|
||||||
result.text = print_table(tree ? to_tree(to_list()) : to_list(), cols, parent->color);
|
result.text = print_table(tree ? to_tree(to_list()) : to_list(), cols, parent->color);
|
||||||
state = 100;
|
state = 100;
|
||||||
@@ -570,17 +579,30 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_ls(json11::Json cfg)
|
|||||||
lister->parent = this;
|
lister->parent = this;
|
||||||
lister->exact = cfg["exact"].bool_value();
|
lister->exact = cfg["exact"].bool_value();
|
||||||
lister->tree = cfg["tree"].bool_value();
|
lister->tree = cfg["tree"].bool_value();
|
||||||
lister->list_pool_id = cfg["pool"].uint64_value();
|
if (!cfg["pool"].is_null())
|
||||||
lister->list_pool_name = lister->list_pool_id ? "" : cfg["pool"].as_string();
|
{
|
||||||
|
lister->list_pool_id = cfg["pool"].uint64_value();
|
||||||
|
lister->list_pool_name = lister->list_pool_id ? "" : cfg["pool"].as_string();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lister->list_pool_id = cfg["pool_id"].uint64_value();
|
||||||
|
lister->list_pool_name = lister->list_pool_id ? "" : cfg["pool_name"].string_value();
|
||||||
|
}
|
||||||
lister->show_stats = cfg["long"].bool_value();
|
lister->show_stats = cfg["long"].bool_value();
|
||||||
lister->show_delete = cfg["del"].bool_value();
|
lister->show_delete = cfg["del"].bool_value();
|
||||||
lister->sort_field = cfg["sort"].string_value() != "" ? cfg["sort"].string_value() : "name";
|
lister->sort_field = cfg["sort"].string_value() != "" ? cfg["sort"].string_value() : "name";
|
||||||
lister->reverse = cfg["reverse"].bool_value();
|
lister->reverse = cfg["reverse"].bool_value();
|
||||||
lister->max_count = cfg["count"].uint64_value();
|
lister->max_count = cfg["count"].uint64_value();
|
||||||
|
if (cfg["names"].is_string())
|
||||||
|
lister->only_names.insert(cfg["names"].string_value());
|
||||||
for (auto & item: cfg["names"].array_items())
|
for (auto & item: cfg["names"].array_items())
|
||||||
{
|
|
||||||
lister->only_names.insert(item.string_value());
|
lister->only_names.insert(item.string_value());
|
||||||
}
|
if (cfg["ids"].is_string())
|
||||||
|
for (auto & item: explode(",", cfg["ids"].string_value(), true))
|
||||||
|
lister->only_ids.push_back(stoull_full(item));
|
||||||
|
for (auto & item: cfg["ids"].array_items())
|
||||||
|
lister->only_ids.push_back(item.uint64_value());
|
||||||
return [lister](cli_result_t & result)
|
return [lister](cli_result_t & result)
|
||||||
{
|
{
|
||||||
lister->loop();
|
lister->loop();
|
||||||
|
|||||||
@@ -374,7 +374,7 @@ struct snap_merger_t
|
|||||||
result = (cli_result_t){ .text = "Done, layers from "+from_name+" to "+to_name+" merged into "+target_name, .data = json11::Json::object {
|
result = (cli_result_t){ .text = "Done, layers from "+from_name+" to "+to_name+" merged into "+target_name, .data = json11::Json::object {
|
||||||
{ "from", from_name },
|
{ "from", from_name },
|
||||||
{ "to", to_name },
|
{ "to", to_name },
|
||||||
{ "into", target_name },
|
{ "target", target_name },
|
||||||
}};
|
}};
|
||||||
state = 100;
|
state = 100;
|
||||||
resume_100:
|
resume_100:
|
||||||
|
|||||||
+46
-10
@@ -17,6 +17,9 @@ struct image_changer_t
|
|||||||
bool force_size = false, inc_size = false;
|
bool force_size = false, inc_size = false;
|
||||||
bool set_readonly = false, set_readwrite = false, force = false;
|
bool set_readonly = false, set_readwrite = false, force = false;
|
||||||
bool set_deleted = false, new_deleted = false;
|
bool set_deleted = false, new_deleted = false;
|
||||||
|
bool set_key = false;
|
||||||
|
std::string enc_key;
|
||||||
|
json11::Json new_owner, new_owner_group, new_reader_group;
|
||||||
bool down_ok = false;
|
bool down_ok = false;
|
||||||
// interval between fsyncs
|
// interval between fsyncs
|
||||||
int fsync_interval = 128;
|
int fsync_interval = 128;
|
||||||
@@ -74,6 +77,12 @@ struct image_changer_t
|
|||||||
state = 100;
|
state = 100;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!parent->check_image_perm(cfg, true))
|
||||||
|
{
|
||||||
|
result = (cli_result_t){ .err = EACCES, .text = "Image permission denied" };
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (auto & ic: parent->cli->st_cli.inode_config)
|
for (auto & ic: parent->cli->st_cli.inode_config)
|
||||||
{
|
{
|
||||||
if (ic.second.parent_id == inode_num)
|
if (ic.second.parent_id == inode_num)
|
||||||
@@ -88,10 +97,7 @@ struct image_changer_t
|
|||||||
(!new_size && !force_size || cfg.size == new_size || cfg.size >= new_size && inc_size) &&
|
(!new_size && !force_size || cfg.size == new_size || cfg.size >= new_size && inc_size) &&
|
||||||
(new_name == "" || new_name == image_name))
|
(new_name == "" || new_name == image_name))
|
||||||
{
|
{
|
||||||
result = (cli_result_t){ .err = 0, .text = "No change", .data = json11::Json::object {
|
result = (cli_result_t){ .err = 0, .text = "No change", .data = parent->format_image(cfg) };
|
||||||
{ "error_code", 0 },
|
|
||||||
{ "error_text", "No change" },
|
|
||||||
}};
|
|
||||||
state = 100;
|
state = 100;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -152,6 +158,36 @@ resume_1:
|
|||||||
{
|
{
|
||||||
cfg.name = new_name;
|
cfg.name = new_name;
|
||||||
}
|
}
|
||||||
|
if (new_owner.is_string())
|
||||||
|
{
|
||||||
|
cfg.owner = new_owner.string_value();
|
||||||
|
}
|
||||||
|
if (new_owner_group.is_string())
|
||||||
|
{
|
||||||
|
cfg.owner_group = new_owner_group.string_value();
|
||||||
|
}
|
||||||
|
if (new_reader_group.is_string())
|
||||||
|
{
|
||||||
|
cfg.reader_group = new_reader_group.string_value();
|
||||||
|
}
|
||||||
|
if (set_key)
|
||||||
|
{
|
||||||
|
if (!force)
|
||||||
|
{
|
||||||
|
result = (cli_result_t){ .err = EINVAL, .text = "Changing image encryption key is only allowed with --force" };
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (enc_key != "" &&
|
||||||
|
enc_key.substr(0, strlen(VAULT_KEY_PREFIX)) != VAULT_KEY_PREFIX &&
|
||||||
|
(!ishexstr(enc_key) || enc_key.size() != 128))
|
||||||
|
{
|
||||||
|
result = (cli_result_t){ .err = EINVAL, .text = "Encryption key is not a 512-bit hex string and not \"\"" };
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cfg.enc_key = enc_key;
|
||||||
|
}
|
||||||
{
|
{
|
||||||
std::string cur_cfg_key = base64_encode(parent->cli->st_cli.etcd_prefix+
|
std::string cur_cfg_key = base64_encode(parent->cli->st_cli.etcd_prefix+
|
||||||
"/config/inode/"+std::to_string(INODE_POOL(inode_num))+
|
"/config/inode/"+std::to_string(INODE_POOL(inode_num))+
|
||||||
@@ -235,12 +271,7 @@ resume_2:
|
|||||||
result = (cli_result_t){
|
result = (cli_result_t){
|
||||||
.err = 0,
|
.err = 0,
|
||||||
.text = "Image "+image_name+" modified",
|
.text = "Image "+image_name+" modified",
|
||||||
.data = json11::Json::object {
|
.data = parent->format_image(cfg)
|
||||||
{ "name", image_name },
|
|
||||||
{ "inode", INODE_NO_POOL(inode_num) },
|
|
||||||
{ "pool", (uint64_t)INODE_POOL(inode_num) },
|
|
||||||
{ "size", new_size },
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
state = 100;
|
state = 100;
|
||||||
}
|
}
|
||||||
@@ -261,9 +292,14 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_modify(json11::Json cfg)
|
|||||||
changer->set_deleted = !cfg["deleted"].is_null();
|
changer->set_deleted = !cfg["deleted"].is_null();
|
||||||
changer->new_deleted = json_is_true(cfg["deleted"]);
|
changer->new_deleted = json_is_true(cfg["deleted"]);
|
||||||
changer->fsync_interval = cfg["fsync_interval"].uint64_value();
|
changer->fsync_interval = cfg["fsync_interval"].uint64_value();
|
||||||
|
changer->enc_key = cfg["enc_key"].string_value();
|
||||||
|
changer->set_key = cfg["enc_key"].is_string();
|
||||||
if (!changer->fsync_interval)
|
if (!changer->fsync_interval)
|
||||||
changer->fsync_interval = 128;
|
changer->fsync_interval = 128;
|
||||||
changer->down_ok = cfg["down_ok"].bool_value();
|
changer->down_ok = cfg["down_ok"].bool_value();
|
||||||
|
changer->new_owner = cfg["owner"];
|
||||||
|
changer->new_owner_group = cfg["owner_group"];
|
||||||
|
changer->new_reader_group = cfg["reader_group"];
|
||||||
// FIXME Check that the image doesn't have children when shrinking
|
// FIXME Check that the image doesn't have children when shrinking
|
||||||
return [changer](cli_result_t & result)
|
return [changer](cli_result_t & result)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
// Copyright (c) Vitaliy Filippov, 2019+
|
||||||
|
// License: VNPL-1.1 (see README.md for details)
|
||||||
|
|
||||||
|
#include "cli.h"
|
||||||
|
#include "cluster_client.h"
|
||||||
|
#include "str_util.h"
|
||||||
|
|
||||||
|
// Create/update/delete a user
|
||||||
|
struct cli_modify_user_t
|
||||||
|
{
|
||||||
|
cli_tool_t *parent;
|
||||||
|
|
||||||
|
std::string user_name;
|
||||||
|
std::string user_type;
|
||||||
|
json11::Json groups;
|
||||||
|
bool del = false;
|
||||||
|
|
||||||
|
int state = 0;
|
||||||
|
cli_result_t result;
|
||||||
|
etcd_kv_t kv;
|
||||||
|
json11::Json::object new_cfg;
|
||||||
|
|
||||||
|
bool is_done()
|
||||||
|
{
|
||||||
|
return state == 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
if (state == 1)
|
||||||
|
goto resume_1;
|
||||||
|
else if (state == 2)
|
||||||
|
goto resume_2;
|
||||||
|
if (user_type != "client" && user_type != "admin" && user_type != "mon" && user_type != "osd")
|
||||||
|
{
|
||||||
|
result = (cli_result_t){ .err = EINVAL, .text = "Unknown user type: "+user_type };
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (groups.is_string())
|
||||||
|
{
|
||||||
|
groups = groups == "" ? std::vector<std::string>() : explode(",", groups.string_value(), true);
|
||||||
|
}
|
||||||
|
else if (groups.is_array())
|
||||||
|
{
|
||||||
|
for (auto & gr: groups.array_items())
|
||||||
|
{
|
||||||
|
if (!gr.is_string())
|
||||||
|
{
|
||||||
|
result = (cli_result_t){ .err = EINVAL, .text = "Group names must be strings" };
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!groups.is_null())
|
||||||
|
{
|
||||||
|
result = (cli_result_t){ .err = EINVAL, .text = "Group names must be strings" };
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (user_name == "")
|
||||||
|
{
|
||||||
|
result = (cli_result_t){ .err = EINVAL, .text = "User name must not be empty" };
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
parent->etcd_txn(json11::Json::object {
|
||||||
|
{ "success", json11::Json::array { json11::Json::object {
|
||||||
|
{ "request_range", json11::Json::object {
|
||||||
|
{ "key", base64_encode(parent->cli->st_cli.etcd_prefix+"/config/user/"+user_name) },
|
||||||
|
} },
|
||||||
|
} } }
|
||||||
|
});
|
||||||
|
state = 1;
|
||||||
|
resume_1:
|
||||||
|
if (parent->waiting > 0)
|
||||||
|
return;
|
||||||
|
if (parent->etcd_err.err)
|
||||||
|
{
|
||||||
|
result = parent->etcd_err;
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
kv = parent->cli->st_cli.parse_etcd_kv(parent->etcd_result["responses"][0]["response_range"]["kvs"][0]);
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
new_cfg = kv.value.object_items();
|
||||||
|
if (!kv.mod_revision && del)
|
||||||
|
{
|
||||||
|
result = (cli_result_t){ .err = ENOENT, .text = "User "+user_name+" does not exist" };
|
||||||
|
state = 100;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!groups.is_null())
|
||||||
|
new_cfg["groups"] = groups;
|
||||||
|
if (user_type != "")
|
||||||
|
new_cfg["type"] = user_type;
|
||||||
|
if (!new_cfg["type"].is_string())
|
||||||
|
new_cfg["type"] = "client";
|
||||||
|
parent->etcd_txn(json11::Json::object {
|
||||||
|
{ "compare", json11::Json::array { json11::Json::object {
|
||||||
|
{ "key", base64_encode(parent->cli->st_cli.etcd_prefix+"/config/user/"+user_name) },
|
||||||
|
{ "target", kv.mod_revision ? "MOD" : "VERSION" },
|
||||||
|
{ kv.mod_revision ? "mod_revision" : "version", kv.mod_revision },
|
||||||
|
} } },
|
||||||
|
{ "success", json11::Json::array {
|
||||||
|
del ? json11::Json::object { { "request_delete_range", json11::Json::object {
|
||||||
|
{ "key", base64_encode(parent->cli->st_cli.etcd_prefix+"/config/user/"+user_name) },
|
||||||
|
} } } : json11::Json::object { { "request_put", json11::Json::object {
|
||||||
|
{ "key", base64_encode(parent->cli->st_cli.etcd_prefix+"/config/user/"+user_name) },
|
||||||
|
{ "value", base64_encode(json11::Json(new_cfg).dump()) }
|
||||||
|
} } },
|
||||||
|
} },
|
||||||
|
{ "failure", json11::Json::array { json11::Json::object {
|
||||||
|
{ "request_range", json11::Json::object {
|
||||||
|
{ "key", base64_encode(parent->cli->st_cli.etcd_prefix+"/config/user/"+user_name) },
|
||||||
|
} },
|
||||||
|
} } },
|
||||||
|
});
|
||||||
|
state = 2;
|
||||||
|
resume_2:
|
||||||
|
if (parent->waiting > 0)
|
||||||
|
return;
|
||||||
|
if (parent->etcd_err.err)
|
||||||
|
{
|
||||||
|
result = parent->etcd_err;
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (parent->etcd_result["succeeded"].bool_value())
|
||||||
|
break;
|
||||||
|
kv = parent->cli->st_cli.parse_etcd_kv(parent->etcd_result["responses"][0]["response_range"]["kvs"][0]);
|
||||||
|
}
|
||||||
|
state = 100;
|
||||||
|
result.text = del ? "User "+user_name+" removed" : "User "+user_name+" modified";
|
||||||
|
new_cfg["name"] = user_name;
|
||||||
|
result.data = del ? json11::Json::object{ { "ok", true } } : new_cfg;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::function<bool(cli_result_t &)> cli_tool_t::start_modify_user(json11::Json cfg)
|
||||||
|
{
|
||||||
|
auto creator = new cli_modify_user_t();
|
||||||
|
creator->parent = this;
|
||||||
|
creator->user_name = cfg["name"].string_value();
|
||||||
|
creator->user_type = cfg["type"].string_value();
|
||||||
|
creator->groups = cfg["groups"];
|
||||||
|
creator->del = cfg["remove"].bool_value();
|
||||||
|
return [creator](cli_result_t & result)
|
||||||
|
{
|
||||||
|
creator->loop();
|
||||||
|
if (creator->is_done())
|
||||||
|
{
|
||||||
|
result = creator->result;
|
||||||
|
delete creator;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
+26
-18
@@ -41,7 +41,7 @@ struct osd_tree_printer_t
|
|||||||
{
|
{
|
||||||
cli_tool_t *parent;
|
cli_tool_t *parent;
|
||||||
json11::Json cfg;
|
json11::Json cfg;
|
||||||
bool flat = false;
|
bool as_tree = false;
|
||||||
bool show_stats = false;
|
bool show_stats = false;
|
||||||
|
|
||||||
int state = 0;
|
int state = 0;
|
||||||
@@ -209,11 +209,14 @@ resume_1:
|
|||||||
for (int i = 1; i < node_seq.size(); i++)
|
for (int i = 1; i < node_seq.size(); i++)
|
||||||
{
|
{
|
||||||
auto & node = placement_tree->nodes.at(node_seq[i]);
|
auto & node = placement_tree->nodes.at(node_seq[i]);
|
||||||
fmt_items.push_back(json11::Json::object{
|
if (as_tree)
|
||||||
{ "type", node.level },
|
{
|
||||||
{ "name", node.name },
|
fmt_items.push_back(json11::Json::object{
|
||||||
{ "parent", node.parent },
|
{ "type", node.level },
|
||||||
});
|
{ "name", node.name },
|
||||||
|
{ "parent", node.parent },
|
||||||
|
});
|
||||||
|
}
|
||||||
for (uint64_t osd_num: node.child_osds)
|
for (uint64_t osd_num: node.child_osds)
|
||||||
{
|
{
|
||||||
auto & osd = placement_tree->osds.at(osd_num);
|
auto & osd = placement_tree->osds.at(osd_num);
|
||||||
@@ -221,17 +224,22 @@ resume_1:
|
|||||||
{ "type", "osd" },
|
{ "type", "osd" },
|
||||||
{ "name", osd.num },
|
{ "name", osd.num },
|
||||||
{ "parent", node.name },
|
{ "parent", node.name },
|
||||||
{ "up", osd.up ? "up" : "down" },
|
{ "up", osd.up },
|
||||||
{ "size", osd.size },
|
{ "size", osd.size },
|
||||||
{ "free", osd.free },
|
{ "free", osd.free },
|
||||||
{ "reweight", osd.reweight },
|
{ "reweight", osd.reweight },
|
||||||
{ "noout", osd.noout },
|
{ "noout", osd.noout },
|
||||||
{ "tags", osd.tags },
|
{ "tags", osd.tags },
|
||||||
{ "block", (uint64_t)osd.block_size },
|
{ "data_block_size", (uint64_t)osd.block_size },
|
||||||
{ "bitmap", (uint64_t)osd.bitmap_granularity },
|
{ "bitmap_granularity", (uint64_t)osd.bitmap_granularity },
|
||||||
{ "commit", osd.immediate_commit == IMMEDIATE_NONE ? "none" : (osd.immediate_commit == IMMEDIATE_ALL ? "all" : "small") },
|
{ "immediate_commit", osd.immediate_commit == IMMEDIATE_NONE ? "none" : (osd.immediate_commit == IMMEDIATE_ALL ? "all" : "small") },
|
||||||
{ "op_stats", osd_stats[osd_num]["op_stats"] },
|
|
||||||
};
|
};
|
||||||
|
if (show_stats)
|
||||||
|
{
|
||||||
|
json_osd["op_stats"] = osd_stats[osd_num]["op_stats"];
|
||||||
|
json_osd["subop_stats"] = osd_stats[osd_num]["subop_stats"];
|
||||||
|
json_osd["recovery_stats"] = osd_stats[osd_num]["recovery_stats"];
|
||||||
|
}
|
||||||
if (osd_stats[osd_num]["slow_ops_primary"].uint64_value() > 0)
|
if (osd_stats[osd_num]["slow_ops_primary"].uint64_value() > 0)
|
||||||
{
|
{
|
||||||
json_osd["slow_ops_primary"] = osd_stats[osd_num]["slow_ops_primary"];
|
json_osd["slow_ops_primary"] = osd_stats[osd_num]["slow_ops_primary"];
|
||||||
@@ -249,7 +257,7 @@ resume_1:
|
|||||||
for (int i = 1; i < node_seq.size(); i++)
|
for (int i = 1; i < node_seq.size(); i++)
|
||||||
{
|
{
|
||||||
auto & node = placement_tree->nodes.at(node_seq[i]);
|
auto & node = placement_tree->nodes.at(node_seq[i]);
|
||||||
if (!flat)
|
if (as_tree)
|
||||||
{
|
{
|
||||||
fmt_items.push_back(json11::Json::object{
|
fmt_items.push_back(json11::Json::object{
|
||||||
{ "type", str_repeat(" ", indents[i]) + node.level },
|
{ "type", str_repeat(" ", indents[i]) + node.level },
|
||||||
@@ -257,7 +265,7 @@ resume_1:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
std::string parent = node.name;
|
std::string parent = node.name;
|
||||||
if (flat)
|
if (!as_tree)
|
||||||
{
|
{
|
||||||
auto cur = &placement_tree->nodes.at(node.name);
|
auto cur = &placement_tree->nodes.at(node.name);
|
||||||
while (cur->parent != "" && cur->parent != node.name)
|
while (cur->parent != "" && cur->parent != node.name)
|
||||||
@@ -270,7 +278,7 @@ resume_1:
|
|||||||
{
|
{
|
||||||
auto & osd = placement_tree->osds.at(osd_num);
|
auto & osd = placement_tree->osds.at(osd_num);
|
||||||
auto fmt = json11::Json::object{
|
auto fmt = json11::Json::object{
|
||||||
{ "type", (flat ? "osd" : str_repeat(" ", indents[i]+1) + "osd") },
|
{ "type", (!as_tree ? "osd" : str_repeat(" ", indents[i]+1) + "osd") },
|
||||||
{ "name", osd.num },
|
{ "name", osd.num },
|
||||||
{ "parent", parent },
|
{ "parent", parent },
|
||||||
{ "up", osd.up ? "up" : "down" },
|
{ "up", osd.up ? "up" : "down" },
|
||||||
@@ -300,7 +308,7 @@ resume_1:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
json11::Json::array cols;
|
json11::Json::array cols;
|
||||||
if (!flat)
|
if (as_tree)
|
||||||
{
|
{
|
||||||
cols.push_back(json11::Json::object{
|
cols.push_back(json11::Json::object{
|
||||||
{ "key", "type" },
|
{ "key", "type" },
|
||||||
@@ -309,9 +317,9 @@ resume_1:
|
|||||||
}
|
}
|
||||||
cols.push_back(json11::Json::object{
|
cols.push_back(json11::Json::object{
|
||||||
{ "key", "name" },
|
{ "key", "name" },
|
||||||
{ "title", flat ? "OSD" : "NAME" },
|
{ "title", !as_tree ? "OSD" : "NAME" },
|
||||||
});
|
});
|
||||||
if (flat)
|
if (!as_tree)
|
||||||
{
|
{
|
||||||
cols.push_back(json11::Json::object{
|
cols.push_back(json11::Json::object{
|
||||||
{ "key", "parent" },
|
{ "key", "parent" },
|
||||||
@@ -414,7 +422,7 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_osd_tree(json11::Json cfg)
|
|||||||
auto osd_tree_printer = new osd_tree_printer_t();
|
auto osd_tree_printer = new osd_tree_printer_t();
|
||||||
osd_tree_printer->parent = this;
|
osd_tree_printer->parent = this;
|
||||||
osd_tree_printer->cfg = cfg;
|
osd_tree_printer->cfg = cfg;
|
||||||
osd_tree_printer->flat = cfg["flat"].bool_value();
|
osd_tree_printer->as_tree = cfg["as_tree"].bool_value();
|
||||||
osd_tree_printer->show_stats = cfg["long"].bool_value();
|
osd_tree_printer->show_stats = cfg["long"].bool_value();
|
||||||
return [osd_tree_printer](cli_result_t & result)
|
return [osd_tree_printer](cli_result_t & result)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -282,10 +282,16 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_pg_list(json11::Json cfg)
|
|||||||
{
|
{
|
||||||
auto pg_lister = new pg_lister_t();
|
auto pg_lister = new pg_lister_t();
|
||||||
pg_lister->parent = this;
|
pg_lister->parent = this;
|
||||||
if (cfg["pool"].uint64_value())
|
if (!cfg["pool"].is_null())
|
||||||
|
{
|
||||||
pg_lister->pool_id = cfg["pool"].uint64_value();
|
pg_lister->pool_id = cfg["pool"].uint64_value();
|
||||||
|
pg_lister->pool_name = pg_lister->pool_id ? "" : cfg["pool"].string_value();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
pg_lister->pool_name = cfg["pool"].string_value();
|
{
|
||||||
|
pg_lister->pool_id = cfg["pool_id"].uint64_value();
|
||||||
|
pg_lister->pool_name = pg_lister->pool_id ? "" : cfg["pool_name"].string_value();
|
||||||
|
}
|
||||||
for (auto & st: cfg["pg_state"].array_items())
|
for (auto & st: cfg["pg_state"].array_items())
|
||||||
pg_lister->pg_state.push_back(st.string_value());
|
pg_lister->pg_state.push_back(st.string_value());
|
||||||
if (cfg["pg_state"].is_string())
|
if (cfg["pg_state"].is_string())
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ std::string validate_pool_config(json11::Json::object & new_cfg, json11::Json ol
|
|||||||
}
|
}
|
||||||
else if (key == "name" || key == "scheme" || key == "immediate_commit" ||
|
else if (key == "name" || key == "scheme" || key == "immediate_commit" ||
|
||||||
key == "failure_domain" || key == "root_node" || key == "scrub_interval" || key == "used_for_app" ||
|
key == "failure_domain" || key == "root_node" || key == "scrub_interval" || key == "used_for_app" ||
|
||||||
key == "used_for_fs" || key == "raw_placement" || key == "local_reads")
|
key == "used_for_fs" || key == "raw_placement" || key == "local_reads" || key == "creator_group")
|
||||||
{
|
{
|
||||||
if (!value.is_string())
|
if (!value.is_string())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ resume_3:
|
|||||||
if (failure_domain != "osd")
|
if (failure_domain != "osd")
|
||||||
pool_err += "\n- different parent '"+failure_domain+"' nodes";
|
pool_err += "\n- different parent '"+failure_domain+"' nodes";
|
||||||
result = (cli_result_t){
|
result = (cli_result_t){
|
||||||
.err = EINVAL,
|
.err = EBUSY,
|
||||||
.text = pool_err,
|
.text = pool_err,
|
||||||
};
|
};
|
||||||
state = 100;
|
state = 100;
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ resume_1:
|
|||||||
{ "space_efficiency", pool_stats[pool_cfg.id]["space_efficiency"].number_value() },
|
{ "space_efficiency", pool_stats[pool_cfg.id]["space_efficiency"].number_value() },
|
||||||
{ "pg_real_size", pool_stats[pool_cfg.id]["pg_real_size"].uint64_value() },
|
{ "pg_real_size", pool_stats[pool_cfg.id]["pg_real_size"].uint64_value() },
|
||||||
{ "osd_count", (uint64_t)pg_per_osd.size() },
|
{ "osd_count", (uint64_t)pg_per_osd.size() },
|
||||||
{ "backfillfull", pool_cfg.backfillfull },
|
{ "backfillfull", !!pool_cfg.backfillfull },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Include full pool config
|
// Include full pool config
|
||||||
@@ -546,6 +546,10 @@ resume_3:
|
|||||||
{ "write_fmt", "Write" },
|
{ "write_fmt", "Write" },
|
||||||
{ "delete_fmt", "Delete" },
|
{ "delete_fmt", "Delete" },
|
||||||
};
|
};
|
||||||
|
if (sort_field == "osd_tags" || sort_field == "primary_affinity_tags")
|
||||||
|
{
|
||||||
|
sort_field += "_fmt";
|
||||||
|
}
|
||||||
auto list = to_list();
|
auto list = to_list();
|
||||||
size_t title_len = 0;
|
size_t title_len = 0;
|
||||||
for (auto & item: list)
|
for (auto & item: list)
|
||||||
@@ -666,15 +670,12 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_pool_ls(json11::Json cfg)
|
|||||||
lister->show_stats = cfg["long"].bool_value();
|
lister->show_stats = cfg["long"].bool_value();
|
||||||
lister->detailed = cfg["detail"].bool_value();
|
lister->detailed = cfg["detail"].bool_value();
|
||||||
lister->sort_field = cfg["sort"].string_value();
|
lister->sort_field = cfg["sort"].string_value();
|
||||||
if ((lister->sort_field == "osd_tags") ||
|
|
||||||
(lister->sort_field == "primary_affinity_tags" ))
|
|
||||||
lister->sort_field = lister->sort_field + "_fmt";
|
|
||||||
lister->reverse = cfg["reverse"].bool_value();
|
lister->reverse = cfg["reverse"].bool_value();
|
||||||
lister->max_count = cfg["count"].uint64_value();
|
lister->max_count = cfg["count"].uint64_value();
|
||||||
|
if (cfg["names"].is_string())
|
||||||
|
lister->only_names.insert(cfg["names"].string_value());
|
||||||
for (auto & item: cfg["names"].array_items())
|
for (auto & item: cfg["names"].array_items())
|
||||||
{
|
|
||||||
lister->only_names.insert(item.string_value());
|
lister->only_names.insert(item.string_value());
|
||||||
}
|
|
||||||
return [lister](cli_result_t & result)
|
return [lister](cli_result_t & result)
|
||||||
{
|
{
|
||||||
lister->loop();
|
lister->loop();
|
||||||
|
|||||||
+15
-10
@@ -39,16 +39,21 @@ struct pool_changer_t
|
|||||||
goto resume_1;
|
goto resume_1;
|
||||||
else if (state == 2)
|
else if (state == 2)
|
||||||
goto resume_2;
|
goto resume_2;
|
||||||
pool_id = stoull_full(cfg["old_name"].string_value());
|
if (!cfg["pool"].is_null())
|
||||||
if (!pool_id)
|
|
||||||
{
|
{
|
||||||
pool_name = cfg["old_name"].string_value();
|
pool_id = cfg["pool"].uint64_value();
|
||||||
if (pool_name == "")
|
pool_name = pool_id ? "" : cfg["pool"].string_value();
|
||||||
{
|
}
|
||||||
result = (cli_result_t){ .err = ENOENT, .text = "Pool ID or name is required to modify it" };
|
else
|
||||||
state = 100;
|
{
|
||||||
return;
|
pool_id = cfg["id"].uint64_value();
|
||||||
}
|
pool_name = pool_id ? "" : cfg["old_name"].string_value();
|
||||||
|
}
|
||||||
|
if (!pool_id && pool_name == "")
|
||||||
|
{
|
||||||
|
result = (cli_result_t){ .err = ENOENT, .text = "Pool ID or name is required to modify it" };
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
resume_0:
|
resume_0:
|
||||||
// Get pools from etcd
|
// Get pools from etcd
|
||||||
@@ -179,7 +184,7 @@ resume_2:
|
|||||||
result = (cli_result_t){
|
result = (cli_result_t){
|
||||||
.err = 0,
|
.err = 0,
|
||||||
.text = "Pool "+pool_name+" updated",
|
.text = "Pool "+pool_name+" updated",
|
||||||
.data = new_pools,
|
.data = new_pools[std::to_string(pool_id)],
|
||||||
};
|
};
|
||||||
state = 100;
|
state = 100;
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-3
@@ -196,7 +196,9 @@ resume_3:
|
|||||||
result = (cli_result_t){
|
result = (cli_result_t){
|
||||||
.err = 0,
|
.err = 0,
|
||||||
.text = "Pool "+pool_name+" deleted",
|
.text = "Pool "+pool_name+" deleted",
|
||||||
.data = new_pools
|
.data = json11::Json::object{
|
||||||
|
{"ok", true},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
state = 100;
|
state = 100;
|
||||||
}
|
}
|
||||||
@@ -207,8 +209,16 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_pool_rm(json11::Json cfg)
|
|||||||
auto pool_remover = new pool_remover_t();
|
auto pool_remover = new pool_remover_t();
|
||||||
pool_remover->parent = this;
|
pool_remover->parent = this;
|
||||||
|
|
||||||
pool_remover->pool_id = cfg["pool"].uint64_value();
|
if (!cfg["pool"].is_null())
|
||||||
pool_remover->pool_name = pool_remover->pool_id ? "" : cfg["pool"].as_string();
|
{
|
||||||
|
pool_remover->pool_id = cfg["pool"].uint64_value();
|
||||||
|
pool_remover->pool_name = pool_remover->pool_id ? "" : cfg["pool"].as_string();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pool_remover->pool_id = cfg["pool_id"].uint64_value();
|
||||||
|
pool_remover->pool_name = pool_remover->pool_id ? "" : cfg["pool_name"].as_string();
|
||||||
|
}
|
||||||
|
|
||||||
pool_remover->force = !cfg["force"].is_null();
|
pool_remover->force = !cfg["force"].is_null();
|
||||||
|
|
||||||
|
|||||||
@@ -269,10 +269,17 @@ resume_100:
|
|||||||
{
|
{
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
snprintf(buf, 1024, "Parent inode of layer %s (id 0x%jx) not found", cur->name.c_str(), cur->parent_id);
|
snprintf(buf, 1024, "Parent inode of layer %s (id 0x%jx) not found", cur->name.c_str(), cur->parent_id);
|
||||||
|
result = (cli_result_t){ .err = ENOENT, .text = buf };
|
||||||
state = 100;
|
state = 100;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cur = &it->second;
|
cur = &it->second;
|
||||||
|
if (!parent->check_image_perm(*cur, true))
|
||||||
|
{
|
||||||
|
result = (cli_result_t){ .err = EACCES, .text = "Image permission denied" };
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
chain_list.push_back(cur->num);
|
chain_list.push_back(cur->num);
|
||||||
}
|
}
|
||||||
if (cur->num != from_cfg->num)
|
if (cur->num != from_cfg->num)
|
||||||
@@ -297,6 +304,12 @@ resume_100:
|
|||||||
auto it = sources.find(ic.second.parent_id);
|
auto it = sources.find(ic.second.parent_id);
|
||||||
if (it != sources.end() && sources.find(ic.second.num) == sources.end())
|
if (it != sources.end() && sources.find(ic.second.num) == sources.end())
|
||||||
{
|
{
|
||||||
|
if (!parent->check_image_perm(ic.second, true))
|
||||||
|
{
|
||||||
|
result = (cli_result_t){ .err = EACCES, .text = "Image permission denied" };
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
merge_children.push_back(ic.second.num);
|
merge_children.push_back(ic.second.num);
|
||||||
if (ic.second.readonly || writers_stopped)
|
if (ic.second.readonly || writers_stopped)
|
||||||
{
|
{
|
||||||
@@ -451,6 +464,7 @@ resume_100:
|
|||||||
inode_config_t new_cfg = *child_cfg;
|
inode_config_t new_cfg = *child_cfg;
|
||||||
new_cfg.deleted = false;
|
new_cfg.deleted = false;
|
||||||
new_cfg.num = target_cfg->num;
|
new_cfg.num = target_cfg->num;
|
||||||
|
new_cfg.enc_key = target_cfg->enc_key;
|
||||||
new_cfg.parent_id = new_parent;
|
new_cfg.parent_id = new_parent;
|
||||||
json11::Json::array cmp = json11::Json::array {
|
json11::Json::array cmp = json11::Json::array {
|
||||||
json11::Json::object {
|
json11::Json::object {
|
||||||
|
|||||||
@@ -264,7 +264,7 @@ struct rm_inode_t
|
|||||||
{
|
{
|
||||||
// Error
|
// Error
|
||||||
result = (cli_result_t){
|
result = (cli_result_t){
|
||||||
.err = EIO,
|
.err = EBUSY,
|
||||||
.text = "Failed: some blocks were not removed",
|
.text = "Failed: some blocks were not removed",
|
||||||
.data = data,
|
.data = data,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -181,6 +181,7 @@ struct rm_osd_t
|
|||||||
if (dry_run || is_dataloss && !force_dataloss || is_warning && !force_warning)
|
if (dry_run || is_dataloss && !force_dataloss || is_warning && !force_warning)
|
||||||
{
|
{
|
||||||
result.err = is_dataloss && !force_dataloss || is_warning && !force_warning ? EBUSY : 0;
|
result.err = is_dataloss && !force_dataloss || is_warning && !force_warning ? EBUSY : 0;
|
||||||
|
result.data = json11::Json::object{ { "pool_effects", pool_effects } };
|
||||||
state = 100;
|
state = 100;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -332,6 +333,9 @@ struct rm_osd_t
|
|||||||
ids = (osd_ids.size() > 1 ? "OSDs " : "OSD ")+ids+(osd_ids.size() > 1 ? " are" : " is")+" removed from etcd";
|
ids = (osd_ids.size() > 1 ? "OSDs " : "OSD ")+ids+(osd_ids.size() > 1 ? " are" : " is")+" removed from etcd";
|
||||||
state = 100;
|
state = 100;
|
||||||
result.text = (result.text != "" ? ids+"\n"+result.text : ids);
|
result.text = (result.text != "" ? ids+"\n"+result.text : ids);
|
||||||
|
result.data = json11::Json::object {
|
||||||
|
{"deleted_osds", osd_ids},
|
||||||
|
};
|
||||||
result.err = 0;
|
result.err = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -195,6 +195,8 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_rm_wildcard(json11::Json c
|
|||||||
auto wildcard_remover = new wildcard_remover_t();
|
auto wildcard_remover = new wildcard_remover_t();
|
||||||
wildcard_remover->parent = this;
|
wildcard_remover->parent = this;
|
||||||
wildcard_remover->cfg = cfg;
|
wildcard_remover->cfg = cfg;
|
||||||
|
if (cfg["globs"].is_string())
|
||||||
|
wildcard_remover->globs.push_back(cfg["globs"].string_value());
|
||||||
for (auto & glob: cfg["globs"].array_items())
|
for (auto & glob: cfg["globs"].array_items())
|
||||||
wildcard_remover->globs.push_back(glob.string_value());
|
wildcard_remover->globs.push_back(glob.string_value());
|
||||||
wildcard_remover->exact = cfg["exact"].bool_value();
|
wildcard_remover->exact = cfg["exact"].bool_value();
|
||||||
|
|||||||
@@ -0,0 +1,491 @@
|
|||||||
|
// Copyright (c) Vitaliy Filippov, 2019+
|
||||||
|
// License: VNPL-1.1 (see README.md for details)
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include "cli.h"
|
||||||
|
#include "cluster_client.h"
|
||||||
|
#include "epoll_manager.h"
|
||||||
|
#include "http_client.h"
|
||||||
|
#include "str_util.h"
|
||||||
|
#include "json_util.h"
|
||||||
|
#include "addr_util.h"
|
||||||
|
#include "openapi.json.h"
|
||||||
|
|
||||||
|
struct cli_serve_conn_t
|
||||||
|
{
|
||||||
|
int peer_fd = 0;
|
||||||
|
std::string peer_addr;
|
||||||
|
http_co_t *co = NULL;
|
||||||
|
cli_tool_t *p = NULL;
|
||||||
|
cli_result_t result;
|
||||||
|
bool keepalive = false;
|
||||||
|
bool closed = false;
|
||||||
|
timespec request_time;
|
||||||
|
std::string request_method;
|
||||||
|
std::string request_path;
|
||||||
|
std::string request_body;
|
||||||
|
std::string response_type;
|
||||||
|
std::function<bool(cli_result_t &)> action_cb;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cli_serve_path_t
|
||||||
|
{
|
||||||
|
std::string cmd;
|
||||||
|
bool allow_get;
|
||||||
|
bool allow_client;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Serve vitastor-cli commands over HTTP in JSON format
|
||||||
|
struct cli_serve_t
|
||||||
|
{
|
||||||
|
std::map<std::string, cli_serve_path_t> cmd_paths = {
|
||||||
|
{"data/delete", {"rm-data", false, false}},
|
||||||
|
{"data/describe", {"describe", true, false}},
|
||||||
|
{"data/fix", {"fix", false, false}},
|
||||||
|
{"data/merge", {"merge-data", false, false}},
|
||||||
|
{"image/create", {"create", false, true}},
|
||||||
|
{"image/delete", {"rm", false, true}},
|
||||||
|
{"image/flatten", {"flatten", false, true}},
|
||||||
|
{"image/list", {"ls", true, true}},
|
||||||
|
{"image/modify", {"modify", false, true}},
|
||||||
|
{"osd/alloc", {"alloc-osd", false, false}},
|
||||||
|
{"osd/delete", {"rm-osd", false, false}},
|
||||||
|
{"osd/list", {"ls-osd", true, false}},
|
||||||
|
{"osd/modify", {"modify-osd", false, false}},
|
||||||
|
{"pg/list", {"ls-pgs", true, false}},
|
||||||
|
{"pool/create", {"create-pool", false, false}},
|
||||||
|
{"pool/delete", {"rm-pool", false, false}},
|
||||||
|
{"pool/list", {"pools", true, false}},
|
||||||
|
{"pool/modify", {"modify-pool", false, false}},
|
||||||
|
{"user/delete", {"remove-user", false, false}},
|
||||||
|
{"user/list", {"ls-user", false, false}},
|
||||||
|
{"user/modify", {"modify-user", false, false}},
|
||||||
|
{"status", {"status", true, false}},
|
||||||
|
};
|
||||||
|
|
||||||
|
cli_tool_t *parent = NULL;
|
||||||
|
json11::Json options;
|
||||||
|
cli_result_t result;
|
||||||
|
|
||||||
|
bool log_body = false;
|
||||||
|
bool stop = false;
|
||||||
|
std::vector<std::string> bind_addresses;
|
||||||
|
int port = 0;
|
||||||
|
int listen_backlog = 0;
|
||||||
|
bool ssl = false;
|
||||||
|
std::vector<int> listen_fds;
|
||||||
|
http_context_t *http_ctx = NULL;
|
||||||
|
std::set<cli_serve_conn_t*> connections;
|
||||||
|
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
bool is_done()
|
||||||
|
{
|
||||||
|
return state == 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
if (state == 1)
|
||||||
|
goto resume_1;
|
||||||
|
else if (state == 2)
|
||||||
|
goto resume_2;
|
||||||
|
else if (state == 100)
|
||||||
|
return;
|
||||||
|
if (options["bind_address"].is_string())
|
||||||
|
bind_addresses = explode(" ", options["bind_address"].string_value(), true);
|
||||||
|
else
|
||||||
|
bind_addresses.push_back("127.0.0.1");
|
||||||
|
port = options["port"].uint64_value();
|
||||||
|
if (!port)
|
||||||
|
port = 8080;
|
||||||
|
else if (port < 0 || port > 65535)
|
||||||
|
{
|
||||||
|
result = (cli_result_t){ .err = EINVAL, .text = "HTTP port can't be larger than 65536" };
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
listen_backlog = options["listen_backlog"].uint64_value();
|
||||||
|
if (!listen_backlog)
|
||||||
|
listen_backlog = 128;
|
||||||
|
ssl = json_is_true(options["ssl"]);
|
||||||
|
if (ssl)
|
||||||
|
{
|
||||||
|
std::string ssl_cert = options["ssl_cert"].string_value();
|
||||||
|
std::string ssl_key = options["ssl_key"].string_value();
|
||||||
|
std::string ssl_ca = options["ssl_ca"].string_value();
|
||||||
|
std::string error;
|
||||||
|
http_ctx = http_context_init(parent->epmgr->tfd, ssl_cert, ssl_key, ssl_ca, ssl_ca != "", error);
|
||||||
|
if (error != "")
|
||||||
|
{
|
||||||
|
result = (cli_result_t){ .err = EINVAL, .text = error };
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto & bind_address: bind_addresses)
|
||||||
|
{
|
||||||
|
int listen_fd = create_and_bind_socket(bind_address, port, listen_backlog, NULL);
|
||||||
|
fcntl(listen_fd, F_SETFL, fcntl(listen_fd, F_GETFL, 0) | O_NONBLOCK);
|
||||||
|
parent->epmgr->set_fd_handler(listen_fd, false, [this](int fd, int events)
|
||||||
|
{
|
||||||
|
accept_connections(fd);
|
||||||
|
});
|
||||||
|
listen_fds.push_back(listen_fd);
|
||||||
|
}
|
||||||
|
resume_1:
|
||||||
|
if (!stop)
|
||||||
|
{
|
||||||
|
state = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (auto conn: connections)
|
||||||
|
{
|
||||||
|
http_close(conn->co);
|
||||||
|
}
|
||||||
|
resume_2:
|
||||||
|
// Wait for all connections to finish
|
||||||
|
if (connections.size() > 0)
|
||||||
|
{
|
||||||
|
state = 2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (http_ctx)
|
||||||
|
{
|
||||||
|
http_context_destroy(http_ctx);
|
||||||
|
http_ctx = NULL;
|
||||||
|
}
|
||||||
|
state = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
void accept_connections(int listen_fd)
|
||||||
|
{
|
||||||
|
sockaddr_storage addr;
|
||||||
|
socklen_t peer_addr_size = sizeof(addr);
|
||||||
|
int peer_fd;
|
||||||
|
while ((peer_fd = accept(listen_fd, (sockaddr*)&addr, &peer_addr_size)) >= 0)
|
||||||
|
{
|
||||||
|
auto peer_addr_str = addr_to_string(addr);
|
||||||
|
assert(peer_fd != 0);
|
||||||
|
timespec ts;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &ts);
|
||||||
|
printf("[%s.%03ju] New connection %d from %s\n", format_datetime(ts.tv_sec).c_str(), (uint64_t)ts.tv_nsec/1000000,
|
||||||
|
peer_fd, peer_addr_str.c_str());
|
||||||
|
fcntl(peer_fd, F_SETFL, fcntl(peer_fd, F_GETFL, 0) | O_NONBLOCK);
|
||||||
|
int one = 1;
|
||||||
|
setsockopt(peer_fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
|
||||||
|
cli_serve_conn_t *conn = new cli_serve_conn_t;
|
||||||
|
conn->peer_fd = peer_fd;
|
||||||
|
conn->peer_addr = peer_addr_str;
|
||||||
|
conn->co = http_init(http_ctx);
|
||||||
|
http_serve(conn->co, peer_fd, (http_options_t){ .ssl = ssl }, [this, conn](http_message_t *msg)
|
||||||
|
{
|
||||||
|
process_request(conn, msg);
|
||||||
|
});
|
||||||
|
connections.insert(conn);
|
||||||
|
// Try to accept next connection
|
||||||
|
peer_addr_size = sizeof(addr);
|
||||||
|
}
|
||||||
|
if (peer_fd == -1 && errno != EAGAIN)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string("accept: ") + strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int map_to_http(int err, std::string *text)
|
||||||
|
{
|
||||||
|
int code = 0;
|
||||||
|
if (err == EINVAL)
|
||||||
|
{
|
||||||
|
code = 400;
|
||||||
|
if (text)
|
||||||
|
*text = "Bad Request";
|
||||||
|
}
|
||||||
|
else if (err == EACCES)
|
||||||
|
{
|
||||||
|
code = 403;
|
||||||
|
if (text)
|
||||||
|
*text = "Forbidden";
|
||||||
|
}
|
||||||
|
else if (err == EOPNOTSUPP)
|
||||||
|
{
|
||||||
|
code = 404;
|
||||||
|
if (text)
|
||||||
|
*text = "Not Found";
|
||||||
|
}
|
||||||
|
else if (err == ENOSYS)
|
||||||
|
{
|
||||||
|
code = 405;
|
||||||
|
if (text)
|
||||||
|
*text = "Method Not Allowed";
|
||||||
|
}
|
||||||
|
else if (err == EAGAIN)
|
||||||
|
{
|
||||||
|
code = 409;
|
||||||
|
if (text)
|
||||||
|
*text = "Update Conflict";
|
||||||
|
}
|
||||||
|
else if (err == ENOTEMPTY || err == EEXIST || err == ENOENT || err == EBUSY)
|
||||||
|
{
|
||||||
|
code = 412;
|
||||||
|
if (text)
|
||||||
|
*text = "Precondition Failed";
|
||||||
|
}
|
||||||
|
else /*if (err == EIO || err == EBADF)*/
|
||||||
|
{
|
||||||
|
code = 500;
|
||||||
|
if (text)
|
||||||
|
*text = "Internal Server Error";
|
||||||
|
}
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string cli_http_response(cli_serve_conn_t *conn)
|
||||||
|
{
|
||||||
|
timespec now;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &now);
|
||||||
|
int code = 200;
|
||||||
|
std::string response;
|
||||||
|
if (conn->result.err)
|
||||||
|
{
|
||||||
|
std::string status_line;
|
||||||
|
code = map_to_http(conn->result.err, &status_line);
|
||||||
|
response = "HTTP/1.1 "+std::to_string(code)+" "+status_line+"\r\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response = "HTTP/1.1 200 OK\r\n";
|
||||||
|
}
|
||||||
|
response += (conn->keepalive
|
||||||
|
? "Connection: keep-alive\r\n"
|
||||||
|
: "Connection: close\r\n");
|
||||||
|
std::string body;
|
||||||
|
if (!conn->result.data.is_null())
|
||||||
|
{
|
||||||
|
response += "Content-Type: application/json\r\n";
|
||||||
|
body = conn->result.data.dump();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!conn->response_type.empty())
|
||||||
|
response += "Content-Type: "+conn->response_type+"\r\n";
|
||||||
|
else
|
||||||
|
response += "Content-Type: text/plain; charset=utf-8\r\n";
|
||||||
|
body = conn->result.text;
|
||||||
|
}
|
||||||
|
response += "Content-Length: "+std::to_string(body.size())+"\r\n\r\n";
|
||||||
|
response += body;
|
||||||
|
if (conn->request_method.find("\n") != std::string::npos)
|
||||||
|
conn->request_method = str_replace(conn->request_method, "\n", "%0a");
|
||||||
|
if (conn->request_method.find(" ") != std::string::npos)
|
||||||
|
conn->request_method = str_replace(conn->request_method, " ", "%20");
|
||||||
|
if (conn->request_path.find("\n") != std::string::npos)
|
||||||
|
conn->request_path = str_replace(conn->request_path, "\n", "%0a");
|
||||||
|
if (conn->request_path.find(" ") != std::string::npos)
|
||||||
|
conn->request_path = str_replace(conn->request_path, " ", "%20");
|
||||||
|
uint64_t response_time = (now.tv_sec-conn->request_time.tv_sec)*1000 + (now.tv_nsec-conn->request_time.tv_nsec)/1000000;
|
||||||
|
printf("[%s.%03ju] %s %s %s %d %.03f sec\n",
|
||||||
|
format_datetime(now.tv_sec).c_str(), (uint64_t)now.tv_nsec/1000000,
|
||||||
|
conn->peer_addr.c_str(), conn->request_method.c_str(), conn->request_path.c_str(), code,
|
||||||
|
response_time/1000.0);
|
||||||
|
if (log_body)
|
||||||
|
{
|
||||||
|
if (conn->request_body.find("\n") != std::string::npos)
|
||||||
|
conn->request_body = str_replace(conn->request_body, "\n", " ");
|
||||||
|
if (conn->request_body.size())
|
||||||
|
printf(" %s\n", conn->request_body.c_str());
|
||||||
|
printf(" %s\n", body.c_str());
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_request(cli_serve_conn_t *conn, http_message_t *msg)
|
||||||
|
{
|
||||||
|
timespec ts;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &ts);
|
||||||
|
if (!msg->error.empty())
|
||||||
|
{
|
||||||
|
// connection is closed
|
||||||
|
fprintf(stderr, "[%s.%03ju] Connection %d closed: %s\n", format_datetime(ts.tv_sec).c_str(),
|
||||||
|
(uint64_t)ts.tv_nsec/1000000, conn->peer_fd, msg->error.c_str());
|
||||||
|
if (conn->p)
|
||||||
|
conn->closed = true;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
connections.erase(conn);
|
||||||
|
http_destroy(conn->co);
|
||||||
|
delete conn;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
conn->keepalive = msg->headers.find("connection") != msg->headers.end() &&
|
||||||
|
msg->headers.at("connection") == "keep-alive";
|
||||||
|
conn->p = new cli_tool_t;
|
||||||
|
conn->p->iodepth = parent->iodepth;
|
||||||
|
conn->p->parallel_osds = parent->parallel_osds;
|
||||||
|
conn->p->json_output = true;
|
||||||
|
conn->p->ringloop = parent->ringloop;
|
||||||
|
conn->p->epmgr = parent->epmgr;
|
||||||
|
conn->p->cli = parent->cli;
|
||||||
|
conn->p->is_command_line = false;
|
||||||
|
// Parse request
|
||||||
|
auto req_line = explode(" ", msg->status_line, true);
|
||||||
|
if (req_line.size() < 2)
|
||||||
|
{
|
||||||
|
if (req_line[0] == "")
|
||||||
|
req_line[0] = "-";
|
||||||
|
req_line.push_back("-");
|
||||||
|
}
|
||||||
|
conn->request_time = ts;
|
||||||
|
conn->request_method = std::move(req_line[0]);
|
||||||
|
conn->request_path = std::move(req_line[1]);
|
||||||
|
conn->request_body = std::move(msg->body);
|
||||||
|
conn->response_type = "";
|
||||||
|
if (parent->cli->st_cli.use_auth)
|
||||||
|
{
|
||||||
|
auto user = std::make_unique<cli_user_t>();
|
||||||
|
user->name = msg->headers["_tls_common_name"];
|
||||||
|
auto user_it = parent->cli->st_cli.user_info.find(user->name);
|
||||||
|
auto userinfo = user_it == parent->cli->st_cli.user_info.end() ? user_it->second : json11::Json();
|
||||||
|
user->type = user->name == "root" ? "admin" : userinfo["type"].string_value();
|
||||||
|
for (auto & gr: userinfo["groups"].array_items())
|
||||||
|
{
|
||||||
|
user->groups.insert(gr.string_value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto ctype = msg->headers["content-type"];
|
||||||
|
if (conn->request_method != "GET" && conn->request_method != "POST")
|
||||||
|
{
|
||||||
|
conn->result = { .err = ENOSYS, .text = "Unsupported request method "+conn->request_method };
|
||||||
|
}
|
||||||
|
else if (ctype != (conn->request_method == "GET" ? "" : "application/json"))
|
||||||
|
{
|
||||||
|
conn->result = { .err = EINVAL, .text = "Unsupported Content-Type: "+ctype+" for "+conn->request_method+" requests" };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto uri = explode("?", conn->request_path, true);
|
||||||
|
uri[0] = trim(uri[0], "/");
|
||||||
|
auto cmd_it = cmd_paths.find(uri[0]);
|
||||||
|
if (uri[0] == "")
|
||||||
|
{
|
||||||
|
std::string text = "Supported APIs:\n\n- GET /openapi\n";
|
||||||
|
for (auto & pp: cmd_paths)
|
||||||
|
{
|
||||||
|
text += (pp.second.allow_get ? "- GET" : "- POST") + (" /" + pp.first) + "\n";
|
||||||
|
}
|
||||||
|
conn->result = { .text = text };
|
||||||
|
}
|
||||||
|
else if (uri[0] == "openapi")
|
||||||
|
{
|
||||||
|
conn->response_type = "application/json";
|
||||||
|
conn->result = { .text = openapi_description };
|
||||||
|
if (parent->cli->st_cli.use_auth)
|
||||||
|
{
|
||||||
|
// Filter available paths by privileges
|
||||||
|
if (conn->p->user->type == "client")
|
||||||
|
{
|
||||||
|
std::string error;
|
||||||
|
auto openapi = json11::Json::parse(openapi_description, error).object_items();
|
||||||
|
json11::Json::object paths;
|
||||||
|
for (auto & kv: openapi["paths"].object_items())
|
||||||
|
{
|
||||||
|
auto cmd_it = cmd_paths.find(kv.first.substr(1));
|
||||||
|
if (cmd_it != cmd_paths.end() && cmd_it->second.allow_client)
|
||||||
|
{
|
||||||
|
paths[kv.first] = kv.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
openapi["paths"] = paths;
|
||||||
|
conn->response_type = "application/json";
|
||||||
|
conn->result = { .text = json11::Json(openapi).dump() };
|
||||||
|
}
|
||||||
|
else if (conn->p->user->type != "admin")
|
||||||
|
{
|
||||||
|
conn->response_type = "";
|
||||||
|
conn->result = { .err = EACCES, .text = "Access denied" };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (cmd_it == cmd_paths.end())
|
||||||
|
{
|
||||||
|
conn->result = { .err = EOPNOTSUPP, .text = "unknown command: "+uri[0] };
|
||||||
|
}
|
||||||
|
else if (conn->request_method == "GET" && !cmd_it->second.allow_get)
|
||||||
|
{
|
||||||
|
conn->result = { .err = ENOSYS, .text = "method /"+uri[0]+" only allows POST requests" };
|
||||||
|
}
|
||||||
|
else if (parent->cli->st_cli.use_auth && conn->p->user->type == "client" && !cmd_it->second.allow_client)
|
||||||
|
{
|
||||||
|
conn->result = { .err = EACCES, .text = "Access denied" };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string error;
|
||||||
|
json11::Json::object cfg;
|
||||||
|
if (conn->request_method == "POST")
|
||||||
|
{
|
||||||
|
cfg = json11::Json::parse(conn->request_body, error).object_items();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Parse URI
|
||||||
|
cfg = parse_uri_params(uri[1]);
|
||||||
|
}
|
||||||
|
if (error != "")
|
||||||
|
{
|
||||||
|
conn->result = { .err = EINVAL, .text = "Invalid JSON in body: "+error };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cfg["command"] = json11::Json::array{cmd_it->second.cmd};
|
||||||
|
conn->p->parse_api_opts(cfg);
|
||||||
|
conn->action_cb = conn->p->start(cfg, conn->result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!conn->action_cb)
|
||||||
|
{
|
||||||
|
http_reply(conn->co, cli_http_response(conn));
|
||||||
|
delete conn->p;
|
||||||
|
conn->p = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
conn->p->loop_and_wait(conn->action_cb, [this, conn](const cli_result_t & r)
|
||||||
|
{
|
||||||
|
conn->result = r;
|
||||||
|
conn->action_cb = NULL;
|
||||||
|
delete conn->p;
|
||||||
|
conn->p = NULL;
|
||||||
|
if (!conn->closed)
|
||||||
|
http_reply(conn->co, cli_http_response(conn));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
connections.erase(conn);
|
||||||
|
http_destroy(conn->co);
|
||||||
|
delete conn;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::function<bool(cli_result_t &)> cli_tool_t::start_serve(json11::Json cfg)
|
||||||
|
{
|
||||||
|
auto server = new cli_serve_t();
|
||||||
|
server->parent = this;
|
||||||
|
server->options = cfg;
|
||||||
|
return [server](cli_result_t & result)
|
||||||
|
{
|
||||||
|
server->loop();
|
||||||
|
if (server->is_done())
|
||||||
|
{
|
||||||
|
result = server->result;
|
||||||
|
delete server;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -18,6 +18,7 @@ struct status_printer_t
|
|||||||
{
|
{
|
||||||
cli_tool_t *parent;
|
cli_tool_t *parent;
|
||||||
|
|
||||||
|
cli_result_t result;
|
||||||
int state = 0;
|
int state = 0;
|
||||||
json11::Json::array mon_members;
|
json11::Json::array mon_members;
|
||||||
json11::Json agg_stats;
|
json11::Json agg_stats;
|
||||||
@@ -89,7 +90,7 @@ resume_2:
|
|||||||
return;
|
return;
|
||||||
if (parent->etcd_err.err)
|
if (parent->etcd_err.err)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s\n", parent->etcd_err.text.c_str());
|
result = parent->etcd_err;
|
||||||
state = 100;
|
state = 100;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -107,7 +108,8 @@ resume_2:
|
|||||||
if (etcd_states[i]["error"].is_null())
|
if (etcd_states[i]["error"].is_null())
|
||||||
{
|
{
|
||||||
etcd_alive++;
|
etcd_alive++;
|
||||||
etcd_db_size = etcd_states[i]["dbSize"].uint64_value();
|
uint64_t db_size = etcd_states[i]["dbSize"].uint64_value();
|
||||||
|
etcd_db_size = db_size > etcd_db_size ? db_size : etcd_db_size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int mon_count = 0;
|
int mon_count = 0;
|
||||||
@@ -265,7 +267,7 @@ resume_2:
|
|||||||
obj_n = agg_stats["object_counts"][str].uint64_value() * parent->cli->st_cli.global_block_size;
|
obj_n = agg_stats["object_counts"][str].uint64_value() * parent->cli->st_cli.global_block_size;
|
||||||
json_status[str+"_data"] = obj_n;
|
json_status[str+"_data"] = obj_n;
|
||||||
}
|
}
|
||||||
printf("%s\n", json11::Json(json_status).dump().c_str());
|
result.data = json11::Json(json_status);
|
||||||
state = 100;
|
state = 100;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -406,7 +408,7 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_status(json11::Json cfg)
|
|||||||
printer->loop();
|
printer->loop();
|
||||||
if (printer->is_done())
|
if (printer->is_done())
|
||||||
{
|
{
|
||||||
result = { .err = 0 };
|
result = printer->result;
|
||||||
delete printer;
|
delete printer;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,128 @@
|
|||||||
|
// Copyright (c) Vitaliy Filippov, 2019+
|
||||||
|
// License: VNPL-1.1 (see README.md for details)
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include "cli.h"
|
||||||
|
#include "cluster_client.h"
|
||||||
|
#include "str_util.h"
|
||||||
|
#include "json_util.h"
|
||||||
|
|
||||||
|
// List users
|
||||||
|
struct user_lister_t
|
||||||
|
{
|
||||||
|
cli_tool_t *parent;
|
||||||
|
std::vector<std::string> only_names;
|
||||||
|
|
||||||
|
int state = 0;
|
||||||
|
cli_result_t result;
|
||||||
|
json11::Json::array users;
|
||||||
|
|
||||||
|
bool is_done()
|
||||||
|
{
|
||||||
|
return state == 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
if (state == 1)
|
||||||
|
goto resume_1;
|
||||||
|
if (state == 100)
|
||||||
|
return;
|
||||||
|
{
|
||||||
|
json11::Json::array select;
|
||||||
|
if (!only_names.size())
|
||||||
|
{
|
||||||
|
select.push_back(json11::Json::object {
|
||||||
|
{ "request_range", json11::Json::object {
|
||||||
|
{ "key", base64_encode(
|
||||||
|
parent->cli->st_cli.etcd_prefix+"/config/user/"
|
||||||
|
) },
|
||||||
|
{ "range_end", base64_encode(
|
||||||
|
parent->cli->st_cli.etcd_prefix+"/config/user0"
|
||||||
|
) },
|
||||||
|
} },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (auto & name: only_names)
|
||||||
|
{
|
||||||
|
select.push_back(json11::Json::object {
|
||||||
|
{ "request_range", json11::Json::object {
|
||||||
|
{ "key", base64_encode(parent->cli->st_cli.etcd_prefix+"/config/user/"+name) },
|
||||||
|
} }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parent->etcd_txn(json11::Json::object {
|
||||||
|
{ "success", select },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
state = 1;
|
||||||
|
resume_1:
|
||||||
|
if (parent->waiting > 0)
|
||||||
|
return;
|
||||||
|
if (parent->etcd_err.err)
|
||||||
|
{
|
||||||
|
result = parent->etcd_err;
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (auto & response: parent->etcd_result["responses"].array_items())
|
||||||
|
{
|
||||||
|
for (auto & kv_item: response["response_range"]["kvs"].array_items())
|
||||||
|
{
|
||||||
|
auto kv = parent->cli->st_cli.parse_etcd_kv(kv_item);
|
||||||
|
auto user = kv.value.object_items();
|
||||||
|
user["name"] = kv.key.substr(parent->cli->st_cli.etcd_prefix.size()+13);
|
||||||
|
if (!parent->json_output)
|
||||||
|
user["groups_fmt"] = implode(",", user["groups"].array_items());
|
||||||
|
users.push_back(std::move(user));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (parent->json_output)
|
||||||
|
{
|
||||||
|
// JSON output
|
||||||
|
result.data = users;
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Table output: name, type, groups
|
||||||
|
json11::Json::array cols;
|
||||||
|
cols.push_back(json11::Json::object{
|
||||||
|
{ "key", "name" },
|
||||||
|
{ "title", "NAME" },
|
||||||
|
});
|
||||||
|
cols.push_back(json11::Json::object{
|
||||||
|
{ "key", "type" },
|
||||||
|
{ "title", "TYPE" },
|
||||||
|
});
|
||||||
|
cols.push_back(json11::Json::object{
|
||||||
|
{ "key", "groups_fmt" },
|
||||||
|
{ "title", "GROUPS" },
|
||||||
|
});
|
||||||
|
result.text = print_table(users, cols, parent->color);
|
||||||
|
state = 100;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::function<bool(cli_result_t &)> cli_tool_t::start_user_ls(json11::Json cfg)
|
||||||
|
{
|
||||||
|
auto lister = new user_lister_t();
|
||||||
|
lister->parent = this;
|
||||||
|
if (cfg["names"].is_string())
|
||||||
|
lister->only_names.push_back(cfg["names"].string_value());
|
||||||
|
for (auto & item: cfg["names"].array_items())
|
||||||
|
lister->only_names.push_back(item.string_value());
|
||||||
|
return [lister](cli_result_t & result)
|
||||||
|
{
|
||||||
|
lister->loop();
|
||||||
|
if (lister->is_done())
|
||||||
|
{
|
||||||
|
result = lister->result;
|
||||||
|
delete lister;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ add_executable(vitastor-disk
|
|||||||
disk_tool.cpp disk_simple_offsets.cpp
|
disk_tool.cpp disk_simple_offsets.cpp
|
||||||
disk_tool_discard.cpp disk_tool_journal.cpp disk_tool_meta.cpp disk_tool_prepare.cpp disk_tool_resize.cpp
|
disk_tool_discard.cpp disk_tool_journal.cpp disk_tool_meta.cpp disk_tool_prepare.cpp disk_tool_resize.cpp
|
||||||
disk_tool_resize_auto.cpp disk_tool_udev.cpp disk_tool_utils.cpp disk_tool_upgrade.cpp
|
disk_tool_resize_auto.cpp disk_tool_udev.cpp disk_tool_utils.cpp disk_tool_upgrade.cpp
|
||||||
../util/crc32c.c ../util/str_util.cpp ../util/json_util.cpp ../../json11/json11.cpp ../util/rw_blocking.cpp ../util/allocator.cpp ../util/ringloop.cpp
|
../util/crc32c.c ../util/xxh_x86dispatch.c ../util/str_util.cpp ../util/json_util.cpp ../../json11/json11.cpp ../util/rw_blocking.cpp ../util/allocator.cpp ../util/ringloop.cpp
|
||||||
../blockstore/blockstore_disk.cpp ../blockstore/blockstore_heap.cpp ../blockstore/multilist.cpp
|
../blockstore/blockstore_disk.cpp ../blockstore/blockstore_heap.cpp ../blockstore/multilist.cpp
|
||||||
)
|
)
|
||||||
target_link_libraries(vitastor-disk
|
target_link_libraries(vitastor-disk
|
||||||
|
|||||||
@@ -136,8 +136,8 @@ struct disk_tool_t
|
|||||||
void choose_journal_block(uint32_t je_size);
|
void choose_journal_block(uint32_t je_size);
|
||||||
int resize_rebuild_journal();
|
int resize_rebuild_journal();
|
||||||
int resize_write_new_journal();
|
int resize_write_new_journal();
|
||||||
void remap_big_write(heap_entry_t *wr);
|
void remap_big_write(blockstore_heap_t *heap, heap_entry_t *wr);
|
||||||
void remap_small_write(heap_entry_t *wr);
|
void remap_small_write(blockstore_heap_t *heap, heap_entry_t *wr);
|
||||||
void fill_old_clean_entry(blockstore_heap_t *heap, heap_entry_t *big_wr);
|
void fill_old_clean_entry(blockstore_heap_t *heap, heap_entry_t *big_wr);
|
||||||
void fill_old_journal_entry(blockstore_heap_t *heap, heap_entry_t *wr);
|
void fill_old_journal_entry(blockstore_heap_t *heap, heap_entry_t *wr);
|
||||||
int resize_rebuild_meta();
|
int resize_rebuild_meta();
|
||||||
@@ -175,7 +175,6 @@ struct disk_tool_t
|
|||||||
void disk_tool_simple_offsets(json11::Json cfg, bool json_output);
|
void disk_tool_simple_offsets(json11::Json cfg, bool json_output);
|
||||||
|
|
||||||
uint64_t sscanf_json(const char *fmt, const json11::Json & str);
|
uint64_t sscanf_json(const char *fmt, const json11::Json & str);
|
||||||
void fromhexstr(const std::string & from, int bytes, uint8_t *to);
|
|
||||||
int disable_cache(const std::string & dev);
|
int disable_cache(const std::string & dev);
|
||||||
uint64_t get_atomic_write_size(const std::string & dev);
|
uint64_t get_atomic_write_size(const std::string & dev);
|
||||||
uint64_t get_device_size(const std::string & dev, bool should_exist = false);
|
uint64_t get_device_size(const std::string & dev, bool should_exist = false);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "disk_tool.h"
|
#include "disk_tool.h"
|
||||||
|
#include "str_util.h"
|
||||||
#include "malloc_or_die.h"
|
#include "malloc_or_die.h"
|
||||||
|
|
||||||
int disk_tool_t::dump_journal()
|
int disk_tool_t::dump_journal()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "disk_tool.h"
|
#include "disk_tool.h"
|
||||||
#include "rw_blocking.h"
|
#include "rw_blocking.h"
|
||||||
#include "json_util.h"
|
#include "json_util.h"
|
||||||
|
#include "str_util.h"
|
||||||
#include "malloc_or_die.h"
|
#include "malloc_or_die.h"
|
||||||
|
|
||||||
int disk_tool_t::process_meta(std::function<void(blockstore_meta_header_v3_t *)> hdr_fn,
|
int disk_tool_t::process_meta(std::function<void(blockstore_meta_header_v3_t *)> hdr_fn,
|
||||||
@@ -751,7 +752,7 @@ close_err0:
|
|||||||
{
|
{
|
||||||
*wr->get_checksum(&heap) = sscanf_json("%jx", write_entry["data_crc32c"]);
|
*wr->get_checksum(&heap) = sscanf_json("%jx", write_entry["data_crc32c"]);
|
||||||
}
|
}
|
||||||
wr->crc32c = wr->calc_crc32c();
|
wr->checksum = wr->calc_checksum(&heap);
|
||||||
assert((uint8_t*)wr + wr->size == new_meta_buf + meta_offset + used_space);
|
assert((uint8_t*)wr + wr->size == new_meta_buf + meta_offset + used_space);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -794,7 +795,7 @@ close_err:
|
|||||||
fromhexstr(meta_entry["ext_bitmap"].string_value(), new_clean_entry_bitmap_size, wr->get_ext_bitmap(&heap));
|
fromhexstr(meta_entry["ext_bitmap"].string_value(), new_clean_entry_bitmap_size, wr->get_ext_bitmap(&heap));
|
||||||
if (new_meta_hdr->data_csum_type != 0)
|
if (new_meta_hdr->data_csum_type != 0)
|
||||||
fromhexstr(meta_entry["data_csum"].string_value(), new_data_csum_size, wr->get_checksums(&heap));
|
fromhexstr(meta_entry["data_csum"].string_value(), new_data_csum_size, wr->get_checksums(&heap));
|
||||||
wr->crc32c = wr->calc_crc32c();
|
wr->checksum = wr->calc_checksum(&heap);
|
||||||
assert((uint8_t*)wr + wr->size == new_meta_buf + meta_offset + used_space);
|
assert((uint8_t*)wr + wr->size == new_meta_buf + meta_offset + used_space);
|
||||||
auto j_it = journal_by_object.find(oid);
|
auto j_it = journal_by_object.find(oid);
|
||||||
if (j_it != journal_by_object.end())
|
if (j_it != journal_by_object.end())
|
||||||
@@ -860,7 +861,7 @@ close_err:
|
|||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
wr->size = wr->get_size(&heap);
|
wr->size = wr->get_size(&heap);
|
||||||
wr->crc32c = wr->calc_crc32c();
|
wr->checksum = wr->calc_checksum(&heap);
|
||||||
assert((uint8_t*)wr + wr->size == new_meta_buf + meta_offset + used_space);
|
assert((uint8_t*)wr + wr->size == new_meta_buf + meta_offset + used_space);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -531,7 +531,7 @@ int disk_tool_t::resize_write_new_journal()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void disk_tool_t::remap_big_write(heap_entry_t *wr)
|
void disk_tool_t::remap_big_write(blockstore_heap_t *heap, heap_entry_t *wr)
|
||||||
{
|
{
|
||||||
uint64_t block_num = wr->big().block_num;
|
uint64_t block_num = wr->big().block_num;
|
||||||
auto remap_it = data_remap.find(block_num);
|
auto remap_it = data_remap.find(block_num);
|
||||||
@@ -544,10 +544,10 @@ void disk_tool_t::remap_big_write(heap_entry_t *wr)
|
|||||||
}
|
}
|
||||||
block_num += data_idx_diff;
|
block_num += data_idx_diff;
|
||||||
wr->big().block_num = block_num;
|
wr->big().block_num = block_num;
|
||||||
wr->crc32c = wr->calc_crc32c();
|
wr->checksum = wr->calc_checksum(heap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void disk_tool_t::remap_small_write(heap_entry_t *wr)
|
void disk_tool_t::remap_small_write(blockstore_heap_t *heap, heap_entry_t *wr)
|
||||||
{
|
{
|
||||||
if (new_meta_format == BLOCKSTORE_META_FORMAT_HEAP && wr->small().len > 0)
|
if (new_meta_format == BLOCKSTORE_META_FORMAT_HEAP && wr->small().len > 0)
|
||||||
{
|
{
|
||||||
@@ -559,7 +559,7 @@ void disk_tool_t::remap_small_write(heap_entry_t *wr)
|
|||||||
memcpy(new_journal_ptr, buffer_area+wr->small().location, wr->small().len);
|
memcpy(new_journal_ptr, buffer_area+wr->small().location, wr->small().len);
|
||||||
wr->small().location = new_journal_ptr-new_journal_buf;
|
wr->small().location = new_journal_ptr-new_journal_buf;
|
||||||
new_journal_ptr += wr->small().len;
|
new_journal_ptr += wr->small().len;
|
||||||
wr->crc32c = wr->calc_crc32c();
|
wr->checksum = wr->calc_checksum(heap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -673,11 +673,11 @@ int disk_tool_t::resize_rebuild_meta()
|
|||||||
{
|
{
|
||||||
if (wr->type() == BS_HEAP_BIG_WRITE || wr->type() == BS_HEAP_BIG_INTENT)
|
if (wr->type() == BS_HEAP_BIG_WRITE || wr->type() == BS_HEAP_BIG_INTENT)
|
||||||
{
|
{
|
||||||
remap_big_write(wr);
|
remap_big_write(heap, wr);
|
||||||
}
|
}
|
||||||
else if (wr->type() == BS_HEAP_SMALL_WRITE)
|
else if (wr->type() == BS_HEAP_SMALL_WRITE)
|
||||||
{
|
{
|
||||||
remap_small_write(wr);
|
remap_small_write(heap, wr);
|
||||||
}
|
}
|
||||||
else if (wr->type() != BS_HEAP_DELETE && new_meta_format != BLOCKSTORE_META_FORMAT_HEAP)
|
else if (wr->type() != BS_HEAP_DELETE && new_meta_format != BLOCKSTORE_META_FORMAT_HEAP)
|
||||||
{
|
{
|
||||||
@@ -786,7 +786,7 @@ int disk_tool_t::resize_rebuild_meta()
|
|||||||
memcpy(((uint8_t*)wr) + sizeof(heap_big_write_t) + new_clean_entry_bitmap_size, bitmap+new_clean_entry_bitmap_size, new_clean_entry_bitmap_size);
|
memcpy(((uint8_t*)wr) + sizeof(heap_big_write_t) + new_clean_entry_bitmap_size, bitmap+new_clean_entry_bitmap_size, new_clean_entry_bitmap_size);
|
||||||
memcpy(((uint8_t*)wr) + sizeof(heap_big_write_t) + 2*new_clean_entry_bitmap_size, bitmap+2*new_clean_entry_bitmap_size, new_data_csum_size);
|
memcpy(((uint8_t*)wr) + sizeof(heap_big_write_t) + 2*new_clean_entry_bitmap_size, bitmap+2*new_clean_entry_bitmap_size, new_data_csum_size);
|
||||||
}
|
}
|
||||||
wr->crc32c = wr->calc_crc32c();
|
wr->checksum = wr->calc_checksum(&dsk);
|
||||||
new_meta_pos += wr->size;
|
new_meta_pos += wr->size;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -22,28 +22,6 @@ uint64_t sscanf_json(const char *fmt, const json11::Json & str)
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int fromhex(char c)
|
|
||||||
{
|
|
||||||
if (c >= '0' && c <= '9')
|
|
||||||
return (c-'0');
|
|
||||||
else if (c >= 'a' && c <= 'f')
|
|
||||||
return (c-'a'+10);
|
|
||||||
else if (c >= 'A' && c <= 'F')
|
|
||||||
return (c-'A'+10);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fromhexstr(const std::string & from, int bytes, uint8_t *to)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < from.size() && i < bytes; i++)
|
|
||||||
{
|
|
||||||
int x = fromhex(from[2*i]), y = fromhex(from[2*i+1]);
|
|
||||||
if (x < 0 || y < 0)
|
|
||||||
break;
|
|
||||||
to[i] = x*16 + y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns 1 = check error, 0 = write through, -1 = write back
|
// returns 1 = check error, 0 = write through, -1 = write back
|
||||||
// (similar to 1 = warning, -1 = error, 0 = success in disable_cache)
|
// (similar to 1 = warning, -1 = error, 0 = success in disable_cache)
|
||||||
static int check_queue_cache(std::string dev, std::string parent_dev)
|
static int check_queue_cache(std::string dev, std::string parent_dev)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ target_link_libraries(vitastor-osd
|
|||||||
${ISAL_LIBRARIES}
|
${ISAL_LIBRARIES}
|
||||||
${IBVERBS_LIBRARIES}
|
${IBVERBS_LIBRARIES}
|
||||||
${RDMACM_LIBRARIES}
|
${RDMACM_LIBRARIES}
|
||||||
|
${OPENSSL_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
# osd_rmw_test
|
# osd_rmw_test
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ void osd_t::parse_config(bool init)
|
|||||||
if (!osd_num)
|
if (!osd_num)
|
||||||
throw std::runtime_error("osd_num is required in the configuration");
|
throw std::runtime_error("osd_num is required in the configuration");
|
||||||
msgr.osd_num = osd_num;
|
msgr.osd_num = osd_num;
|
||||||
|
st_cli.osd_num = osd_num;
|
||||||
}
|
}
|
||||||
if (bs)
|
if (bs)
|
||||||
{
|
{
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user