forked from vitalif/vitastor
Compare commits
2 Commits
kv-list-en
...
cli-rm-dat
| Author | SHA1 | Date | |
|---|---|---|---|
| 1742cdc081 | |||
| 66434e3220 |
@@ -221,24 +221,6 @@ jobs:
|
||||
echo ""
|
||||
done
|
||||
|
||||
test_modify_pool:
|
||||
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_modify_pool.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:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
@@ -797,24 +779,6 @@ jobs:
|
||||
echo ""
|
||||
done
|
||||
|
||||
test_modify_image_resize:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||
steps:
|
||||
- name: Run test
|
||||
id: test
|
||||
timeout-minutes: 3
|
||||
run: /root/vitastor/tests/test_modify_image_resize.sh
|
||||
- name: Print logs
|
||||
if: always() && steps.test.outcome == 'failure'
|
||||
run: |
|
||||
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
|
||||
echo "-------- $i --------"
|
||||
cat $i
|
||||
echo ""
|
||||
done
|
||||
|
||||
test_rm_degraded:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
@@ -833,24 +797,6 @@ jobs:
|
||||
echo ""
|
||||
done
|
||||
|
||||
test_describe:
|
||||
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_describe.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_chain:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
@@ -1067,24 +1013,6 @@ jobs:
|
||||
echo ""
|
||||
done
|
||||
|
||||
test_kv_list_bounds:
|
||||
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_kv_list_bounds.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_splitbrain:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
@@ -1265,24 +1193,6 @@ jobs:
|
||||
echo ""
|
||||
done
|
||||
|
||||
test_cli_help:
|
||||
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_cli_help.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_root_node:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
@@ -1661,24 +1571,6 @@ jobs:
|
||||
echo ""
|
||||
done
|
||||
|
||||
test_ls_tree:
|
||||
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_ls_tree.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_read_bitmap:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
@@ -2471,24 +2363,6 @@ jobs:
|
||||
echo ""
|
||||
done
|
||||
|
||||
test_nfs_list_bounds:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
container: ${{env.TEST_IMAGE}}:${{github.sha}}
|
||||
steps:
|
||||
- name: Run test
|
||||
id: test
|
||||
timeout-minutes: 3
|
||||
run: /root/vitastor/tests/test_nfs_list_bounds.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:
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
|
||||
@@ -231,8 +231,8 @@ Options:
|
||||
* `--writers-stopped` allows optimised removal in case of a single 'slim' read-write
|
||||
child and 'fat' removed parent: the child is merged into parent and parent is renamed
|
||||
to child in that case. In other cases parent layers are always merged into children.
|
||||
* `--exact` - remove multiple images with given names.
|
||||
* `--matching` - remove multiple images with names matching given glob patterns.
|
||||
* `--exact` - remove multiple images with names matching given glob patterns.
|
||||
* `--matching` - remove multiple images with given names
|
||||
* `--down-ok` - continue deletion/merging even if some data will be left on unavailable OSDs.
|
||||
|
||||
## flatten
|
||||
@@ -243,15 +243,16 @@ Flatten a layer, i.e. merge data and detach it from parents.
|
||||
|
||||
## rm-data
|
||||
|
||||
`vitastor-cli rm-data --pool <pool> --inode <inode> [--wait-list] [--min-offset <offset>]`
|
||||
`vitastor-cli rm-data --pool <id|name> --inode <inode> [--wait-list] [--min-offset <offset>]`
|
||||
|
||||
Remove inode data without changing metadata.
|
||||
Remove inode data without changing metadata. `<inode>` may be decimal or 0x hex.
|
||||
|
||||
```
|
||||
--wait-list Retrieve full objects listings before starting to remove objects.
|
||||
Requires more memory, but allows to show correct removal progress.
|
||||
--min-offset Purge only data starting with specified offset.
|
||||
--max-offset Purge only data before specified offset.
|
||||
Offsets are in bytes or with a K/M/G/T unit suffix, decimal or 0x hex.
|
||||
--client_wait_up_timeout 16 Timeout for waiting until PGs are up in seconds.
|
||||
```
|
||||
|
||||
@@ -287,7 +288,7 @@ Describe unclean object locations in the cluster. Options:
|
||||
|
||||
## fix
|
||||
|
||||
`vitastor-cli fix [--objects <objects>] --bad-osds <osds> [--part <part>] [--check no]`
|
||||
`vitastor-cli fix [--objects <objects>] [--bad-osds <osds>] [--part <part>] [--check no]`
|
||||
|
||||
Fix inconsistent objects in the cluster by deleting some copies.
|
||||
|
||||
@@ -298,7 +299,7 @@ Fix inconsistent objects in the cluster by deleting some copies.
|
||||
Plain text format: 0x<inode>:0x<stripe> <any delimiter> 0x<inode>:0x<stripe> ...
|
||||
JSON format: [{"inode":"0x...","stripe":"0x..."},...]
|
||||
--bad-osds <osds>
|
||||
Required. Remove inconsistent copies/parts of objects from these OSDs, effectively
|
||||
Remove inconsistent copies/parts of objects from these OSDs, effectively
|
||||
marking them bad and allowing Vitastor to recover objects from other copies.
|
||||
--part <number>
|
||||
Only remove EC part <number> (from 0 to pg_size-1), required for extreme
|
||||
@@ -316,17 +317,14 @@ Allocate a new OSD number and reserve it by creating empty `/osd/stats/<n>` key.
|
||||
|
||||
## rm-osd
|
||||
|
||||
`vitastor-cli rm-osd [-f|--force] [--allow-data-loss] [--allow-up] [--dry-run] <osd_id> [osd_id...]`
|
||||
`vitastor-cli rm-osd [--force] [--allow-data-loss] [--dry-run] <osd_id> [osd_id...]`
|
||||
|
||||
Remove metadata and configuration for specified OSD(s) from etcd.
|
||||
|
||||
Refuses to remove OSDs that are up or whose removal makes PGs degraded, offline or incomplete.
|
||||
Refuses to remove OSDs with data without `--force` and `--allow-data-loss`.
|
||||
|
||||
* `-f|--force` - remove OSDs even if they are up or PGs become degraded or offline.
|
||||
* `--allow-data-loss` - remove OSDs even if PGs or objects become incomplete (DATA LOSS). Implies `--force`.
|
||||
* `--allow-up` - remove OSDs even if they are up.
|
||||
* `--dry-run` - only check if deletion is possible without data loss and
|
||||
redundancy degradation.
|
||||
With `--dry-run` only checks if deletion is possible without data loss and
|
||||
redundancy degradation.
|
||||
|
||||
## osd-tree
|
||||
|
||||
@@ -358,7 +356,7 @@ host stump
|
||||
|
||||
## ls-osd
|
||||
|
||||
`vitastor-cli osds|ls-osds|ls-osd|osd-ls [-l|--long]`
|
||||
`vitastor-cli osds|ls-osd|osd-ls [-l|--long]`
|
||||
|
||||
Show current OSDs as list, optionally with I/O statistics if -l is specified.
|
||||
|
||||
@@ -413,8 +411,8 @@ Create a pool. Required parameters:
|
||||
|
||||
| <!-- --> | <!-- --> |
|
||||
|--------------------------|-----------------------------------------------------------------------------------------|
|
||||
| `-s R` or `--pg_size R` | Number of replicas for replicated pools, from 1 to 64 |
|
||||
| `--ec N+K` | Number of data (N) and parity (K) chunks for erasure-coded pools, N+K from 3 to 64 |
|
||||
| `-s R` or `--pg_size R` | Number of replicas for replicated pools |
|
||||
| `--ec N+K` | Number of data (N) and parity (K) chunks for erasure-coded pools |
|
||||
| `-n N` or `--pg_count N` | PG count for the new pool (start with 10*\<OSD count\>/pg_size rounded to a power of 2) |
|
||||
|
||||
Optional parameters:
|
||||
@@ -428,7 +426,7 @@ Optional parameters:
|
||||
| `--block_size 128k` | Put pool only on OSDs with this data block size |
|
||||
| `--bitmap_granularity 4k` | Put pool only on OSDs with this logical sector size |
|
||||
| `--immediate_commit none` | Put pool only on OSDs with this or larger immediate_commit (none < small < all) |
|
||||
| `--level_placement <rules>` | Use additional failure domain rules (example: "dc=112233", "" to remove) |
|
||||
| `--level_placement <rules>` | Use additional failure domain rules (example: "dc=112233") |
|
||||
| `--raw_placement <rules>` | Specify raw PG generation rules ([details](../config/pool.en.md#raw_placement)) |
|
||||
| `--local_reads primary` | Local read policy for replicated pools: primary, nearest or random |
|
||||
| `--primary_affinity_tags tags` | Prefer to put primary copies on OSDs with all specified tags |
|
||||
@@ -473,8 +471,6 @@ These, however, can still be modified with -f|--force.
|
||||
|
||||
See [create-pool](#create-pool) for parameter descriptions.
|
||||
|
||||
Renaming with --name fails if the new name is already taken by a different pool.
|
||||
|
||||
Examples:
|
||||
|
||||
`vitastor-cli modify-pool pool_A --name pool_B`
|
||||
@@ -513,7 +509,7 @@ the Vitastor authorization model.
|
||||
|
||||
## modify-user
|
||||
|
||||
`vitastor-cli modify-user|user-modify --groups <group1,group2,...> <username>`
|
||||
`vitastor-cli modify-user --groups <group1,group2,...> <username>`
|
||||
|
||||
Create or update a user with the given name (matching TLS certificate CN).
|
||||
|
||||
|
||||
@@ -239,8 +239,8 @@ vitastor-cli dd [iimg=<image> | if=<file>] [oimg=<image> | of=<file>] [bs=1M] \
|
||||
у удаляемой цепочки есть только один дочерний образ, содержащий небольшой объём данных.
|
||||
В этом случае дочерний образ вливается в родительский и удаляется, а родительский
|
||||
переименовывается в дочерний.
|
||||
* `--exact` - удалить все образы с точно заданными именами.
|
||||
* `--matching` - удалить все образы с именами, подходящими под переданные glob-шаблоны.
|
||||
* `--exact` - удалить все образы с именами, подходящими под переданные glob-шаблоны.
|
||||
* `--matching` - удалить все образы с точно заданными именами.
|
||||
* `--down-ok` - продолжать удаление/слияние, даже если часть данных останется неудалённой на недоступных OSD.
|
||||
|
||||
## flatten
|
||||
@@ -252,15 +252,16 @@ vitastor-cli dd [iimg=<image> | if=<file>] [oimg=<image> | of=<file>] [bs=1M] \
|
||||
|
||||
## rm-data
|
||||
|
||||
`vitastor-cli rm-data --pool <pool> --inode <inode> [--wait-list] [--min-offset <offset>]`
|
||||
`vitastor-cli rm-data --pool <id|name> --inode <inode> [--wait-list] [--min-offset <offset>]`
|
||||
|
||||
Удалить данные инода, не меняя метаданные образов.
|
||||
Удалить данные инода, не меняя метаданные образов. `<inode>` можно задать в десятичном виде или в hex с 0x.
|
||||
|
||||
```
|
||||
--wait-list Сначала запросить полный листинг объектов, а потом начать удалять.
|
||||
Требует больше памяти, но позволяет правильно печатать прогресс удаления.
|
||||
--min-offset Удалять только данные, начиная с заданного смещения.
|
||||
--max-offset Удалять только данные до (исключительно) заданного смещения.
|
||||
Смещения задаются в байтах или с суффиксом K/M/G/T, в десятичном виде или в hex с 0x.
|
||||
--client_wait_up_timeout 16 Время ожидания поднятия PG в секундах.
|
||||
```
|
||||
|
||||
@@ -302,7 +303,7 @@ vitastor-cli dd [iimg=<image> | if=<file>] [oimg=<image> | of=<file>] [bs=1M] \
|
||||
|
||||
## fix
|
||||
|
||||
`vitastor-cli fix [--objects <объекты>] --bad-osds <osds> [--part <номер>] [--check no]`
|
||||
`vitastor-cli fix [--objects <объекты>] [--bad-osds <osds>] [--part <номер>] [--check no]`
|
||||
|
||||
Исправить неконсистентные (неоднозначные) объекты путём удаления части копий.
|
||||
|
||||
@@ -313,9 +314,9 @@ vitastor-cli dd [iimg=<image> | if=<file>] [oimg=<image> | of=<file>] [bs=1M] \
|
||||
Простой формат: 0x<инод>:0x<смещение> <любой разделитель> 0x<инод>:0x<смещение> ...
|
||||
Формат JSON: [{"inode":"0x<инод>","stripe":"0x<смещение>"},...]
|
||||
--bad-osds <osds>
|
||||
Обязательная опция. Удалить неконсистентные копии/части объектов с данных
|
||||
OSD, таким образом признавая потерю этих копий и позволяя Vitastor-у
|
||||
восстановить объекты из других копий.
|
||||
Удалить неконсистентные копии/части объектов с данных OSD, таким образом
|
||||
признавая потерю этих копий и позволяя Vitastor-у восстановить объекты из
|
||||
других копий.
|
||||
--part <номер>
|
||||
Удалить только части EC с заданным номером (от 0 до pg_size-1). Нужно только
|
||||
в редких граничных случаях, когда один и тот же OSD содержит несколько частей
|
||||
@@ -334,19 +335,14 @@ vitastor-cli dd [iimg=<image> | if=<file>] [oimg=<image> | of=<file>] [bs=1M] \
|
||||
|
||||
## rm-osd
|
||||
|
||||
`vitastor-cli rm-osd [-f|--force] [--allow-data-loss] [--allow-up] [--dry-run] <osd_id> [osd_id...]`
|
||||
`vitastor-cli rm-osd [--force] [--allow-data-loss] [--dry-run] <osd_id> [osd_id...]`
|
||||
|
||||
Удалить метаданные и конфигурацию для заданных OSD из etcd.
|
||||
|
||||
Отказывается удалять работающие OSD, а также OSD, после удаления которых PG станут
|
||||
деградированными, недоступными (offline) или неполными (incomplete).
|
||||
Отказывается удалять OSD с данными без опций `--force` и `--allow-data-loss`.
|
||||
|
||||
* `-f|--force` - удалить OSD, даже если они работают или PG станут деградированными или недоступными.
|
||||
* `--allow-data-loss` - удалить OSD, даже если PG или объекты станут неполными (ПОТЕРЯ ДАННЫХ).
|
||||
Включает действие `--force`.
|
||||
* `--allow-up` - удалить OSD, даже если они работают.
|
||||
* `--dry-run` - только проверить, возможно ли удаление без потери данных и деградации
|
||||
избыточности.
|
||||
С опцией `--dry-run` только проверяет, возможно ли удаление без потери данных и деградации
|
||||
избыточности.
|
||||
|
||||
## osd-tree
|
||||
|
||||
@@ -378,7 +374,7 @@ host stump
|
||||
|
||||
## ls-osd
|
||||
|
||||
`vitastor-cli osds|ls-osds|ls-osd|osd-ls [-l|--long]`
|
||||
`vitastor-cli osds|ls-osd|osd-ls [-l|--long]`
|
||||
|
||||
Показать список OSD, со статистикой ввода-вывода, если установлено -l.
|
||||
|
||||
@@ -408,7 +404,7 @@ OSD PARENT UP SIZE USED% TAGS WEIGHT BLOCK BITMAP
|
||||
|
||||
## pg-list
|
||||
|
||||
`vitastor-cli pg-list|pg-ls|list-pg|ls-pg|ls-pgs|pgs [OPTIONS] [state1+state2] [^state3] [...]`
|
||||
`vitastor-cli pg-list|pg-ls|list-pg|ls-pg|ls-pgs [OPTIONS] [state1+state2] [^state3] [...]`
|
||||
|
||||
Вывести список PG с состояними, удовлетворяющими любому из переданных фильтров (^ или !
|
||||
в начале фильтра означает отрицание). Опции:
|
||||
@@ -434,8 +430,8 @@ OSD PARENT UP SIZE USED% TAGS WEIGHT BLOCK BITMAP
|
||||
|
||||
| <!-- --> | <!-- --> |
|
||||
|---------------------------|-----------------------------------------------------------------------------------------------|
|
||||
| `-s R` или `--pg_size R` | Число копий данных для реплицированных пулов, от 1 до 64 |
|
||||
| `--ec N+K` | Число частей данных (N) и чётности (K) для пулов с кодами коррекции ошибок, N+K от 3 до 64 |
|
||||
| `-s R` или `--pg_size R` | Число копий данных для реплицированных пулов |
|
||||
| `--ec N+K` | Число частей данных (N) и чётности (K) для пулов с кодами коррекции ошибок |
|
||||
| `-n N` или `--pg_count N` | Число PG для нового пула (начните с 10*\<число OSD\>/pg_size, округлённого до степени двойки) |
|
||||
|
||||
Необязательные параметры:
|
||||
@@ -449,7 +445,7 @@ OSD PARENT UP SIZE USED% TAGS WEIGHT BLOCK BITMAP
|
||||
| `--block_size 128k` | ...только OSD с данным размером блока |
|
||||
| `--bitmap_granularity 4k` | ...только OSD с данным размером логического сектора |
|
||||
| `--immediate_commit none` | ...только OSD с этим или большим immediate_commit (none < small < all) |
|
||||
| `--level_placement <rules>` | Задать правила дополнительных доменов отказа (пример: "dc=112233", "" — удалить) |
|
||||
| `--level_placement <rules>` | Задать правила дополнительных доменов отказа (пример: "dc=112233") |
|
||||
| `--raw_placement <rules>` | Задать низкоуровневые правила генерации PG ([детали](../config/pool.ru.md#raw_placement)) |
|
||||
| `--local_reads primary` | Политика локальных чтений для реплик: primary, nearest или random |
|
||||
| `--primary_affinity_tags tags` | Предпочитать OSD со всеми данными тегами для роли первичных |
|
||||
@@ -492,8 +488,6 @@ OSD PARENT UP SIZE USED% TAGS WEIGHT BLOCK BITMAP
|
||||
|
||||
Описания параметров смотрите в [create-pool](#create-pool).
|
||||
|
||||
Переименование с --name запрещается, если новое название уже занято другим пулом.
|
||||
|
||||
Примеры:
|
||||
|
||||
`vitastor-cli modify-pool pool_A --name pool_B`
|
||||
@@ -534,7 +528,7 @@ OSD PARENT UP SIZE USED% TAGS WEIGHT BLOCK BITMAP
|
||||
|
||||
## modify-user
|
||||
|
||||
`vitastor-cli modify-user|user-modify --groups <group1,group2,...> <username>`
|
||||
`vitastor-cli modify-user --groups <group1,group2,...> <username>`
|
||||
|
||||
Создать или изменить пользователя с указанным именем (совпадающим с CN TLS-сертификата).
|
||||
|
||||
|
||||
@@ -21,11 +21,8 @@ if("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/local/?$")
|
||||
endif()
|
||||
set(ENABLE_COVERAGE false CACHE BOOL "Enable code coverage")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
add_definitions(-DVITASTOR_VERSION="3.2.2")
|
||||
add_definitions(-D_GNU_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -Wno-c99-designator -Wno-vla-cxx-extension -Wno-sign-compare -Wno-comment -Wno-parentheses -Wno-pointer-arith -fdiagnostics-color=always -fno-omit-frame-pointer -fvisibility=hidden -I ${CMAKE_SOURCE_DIR}/src)
|
||||
add_definitions(-D_GNU_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -Wno-sign-compare -Wno-comment -Wno-parentheses -Wno-pointer-arith -fdiagnostics-color=always -fno-omit-frame-pointer -fvisibility=hidden -I ${CMAKE_SOURCE_DIR}/src)
|
||||
add_link_options(-fno-omit-frame-pointer)
|
||||
if (${WITH_ASAN})
|
||||
add_definitions(-fsanitize=address)
|
||||
|
||||
@@ -572,7 +572,7 @@ void blockstore_heap_t::finish_load()
|
||||
if (postponed_items.size())
|
||||
{
|
||||
// Sort "postponed" items and load in batches
|
||||
std::sort(postponed_items.begin(), postponed_items.end(), [](const heap_list_item_t* a, const heap_list_item_t* b)
|
||||
std::sort(postponed_items.begin(), postponed_items.end(), [this](const heap_list_item_t* a, const heap_list_item_t* b)
|
||||
{
|
||||
return a->entry.inode < b->entry.inode || a->entry.inode == b->entry.inode &&
|
||||
(a->entry.stripe < b->entry.stripe || a->entry.stripe == b->entry.stripe &&
|
||||
|
||||
@@ -273,7 +273,7 @@ resume_6:
|
||||
rc->buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, rc->len);
|
||||
data = ((ring_data_t*)sqe->user_data);
|
||||
data->iov = (iovec){ rc->buf, rc->len };
|
||||
data->callback = [rc](ring_data_t *data)
|
||||
data->callback = [this, rc](ring_data_t *data)
|
||||
{
|
||||
if (data->res < 0)
|
||||
{
|
||||
|
||||
@@ -355,7 +355,7 @@ resume_11:
|
||||
BS_SUBMIT_GET_SQE(sqe, data);
|
||||
io_uring_prep_fsync(sqe, dsk.data_fd, IORING_FSYNC_DATASYNC);
|
||||
data->iov = { 0 };
|
||||
data->callback = [this, fs = data_fsync_cur](ring_data_t *data)
|
||||
data->callback = [this, op, fs = data_fsync_cur](ring_data_t *data)
|
||||
{
|
||||
live = true;
|
||||
if (data->res != 0)
|
||||
|
||||
@@ -83,8 +83,10 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op)
|
||||
if (dsk.csum_block_size)
|
||||
{
|
||||
// More complex check because all journal entries have different lengths
|
||||
int left = PRIV(op)->sync_big_writes.size();
|
||||
for (auto & sbw: PRIV(op)->sync_big_writes)
|
||||
{
|
||||
left--;
|
||||
auto & dirty_entry = dirty_db.at(sbw);
|
||||
uint64_t dyn_size = dsk.dirty_dyn_size(dirty_entry.offset, dirty_entry.len);
|
||||
if (!space_check.check_available(op, 1, sizeof(journal_entry_big_write) + dyn_size, 0))
|
||||
|
||||
@@ -245,7 +245,7 @@ void cluster_client_t::inc_wait(uint64_t opcode, uint64_t flags, cluster_op_t *n
|
||||
{
|
||||
next->prev_wait += inc;
|
||||
assert(next->prev_wait >= 0);
|
||||
if (!next->prev_wait && !continuing_ops)
|
||||
if (!next->prev_wait)
|
||||
{
|
||||
// Kind of std::vector with local "small vector optimisation"
|
||||
if (bh_op_count >= bh_op_max)
|
||||
|
||||
@@ -71,8 +71,8 @@ struct msgr_rdma_connection_t;
|
||||
struct msgr_rdma_context_t;
|
||||
#endif
|
||||
|
||||
class op_aes_xts_encrypt_t;
|
||||
class op_aes_xts_decrypt_t;
|
||||
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);
|
||||
|
||||
@@ -216,7 +216,7 @@ protected:
|
||||
friend class copy_op_writer_t;
|
||||
friend class gcm_op_writer_t;
|
||||
friend class get_op_writer_t;
|
||||
friend struct osd_client_t;
|
||||
friend class osd_client_t;
|
||||
|
||||
int keepalive_timer_id = -1;
|
||||
|
||||
|
||||
@@ -284,6 +284,7 @@ bool msgr_handshake_t::on_error(const std::string & prefix)
|
||||
|
||||
bool msgr_handshake_t::init(bool server_mode)
|
||||
{
|
||||
this->ctx = ctx;
|
||||
std::unique_ptr<EVP_PKEY_CTX, decltype(&EVP_PKEY_CTX_free)> kctx(EVP_PKEY_CTX_new(ctx->params, NULL), EVP_PKEY_CTX_free);
|
||||
if (!kctx)
|
||||
return on_error("EVP_PKEY_CTX_new with EC params: ");
|
||||
|
||||
@@ -108,20 +108,21 @@ static const char* help_text =
|
||||
" --writers-stopped allows optimised removal in case of a single 'slim' read-write\n"
|
||||
" child and 'fat' removed parent: the child is merged into parent and parent is renamed\n"
|
||||
" to child in that case. In other cases parent layers are always merged into children.\n"
|
||||
" --exact Remove multiple images with given names.\n"
|
||||
" --matching Remove multiple images with names matching given glob patterns.\n"
|
||||
" --exact Remove multiple images with names matching given glob patterns.\n"
|
||||
" --matching Remove multiple images with given names\n"
|
||||
" --writers-stopped Allow renaming inodes over their read/write children.\n"
|
||||
" --down-ok Continue deletion/merging even if some data will be left on unavailable OSDs.\n"
|
||||
"\n"
|
||||
"vitastor-cli flatten <layer>\n"
|
||||
" Flatten a layer, i.e. merge data and detach it from parents.\n"
|
||||
"\n"
|
||||
"vitastor-cli rm-data --pool <pool> --inode <inode> [--wait-list] [--min-offset <offset>]\n"
|
||||
" Remove inode data without changing metadata.\n"
|
||||
"vitastor-cli rm-data --pool <id|name> --inode <inode> [--wait-list] [--min-offset <offset>]\n"
|
||||
" Remove inode data without changing metadata. <inode> may be decimal or 0x hex.\n"
|
||||
" --wait-list Retrieve full objects listings before starting to remove objects.\n"
|
||||
" Requires more memory, but allows to show correct removal progress.\n"
|
||||
" --min-offset Purge only data starting with specified offset.\n"
|
||||
" --max-offset Purge only data before specified offset.\n"
|
||||
" Offsets are in bytes or with a K/M/G/T unit suffix, decimal or 0x hex.\n"
|
||||
" --client_wait_up_timeout 16 Timeout for waiting until PGs are up in seconds.\n"
|
||||
"\n"
|
||||
"vitastor-cli merge-data <from> <to> [--target <target>]\n"
|
||||
@@ -145,15 +146,12 @@ static const char* help_text =
|
||||
" --min-offset, --max-offset\n"
|
||||
" Restrict listing to specific offsets inside inodes.\n"
|
||||
"\n"
|
||||
"vitastor-cli raw-ls --pool_id ID (--pg_num NUM|--offset NUM) [OPTIONS]\n"
|
||||
"vitastor-cli raw-ls [OPTIONS]\n"
|
||||
" Find object(s) in the cluster using raw secondary listing operations. Options:\n"
|
||||
" [--min_inode NUM] [--max_inode NUM] [--offset NUM] [--pg_num NUM] [--pg_count COUNT]\n"
|
||||
" [--pg_stripe_size NUM] [--osds 1,2,3,...]\n"
|
||||
" --pool_id (numeric pool ID) is required. --pg_num is required unless --offset is given,\n"
|
||||
" in which case the PG is calculated from the offset. --pg_count, --pg_stripe_size and\n"
|
||||
" --osds default to the pool configuration and are required if the pool does not exist.\n"
|
||||
"\n"
|
||||
"vitastor-cli fix [--objects <objects>] --bad-osds <osds> [--part <part>] [--check no]\n"
|
||||
"vitastor-cli fix [--objects <objects>] [--bad-osds <osds>] [--part <part>] [--check no]\n"
|
||||
" Fix inconsistent objects in the cluster by deleting some copies.\n"
|
||||
" --objects <objects>\n"
|
||||
" Objects to fix, either in plain text or JSON format. If not specified,\n"
|
||||
@@ -161,7 +159,7 @@ static const char* help_text =
|
||||
" Plain text format: 0x<inode>:0x<stripe> <any delimiter> 0x<inode>:0x<stripe> ...\n"
|
||||
" JSON format: [{\"inode\":\"0x...\",\"stripe\":\"0x...\"},...]\n"
|
||||
" --bad-osds <osds>\n"
|
||||
" Required. Remove inconsistent copies/parts of objects from these OSDs, effectively\n"
|
||||
" Remove inconsistent copies/parts of objects from these OSDs, effectively\n"
|
||||
" marking them bad and allowing Vitastor to recover objects from other copies.\n"
|
||||
" --part <number>\n"
|
||||
" Only remove EC part <number> (from 0 to pg_size-1), required for extreme\n"
|
||||
@@ -173,19 +171,16 @@ static const char* help_text =
|
||||
"vitastor-cli alloc-osd\n"
|
||||
" Allocate a new OSD number and reserve it by creating empty /osd/stats/<n> key.\n"
|
||||
"\n"
|
||||
"vitastor-cli rm-osd [-f|--force] [--allow-data-loss] [--allow-up] [--dry-run] <osd_id> [osd_id...]\n"
|
||||
"vitastor-cli rm-osd [--force] [--allow-data-loss] [--dry-run] <osd_id> [osd_id...]\n"
|
||||
" Remove metadata and configuration for specified OSD(s) from etcd.\n"
|
||||
" Refuses to remove OSDs that are up or whose removal makes PGs degraded, offline or incomplete.\n"
|
||||
" -f|--force Remove OSDs even if they are up or PGs become degraded or offline.\n"
|
||||
" --allow-data-loss Remove OSDs even if PGs or objects become incomplete (DATA LOSS). Implies --force.\n"
|
||||
" --allow-up Remove OSDs even if they are up.\n"
|
||||
" --dry-run Only check if deletion is possible without data loss and\n"
|
||||
" redundancy degradation.\n"
|
||||
" Refuses to remove OSDs with data without --force and --allow-data-loss.\n"
|
||||
" With --dry-run only checks if deletion is possible without data loss and\n"
|
||||
" redundancy degradation.\n"
|
||||
"\n"
|
||||
"vitastor-cli osd-tree [-l|--long]\n"
|
||||
" Show current OSD tree, optionally with I/O statistics if -l is specified.\n"
|
||||
"\n"
|
||||
"vitastor-cli osds|ls-osds|ls-osd|osd-ls [-l|--long]\n"
|
||||
"vitastor-cli osds|ls-osd|osd-ls [-l|--long]\n"
|
||||
" Show current OSDs as list, optionally with I/O statistics if -l is specified.\n"
|
||||
"\n"
|
||||
"vitastor-cli modify-osd [--tags tag1,tag2,...] [--reweight <number>] [--noout true/false] <osd_number>\n"
|
||||
@@ -203,8 +198,8 @@ static const char* help_text =
|
||||
"\n"
|
||||
"vitastor-cli create-pool|pool-create <name> (-s <pg_size>|--ec <N>+<K>) -n <pg_count> [OPTIONS]\n"
|
||||
" Create a pool. Required parameters:\n"
|
||||
" -s|--pg_size R Number of replicas for replicated pools, from 1 to 64\n"
|
||||
" --ec N+K Number of data (N) and parity (K) chunks for erasure-coded pools, N+K from 3 to 64\n"
|
||||
" -s|--pg_size R Number of replicas for replicated pools\n"
|
||||
" --ec N+K Number of data (N) and parity (K) chunks for erasure-coded pools\n"
|
||||
" -n|--pg_count N PG count for the new pool (start with 10*<OSD count>/pg_size rounded to a power of 2)\n"
|
||||
" Optional parameters:\n"
|
||||
" --pg_minsize <number> R or N+K minus number of failures to tolerate without downtime\n"
|
||||
@@ -214,7 +209,7 @@ static const char* help_text =
|
||||
" --block_size 128k Put pool only on OSDs with this data block size\n"
|
||||
" --bitmap_granularity 4k Put pool only on OSDs with this logical sector size\n"
|
||||
" --immediate_commit all Put pool only on OSDs with this or larger immediate_commit (none < small < all)\n"
|
||||
" --level_placement <rules> Use additional failure domain rules (example: \"dc=112233\", \"\" to remove)\n"
|
||||
" --level_placement <rules> Use additional failure domain rules (example: \"dc=112233\")\n"
|
||||
" --raw_placement <rules> Specify raw PG generation rules (see documentation for details)\n"
|
||||
" --local_reads primary Local read policy for replicated pools: primary, nearest or random\n"
|
||||
" --primary_affinity_tags tags Prefer to put primary copies on OSDs with all specified tags\n"
|
||||
@@ -241,7 +236,6 @@ static const char* help_text =
|
||||
" [--immediate_commit <all|small|none>] [--pg_stripe_size <size>]\n"
|
||||
" These, however, can still be modified with -f|--force.\n"
|
||||
" See create-pool for parameter descriptions.\n"
|
||||
" Renaming with --name fails if another pool already has the new name.\n"
|
||||
" Examples:\n"
|
||||
" vitastor-cli modify-pool pool_A --name pool_B\n"
|
||||
" vitastor-cli modify-pool 2 --pg_size 4 -n 128\n"
|
||||
@@ -260,7 +254,7 @@ static const char* help_text =
|
||||
"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|user-modify --type <type> --groups group1,group2,... <username>\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 or admin. Default is client.\n"
|
||||
" --groups GROUPS Set user's groups.\n"
|
||||
|
||||
@@ -37,6 +37,7 @@ struct dd_in_info_t
|
||||
// in
|
||||
std::string iimg, ifile;
|
||||
bool in_direct = false;
|
||||
bool detect_size = true;
|
||||
|
||||
// out
|
||||
cli_result_t result;
|
||||
@@ -67,7 +68,10 @@ struct dd_in_info_t
|
||||
return;
|
||||
}
|
||||
in_granularity = pool_it->second.bitmap_granularity;
|
||||
in_size = iwatch->cfg.size;
|
||||
if (detect_size)
|
||||
{
|
||||
in_size = iwatch->cfg.size;
|
||||
}
|
||||
in_seekable = true;
|
||||
return;
|
||||
}
|
||||
@@ -88,31 +92,33 @@ struct dd_in_info_t
|
||||
{
|
||||
ifd = 0;
|
||||
}
|
||||
struct stat st;
|
||||
if (fstat(ifd, &st) < 0)
|
||||
if (detect_size)
|
||||
{
|
||||
result = (cli_result_t){ .err = errno, .text = "Failed to stat "+ifile+": "+std::string(strerror(errno)) };
|
||||
close(ifd);
|
||||
ifd = -1;
|
||||
return;
|
||||
}
|
||||
if (S_ISREG(st.st_mode))
|
||||
{
|
||||
in_size = st.st_size;
|
||||
}
|
||||
else if (S_ISBLK(st.st_mode))
|
||||
{
|
||||
if (ioctl(ifd, BLKGETSIZE64, &in_size) < 0)
|
||||
struct stat st;
|
||||
if (fstat(ifd, &st) < 0)
|
||||
{
|
||||
result = (cli_result_t){ .err = errno, .text = "Failed to get "+ifile+" size: "+std::string(strerror(errno)) };
|
||||
result = (cli_result_t){ .err = errno, .text = "Failed to stat "+ifile+": "+std::string(strerror(errno)) };
|
||||
close(ifd);
|
||||
ifd = -1;
|
||||
return;
|
||||
}
|
||||
if (S_ISREG(st.st_mode))
|
||||
{
|
||||
in_size = st.st_size;
|
||||
}
|
||||
else if (S_ISBLK(st.st_mode))
|
||||
{
|
||||
if (ioctl(ifd, BLKGETSIZE64, &in_size) < 0)
|
||||
{
|
||||
result = (cli_result_t){ .err = errno, .text = "Failed to get "+ifile+" size: "+std::string(strerror(errno)) };
|
||||
close(ifd);
|
||||
ifd = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
in_seekable = false;
|
||||
// a seekable character device has no size (st_size 0), read it like a pipe
|
||||
if (!S_ISCHR(st.st_mode) && lseek(ifd, 1, SEEK_SET) == (off_t)1)
|
||||
if (lseek(ifd, 1, SEEK_SET) == (off_t)1)
|
||||
{
|
||||
in_seekable = true;
|
||||
lseek(ifd, 0, SEEK_SET);
|
||||
@@ -169,7 +175,7 @@ struct dd_out_info_t
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool open_output(cli_tool_t *parent, int & state, int base_state, uint64_t oseek, uint64_t blocksize)
|
||||
bool open_output(cli_tool_t *parent, int & state, int base_state)
|
||||
{
|
||||
if (state == base_state)
|
||||
goto resume_1;
|
||||
@@ -206,13 +212,6 @@ struct dd_out_info_t
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if ((oseek % out_granularity) || (blocksize % out_granularity))
|
||||
{
|
||||
result = (cli_result_t){ .err = EINVAL, .text = "Unaligned write to Vitastor is not supported" };
|
||||
parent->cli->st_cli->close_watch(owatch);
|
||||
owatch = NULL;
|
||||
return true;
|
||||
}
|
||||
if (out_size % 4096)
|
||||
{
|
||||
out_size += (4096 - (out_size % 4096));
|
||||
@@ -289,13 +288,13 @@ resume_2:
|
||||
}
|
||||
if (ofile != "")
|
||||
{
|
||||
ofd = open(ofile.c_str(), (out_direct ? O_DIRECT : 0) | (out_append ? O_WRONLY|O_APPEND : O_RDWR) | (out_create ? O_CREAT : 0), 0666);
|
||||
ofd = open(ofile.c_str(), (out_direct ? O_DIRECT : 0) | (out_append ? O_APPEND : O_RDWR) | (out_create ? O_CREAT : 0), 0666);
|
||||
if (ofd < 0)
|
||||
{
|
||||
result = (cli_result_t){ .err = errno, .text = "Failed to open "+ofile+": "+std::string(strerror(errno)) };
|
||||
return true;
|
||||
}
|
||||
if (out_trunc && ftruncate(ofd, out_size ? out_size : oseek) < 0)
|
||||
if (out_trunc && ftruncate(ofd, out_size) < 0)
|
||||
{
|
||||
result = (cli_result_t){ .err = errno, .text = "Failed to truncate "+ofile+": "+std::string(strerror(errno)) };
|
||||
return true;
|
||||
@@ -315,16 +314,6 @@ resume_2:
|
||||
out_seekable = true;
|
||||
lseek(ofd, 0, SEEK_SET);
|
||||
}
|
||||
struct stat st;
|
||||
if (fstat(ofd, &st) < 0)
|
||||
{
|
||||
result = (cli_result_t){ .err = errno, .text = "Failed to stat "+(ofile != "" ? ofile : "stdout")+": "+std::string(strerror(errno)) };
|
||||
return true;
|
||||
}
|
||||
if (!S_ISREG(st.st_mode) && !S_ISBLK(st.st_mode))
|
||||
{
|
||||
end_fsync = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -406,7 +395,6 @@ struct cli_dd_t
|
||||
uint64_t written_size = 0;
|
||||
uint64_t written_progress = 0;
|
||||
timespec tv_begin = {}, tv_progress = {};
|
||||
bool progress_shown = false;
|
||||
int state = 0;
|
||||
int copy_error = 0;
|
||||
int in_waiting = 0, out_waiting = 0;
|
||||
@@ -425,7 +413,6 @@ struct cli_dd_t
|
||||
auto res = read(fd, buf, blocksize < to_skip ? blocksize : to_skip);
|
||||
if (res <= 0)
|
||||
{
|
||||
free(buf);
|
||||
return res == 0 ? -EPIPE : -errno;
|
||||
}
|
||||
to_skip -= res;
|
||||
@@ -735,12 +722,6 @@ struct cli_dd_t
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (end && !end_status && !parent->json_output)
|
||||
{
|
||||
if (progress_shown)
|
||||
fprintf(stderr, "\r\033[K");
|
||||
return;
|
||||
}
|
||||
timespec tv_now;
|
||||
clock_gettime(CLOCK_REALTIME, &tv_now);
|
||||
double sec_delta = ((tv_now.tv_sec - tv_progress.tv_sec) + (double)(tv_now.tv_nsec - tv_progress.tv_nsec)/1000000000.0);
|
||||
@@ -783,7 +764,6 @@ struct cli_dd_t
|
||||
format_size((uint64_t)(delta/sec_delta), true).c_str(),
|
||||
format_size((uint64_t)(written_size/sec_total), true).c_str()
|
||||
);
|
||||
progress_shown = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -846,12 +826,6 @@ struct cli_dd_t
|
||||
state = 100;
|
||||
return;
|
||||
}
|
||||
if (oinfo.ofile != "" && oinfo.out_append && oinfo.out_trunc)
|
||||
{
|
||||
result = (cli_result_t){ .err = EINVAL, .text = "conv=trunc and oflag=append can't be used together" };
|
||||
state = 100;
|
||||
return;
|
||||
}
|
||||
if ((iinfo.iimg != "" ? "i"+iinfo.iimg : "f"+iinfo.ifile) == (oinfo.oimg != "" ? "i"+oinfo.oimg : "f"+oinfo.ofile))
|
||||
{
|
||||
result = (cli_result_t){ .err = EINVAL, .text = "Input and output image/file can't be equal" };
|
||||
@@ -874,20 +848,13 @@ struct cli_dd_t
|
||||
state = 100;
|
||||
return;
|
||||
}
|
||||
if (iinfo.in_seekable && iseek >= iinfo.in_size)
|
||||
{
|
||||
iinfo.close_input(parent);
|
||||
result = (cli_result_t){ .err = EINVAL, .text = "Input seek position is beyond end of input" };
|
||||
state = 100;
|
||||
return;
|
||||
}
|
||||
if (!oinfo.out_size && (iinfo.in_seekable || bytelimit))
|
||||
if (!oinfo.out_size)
|
||||
{
|
||||
oinfo.out_size = oseek + (iinfo.in_seekable && (!bytelimit || iinfo.in_size-iseek < bytelimit) ? iinfo.in_size-iseek : bytelimit);
|
||||
}
|
||||
resume_1:
|
||||
resume_2:
|
||||
if (!oinfo.open_output(parent, state, 1, oseek, blocksize))
|
||||
if (!oinfo.open_output(parent, state, 1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -904,6 +871,11 @@ resume_2:
|
||||
goto close_end;
|
||||
}
|
||||
// Copy data
|
||||
if (iinfo.in_seekable && iseek >= iinfo.in_size)
|
||||
{
|
||||
result = (cli_result_t){ .err = EINVAL, .text = "Input seek position is beyond end of input" };
|
||||
goto close_end;
|
||||
}
|
||||
if (!iinfo.iwatch && !iinfo.in_seekable && iseek)
|
||||
{
|
||||
// Read and ignore some data from input
|
||||
@@ -917,8 +889,9 @@ resume_2:
|
||||
in_iodepth = iinfo.in_seekable ? iodepth : 1;
|
||||
out_iodepth = oinfo.out_seekable ? iodepth : 1;
|
||||
write_zero = write_zero || !oinfo.out_seekable;
|
||||
oinfo.end_fsync = oinfo.end_fsync && oinfo.out_seekable;
|
||||
read_offset = 0;
|
||||
read_end = iinfo.in_seekable ? iinfo.in_size-iseek : UINT64_MAX;
|
||||
read_end = iinfo.in_seekable ? iinfo.in_size-iseek : 0;
|
||||
if (oinfo.out_size && (!read_end || read_end > oinfo.out_size-oseek))
|
||||
read_end = oinfo.out_size-oseek;
|
||||
if (bytelimit && (!read_end || read_end > bytelimit))
|
||||
@@ -1027,14 +1000,10 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_dd(json11::Json cfg)
|
||||
if (!dd->iodepth)
|
||||
dd->iodepth = 4;
|
||||
if (cfg["status"] == "none")
|
||||
{
|
||||
dd->end_status = false;
|
||||
progress = false;
|
||||
}
|
||||
else if (cfg["status"] == "noxfer")
|
||||
dd->end_status = false;
|
||||
else if (cfg["status"] == "progress")
|
||||
progress = true;
|
||||
dd->iinfo.detect_size = cfg["size"].is_null();
|
||||
dd->oinfo.out_size = parse_size(cfg["size"].as_string());
|
||||
dd->conv = explode_json(",", cfg["conv"]);
|
||||
dd->iflag = explode_json(",", cfg["iflag"]);
|
||||
|
||||
@@ -41,8 +41,6 @@ struct cli_describe_t
|
||||
std::vector<osd_num_t> only_osds;
|
||||
uint64_t min_inode = 0, max_inode = 0;
|
||||
uint64_t min_offset = 0, max_offset = 0;
|
||||
std::map<osd_num_t, std::string> skipped_pgs;
|
||||
bool incomplete = false;
|
||||
|
||||
cli_tool_t *parent = NULL;
|
||||
int state = 0;
|
||||
@@ -57,13 +55,6 @@ struct cli_describe_t
|
||||
return state == 100;
|
||||
}
|
||||
|
||||
bool is_osd_up(osd_num_t osd_num)
|
||||
{
|
||||
auto & peer_states = parent->cli->st_cli->peer_states;
|
||||
auto peer_it = peer_states.find(osd_num);
|
||||
return peer_it != peer_states.end() && !peer_it->second.is_null();
|
||||
}
|
||||
|
||||
void parse_options(json11::Json cfg)
|
||||
{
|
||||
uint64_t pool_id;
|
||||
@@ -92,18 +83,19 @@ struct cli_describe_t
|
||||
}
|
||||
only_pg = cfg["pg"].uint64_value();
|
||||
min_inode = cfg["inode"].uint64_value();
|
||||
min_offset = stoull_full(cfg["min_offset"].string_value(), 0);
|
||||
max_offset = stoull_full(cfg["max_offset"].string_value(), 0);
|
||||
if (min_inode)
|
||||
{
|
||||
if (!INODE_POOL(min_inode))
|
||||
min_inode |= (uint64_t)only_pool << (64-POOL_ID_BITS);
|
||||
max_inode = min_inode;
|
||||
min_offset = max_offset = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
min_inode = stoull_full(cfg["min_inode"].string_value(), 0); // to support 0x...
|
||||
max_inode = stoull_full(cfg["max_inode"].string_value(), 0);
|
||||
min_offset = stoull_full(cfg["min_offset"].string_value(), 0);
|
||||
max_offset = stoull_full(cfg["max_offset"].string_value(), 0);
|
||||
if (!min_inode && !max_inode && only_pool)
|
||||
{
|
||||
min_inode = (uint64_t)only_pool << (64-POOL_ID_BITS);
|
||||
@@ -111,9 +103,6 @@ struct cli_describe_t
|
||||
(((uint64_t)1 << (64-POOL_ID_BITS)) - 1);
|
||||
}
|
||||
}
|
||||
// The OSD ends the listing at (max_inode, max_offset) inclusive
|
||||
if (max_inode && cfg["max_offset"].is_null())
|
||||
max_offset = UINT64_MAX;
|
||||
only_osds = parse_uint64_list(cfg["osds"]);
|
||||
object_state = stoull_full(cfg["object_state"].string_value(), 0);
|
||||
if (!object_state && cfg["object_state"].is_string())
|
||||
@@ -169,43 +158,10 @@ struct cli_describe_t
|
||||
if (pp.first >= min_pool && (!max_pool || pp.first <= max_pool))
|
||||
{
|
||||
for (auto & pgp: pp.second.pg_config)
|
||||
{
|
||||
if (only_pool && only_pg && (pp.first != only_pool || pgp.first != only_pg))
|
||||
continue;
|
||||
if (is_osd_up(pgp.second.cur_primary))
|
||||
only_osds.push_back(pgp.second.cur_primary);
|
||||
else
|
||||
{
|
||||
auto & pgs = skipped_pgs[pgp.second.cur_primary];
|
||||
pgs += (pgs == "" ? "" : ", ")+std::to_string(pp.first)+"/"+std::to_string(pgp.first);
|
||||
}
|
||||
}
|
||||
only_osds.push_back(pgp.second.cur_primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<osd_num_t> filtered_osds;
|
||||
for (auto osd_num: only_osds)
|
||||
{
|
||||
if (is_osd_up(osd_num))
|
||||
filtered_osds.push_back(osd_num);
|
||||
else
|
||||
skipped_pgs[osd_num] = "";
|
||||
}
|
||||
only_osds = filtered_osds;
|
||||
}
|
||||
// Requests to an OSD which is down are never answered
|
||||
for (auto & sk: skipped_pgs)
|
||||
{
|
||||
if (!sk.first)
|
||||
fprintf(stderr, "No primary OSD for PG %s, skipping\n", sk.second.c_str());
|
||||
else if (sk.second != "")
|
||||
fprintf(stderr, "OSD %ju is down, skipping PG %s\n", sk.first, sk.second.c_str());
|
||||
else
|
||||
fprintf(stderr, "OSD %ju is down, skipping it\n", sk.first);
|
||||
incomplete = true;
|
||||
}
|
||||
remove_duplicates(only_osds);
|
||||
parent->cli->init_msgr();
|
||||
if (parent->json_output && parent->is_command_line)
|
||||
@@ -234,7 +190,6 @@ struct cli_describe_t
|
||||
{
|
||||
if (op->reply.hdr.retval < 0)
|
||||
{
|
||||
incomplete = true;
|
||||
fprintf(
|
||||
stderr, "Failed to describe objects on OSD %ju (retval=%jd)\n",
|
||||
osd_num, op->reply.hdr.retval
|
||||
@@ -242,7 +197,6 @@ struct cli_describe_t
|
||||
}
|
||||
else if (op->reply.describe.result_bytes != op->reply.hdr.retval * sizeof(osd_reply_describe_item_t))
|
||||
{
|
||||
incomplete = true;
|
||||
fprintf(
|
||||
stderr, "Invalid response size from OSD %ju (expected %ju bytes, got %ju bytes)\n",
|
||||
osd_num, op->reply.hdr.retval * sizeof(osd_reply_describe_item_t), op->reply.describe.result_bytes
|
||||
@@ -309,13 +263,6 @@ struct cli_describe_t
|
||||
{
|
||||
result.data = describe_items;
|
||||
}
|
||||
if (incomplete)
|
||||
{
|
||||
result.err = EIO;
|
||||
if (parent->json_output && parent->is_command_line)
|
||||
parent->json_output = false;
|
||||
result.text = "The listing is incomplete, see the messages above";
|
||||
}
|
||||
state = 100;
|
||||
describe_items.clear();
|
||||
}
|
||||
|
||||
@@ -21,20 +21,12 @@ struct cli_fix_t
|
||||
json11::Json options;
|
||||
cli_result_t result;
|
||||
json11::Json::array fix_result;
|
||||
bool incomplete = false;
|
||||
|
||||
bool is_done()
|
||||
{
|
||||
return state == 100;
|
||||
}
|
||||
|
||||
bool is_osd_up(osd_num_t osd_num)
|
||||
{
|
||||
auto & peer_states = parent->cli->st_cli->peer_states;
|
||||
auto peer_it = peer_states.find(osd_num);
|
||||
return peer_it != peer_states.end() && !peer_it->second.is_null();
|
||||
}
|
||||
|
||||
void parse_objects_str(std::string str)
|
||||
{
|
||||
str = trim(str);
|
||||
@@ -131,20 +123,6 @@ struct cli_fix_t
|
||||
state = 100;
|
||||
return;
|
||||
}
|
||||
{
|
||||
std::string down_osds;
|
||||
for (auto osd_num: bad_osds)
|
||||
{
|
||||
if (!is_osd_up(osd_num))
|
||||
down_osds += (down_osds == "" ? "" : ", ")+std::to_string(osd_num);
|
||||
}
|
||||
if (down_osds != "")
|
||||
{
|
||||
result = (cli_result_t){ .err = EAGAIN, .text = "Bad OSD(s) "+down_osds+" are down, can't remove object copies from them" };
|
||||
state = 100;
|
||||
return;
|
||||
}
|
||||
}
|
||||
remove_duplicates(objects);
|
||||
parent->cli->init_msgr();
|
||||
resume_1:
|
||||
@@ -160,7 +138,6 @@ struct cli_fix_t
|
||||
if (pool_cfg_it == parent->cli->st_cli->pool_config.end())
|
||||
{
|
||||
fprintf(stderr, "Object %jx:%jx is from unknown pool\n", obj.inode, obj.stripe);
|
||||
incomplete = true;
|
||||
continue;
|
||||
}
|
||||
auto & pool_cfg = pool_cfg_it->second;
|
||||
@@ -173,20 +150,9 @@ struct cli_fix_t
|
||||
stderr, "Object %jx:%jx is from PG %u/%u which is not currently active\n",
|
||||
obj.inode, obj.stripe, pool_cfg_it->first, pg_num
|
||||
);
|
||||
incomplete = true;
|
||||
continue;
|
||||
}
|
||||
osd_num_t primary_osd = pg_it->second.cur_primary;
|
||||
if (!is_osd_up(primary_osd))
|
||||
{
|
||||
// Requests to an OSD which is down are never answered
|
||||
fprintf(
|
||||
stderr, "Object %jx:%jx is from PG %u/%u whose primary OSD %ju is down\n",
|
||||
obj.inode, obj.stripe, pool_cfg_it->first, pg_num, primary_osd
|
||||
);
|
||||
incomplete = true;
|
||||
continue;
|
||||
}
|
||||
// Describe -> Remove some copies -> Scrub again
|
||||
osd_op_t *op = new osd_op_t;
|
||||
op->req = (osd_any_op_t){
|
||||
@@ -206,7 +172,6 @@ struct cli_fix_t
|
||||
if (op->reply.hdr.retval < 0 || op->reply.describe.result_bytes != op->reply.hdr.retval * sizeof(osd_reply_describe_item_t))
|
||||
{
|
||||
fprintf(stderr, "Failed to describe objects on OSD %ju (retval=%jd)\n", primary_osd, op->reply.hdr.retval);
|
||||
incomplete = true;
|
||||
parent->waiting--;
|
||||
loop();
|
||||
}
|
||||
@@ -243,7 +208,6 @@ struct cli_fix_t
|
||||
(*rm_count)--;
|
||||
if (rm_op->reply.hdr.retval < 0)
|
||||
{
|
||||
incomplete = true;
|
||||
fprintf(
|
||||
stderr, "Failed to remove object %jx:%jx from OSD %ju (retval=%jd)\n",
|
||||
rm_op->req.sec_del.oid.inode, rm_op->req.sec_del.oid.stripe,
|
||||
@@ -288,7 +252,6 @@ struct cli_fix_t
|
||||
{
|
||||
if (scrub_op->reply.hdr.retval < 0 && scrub_op->reply.hdr.retval != -ENOENT)
|
||||
{
|
||||
incomplete = true;
|
||||
fprintf(
|
||||
stderr, "Failed to scrub %jx:%jx on OSD %ju (retval=%jd)\n",
|
||||
obj.inode, obj.stripe, primary_osd, scrub_op->reply.hdr.retval
|
||||
@@ -326,11 +289,6 @@ struct cli_fix_t
|
||||
{
|
||||
result.data = fix_result;
|
||||
}
|
||||
if (incomplete)
|
||||
{
|
||||
result.err = EIO;
|
||||
result.text = "Some objects were not fixed, see the messages above";
|
||||
}
|
||||
state = 100;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -250,17 +250,11 @@ resume_1:
|
||||
|
||||
json11::Json::array to_tree(const json11::Json::array & array)
|
||||
{
|
||||
std::set<uint64_t> listed;
|
||||
for (const auto & item: array)
|
||||
{
|
||||
listed.insert(item["inode_id"].uint64_value());
|
||||
}
|
||||
std::map<uint64_t, json11::Json::array> children;
|
||||
for (const auto & item: array)
|
||||
{
|
||||
uint64_t parent_id = item["parent_inode_id"].uint64_value();
|
||||
// Layers with a filtered out parent are shown as roots
|
||||
children[listed.find(parent_id) != listed.end() ? parent_id : 0].push_back(item);
|
||||
children[parent_id].push_back(item);
|
||||
}
|
||||
json11::Json::array tree;
|
||||
std::function<void(uint64_t, const std::string &)> add_children = [&](uint64_t parent_id, const std::string & prefix)
|
||||
|
||||
@@ -287,20 +287,7 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_modify(json11::Json cfg)
|
||||
changer->parent = this;
|
||||
changer->image_name = cfg["image"].string_value();
|
||||
changer->new_name = cfg["rename"].string_value();
|
||||
if (!cfg["resize"].is_null())
|
||||
{
|
||||
bool ok = false;
|
||||
changer->new_size = parse_size(cfg["resize"].as_string(), &ok);
|
||||
if (!ok)
|
||||
{
|
||||
delete changer;
|
||||
return [size = cfg["resize"].as_string()](cli_result_t & result)
|
||||
{
|
||||
result = (cli_result_t){ .err = EINVAL, .text = "Invalid syntax for size: "+size };
|
||||
return true;
|
||||
};
|
||||
}
|
||||
}
|
||||
changer->new_size = parse_size(cfg["resize"].as_string());
|
||||
changer->force_size = cfg["force_size"].bool_value();
|
||||
changer->inc_size = cfg["inc_size"].bool_value();
|
||||
changer->force = cfg["force"].bool_value();
|
||||
|
||||
@@ -412,8 +412,6 @@ resume_1:
|
||||
load_osd_tree();
|
||||
if (parent->waiting > 0)
|
||||
return;
|
||||
if (state == 100)
|
||||
return;
|
||||
format_tree();
|
||||
state = 100;
|
||||
}
|
||||
|
||||
@@ -251,7 +251,7 @@ resume_1:
|
||||
{ "title", "MISPLACED" },
|
||||
});
|
||||
cols.push_back(json11::Json::object{
|
||||
{ "key", "fmt_degraded" },
|
||||
{ "key", "fmt_misplaced" },
|
||||
{ "title", "DEGRADED" },
|
||||
});
|
||||
cols.push_back(json11::Json::object{
|
||||
@@ -273,8 +273,6 @@ resume_1:
|
||||
load_pg_stats();
|
||||
if (parent->waiting > 0)
|
||||
return;
|
||||
if (state == 100)
|
||||
return;
|
||||
format_pgs();
|
||||
state = 100;
|
||||
}
|
||||
|
||||
@@ -103,22 +103,24 @@ std::string validate_pool_config(json11::Json::object & new_cfg, json11::Json ol
|
||||
// level=rule, level=rule, ...
|
||||
if (!value.is_object())
|
||||
{
|
||||
if (!value.is_null() && !value.is_string())
|
||||
{
|
||||
return key+" must be a string";
|
||||
}
|
||||
json11::Json::object obj;
|
||||
for (auto & item: explode(",", value.string_value(), true))
|
||||
{
|
||||
auto pair = explode("=", item, true);
|
||||
if (pair.size() != 2)
|
||||
if (pair.size() >= 2)
|
||||
{
|
||||
return "--level_placement must be <level>=<rule>[,<level>=<rule>...], but it is "+value.string_value();
|
||||
obj[pair[0]] = pair[1];
|
||||
}
|
||||
obj[pair[0]] = pair[1];
|
||||
}
|
||||
// empty value = remove the rules; null keeps the old ones from being merged back
|
||||
value = obj.size() ? json11::Json(obj) : json11::Json();
|
||||
if (obj.size())
|
||||
{
|
||||
value = obj;
|
||||
}
|
||||
else
|
||||
{
|
||||
new_cfg.erase(kv_it++);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (key == "osd_tags" || key == "primary_affinity_tags")
|
||||
@@ -167,10 +169,6 @@ std::string validate_pool_config(json11::Json::object & new_cfg, json11::Json ol
|
||||
{
|
||||
new_cfg.erase("local_reads");
|
||||
}
|
||||
if (new_cfg.find("level_placement") != new_cfg.end() && new_cfg["level_placement"].is_null())
|
||||
{
|
||||
new_cfg.erase("level_placement");
|
||||
}
|
||||
|
||||
// Prevent autovivification of object keys. Now we don't modify the config, we just check it
|
||||
json11::Json cfg = new_cfg;
|
||||
@@ -235,10 +233,9 @@ std::string validate_pool_config(json11::Json::object & new_cfg, json11::Json ol
|
||||
{
|
||||
return "PG size can't be smaller than 3 for EC/XOR pools";
|
||||
}
|
||||
// same limit as in etcd_state_client.cpp and mon/pool_config.js
|
||||
if (pg_size > 64)
|
||||
if (pg_size > 256)
|
||||
{
|
||||
return "PG size can't be greater than 64";
|
||||
return "PG size can't be greater than 256";
|
||||
}
|
||||
|
||||
// PG rules
|
||||
|
||||
@@ -116,21 +116,7 @@ resume_1:
|
||||
state = 100;
|
||||
return;
|
||||
}
|
||||
if (new_cfg["name"] != old_cfg["name"])
|
||||
{
|
||||
for (auto & pce: kv.value.object_items())
|
||||
{
|
||||
if (pce.second["name"] == new_cfg["name"])
|
||||
{
|
||||
result = (cli_result_t){
|
||||
.err = EEXIST,
|
||||
.text = "Pool with name \""+new_cfg["name"].string_value()+"\" already exists (ID "+pce.first+")",
|
||||
};
|
||||
state = 100;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (new_cfg.find("used_for_app") != new_cfg.end() && !force)
|
||||
{
|
||||
// Check that pool doesn't have images
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Vitaliy Filippov, 2019+
|
||||
// License: VNPL-1.1 (see README.md for details)
|
||||
|
||||
#include <ctype.h>
|
||||
#include "cli.h"
|
||||
#include "cluster_client.h"
|
||||
|
||||
@@ -23,6 +24,7 @@ struct rm_inode_t
|
||||
{
|
||||
uint64_t inode = 0;
|
||||
pool_id_t pool_id = 0;
|
||||
std::string pool_name;
|
||||
uint64_t min_offset = 0;
|
||||
uint64_t max_offset = 0;
|
||||
bool down_ok = false;
|
||||
@@ -302,6 +304,25 @@ struct rm_inode_t
|
||||
goto resume_1;
|
||||
if (state == 100)
|
||||
return;
|
||||
if (pool_name != "")
|
||||
{
|
||||
pool_id = 0;
|
||||
for (auto & pp: parent->cli->st_cli->pool_config)
|
||||
{
|
||||
if (pp.second.name == pool_name)
|
||||
{
|
||||
pool_id = pp.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!pool_id)
|
||||
{
|
||||
result = (cli_result_t){ .err = ENOENT, .text = "Pool "+pool_name+" does not exist" };
|
||||
state = 100;
|
||||
return;
|
||||
}
|
||||
inode = INODE_WITH_POOL(pool_id, inode);
|
||||
}
|
||||
if (!pool_id)
|
||||
{
|
||||
result = (cli_result_t){ .err = EINVAL, .text = "Pool is not specified" };
|
||||
@@ -317,39 +338,103 @@ struct rm_inode_t
|
||||
}
|
||||
};
|
||||
|
||||
// Decimal or 0x hex, without sign, octal or overflow
|
||||
static bool parse_rm_number(std::string s, uint64_t & value)
|
||||
{
|
||||
int base = 10;
|
||||
if (s.size() > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
|
||||
{
|
||||
s = s.substr(2);
|
||||
base = 16;
|
||||
}
|
||||
if (s == "" || s.find_first_not_of(base == 16 ? "0123456789abcdefABCDEF" : "0123456789") != std::string::npos)
|
||||
return false;
|
||||
errno = 0;
|
||||
value = strtoull(s.c_str(), NULL, base);
|
||||
return !errno;
|
||||
}
|
||||
|
||||
// Command-line values are strings with optional K/M/G/T suffixes, internal callers pass numbers
|
||||
static bool parse_rm_offset(const json11::Json & value, uint64_t & offset)
|
||||
{
|
||||
if (!value.is_string())
|
||||
{
|
||||
offset = value.uint64_value();
|
||||
return true;
|
||||
}
|
||||
std::string s = value.string_value();
|
||||
int shift = 0;
|
||||
switch (s.size() ? tolower(s.back()) : 0)
|
||||
{
|
||||
case 'k': shift = 10; break;
|
||||
case 'm': shift = 20; break;
|
||||
case 'g': shift = 30; break;
|
||||
case 't': shift = 40; break;
|
||||
}
|
||||
if (shift)
|
||||
s.pop_back();
|
||||
if (!parse_rm_number(s, offset) || offset > (UINT64_MAX >> shift))
|
||||
return false;
|
||||
offset <<= shift;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_rm_inode(const json11::Json & value, uint64_t & inode)
|
||||
{
|
||||
if (!value.is_string())
|
||||
{
|
||||
inode = value.uint64_value();
|
||||
return true;
|
||||
}
|
||||
return parse_rm_number(value.string_value(), inode);
|
||||
}
|
||||
|
||||
// A non-numeric string is a pool name, resolved after loading the pool configuration
|
||||
static bool parse_rm_pool(const json11::Json & value, uint64_t & pool_id, std::string & pool_name)
|
||||
{
|
||||
pool_id = value.uint64_value();
|
||||
if (value.is_string() && !pool_id && value.string_value() != "0")
|
||||
pool_name = value.string_value();
|
||||
return pool_id < POOL_ID_MAX && (!value.is_string() || value.string_value() != "");
|
||||
}
|
||||
|
||||
std::function<bool(cli_result_t &)> cli_tool_t::start_rm_data(json11::Json cfg)
|
||||
{
|
||||
uint64_t inode = 0, pool_id = 0, min_offset = 0, max_offset = 0;
|
||||
std::string pool_name, error;
|
||||
if (!parse_rm_inode(cfg["inode"], inode))
|
||||
error = "Invalid --inode value: "+cfg["inode"].as_string();
|
||||
else if (!parse_rm_pool(cfg["pool"], pool_id, pool_name))
|
||||
error = "Invalid --pool value: "+cfg["pool"].as_string();
|
||||
else if (!parse_rm_offset(cfg["min_offset"], min_offset))
|
||||
error = "Invalid --min-offset value: "+cfg["min_offset"].as_string();
|
||||
else if (!parse_rm_offset(cfg["max_offset"], max_offset))
|
||||
error = "Invalid --max-offset value: "+cfg["max_offset"].as_string();
|
||||
else if (!max_offset && cfg["max_offset"].is_string())
|
||||
error = "--max-offset 0 is invalid, omit it to remove data up to the end";
|
||||
else if (max_offset && min_offset >= max_offset)
|
||||
error = "--min-offset must be less than --max-offset";
|
||||
if (error != "")
|
||||
{
|
||||
return [error](cli_result_t & result)
|
||||
{
|
||||
result = (cli_result_t){ .err = EINVAL, .text = error };
|
||||
return true;
|
||||
};
|
||||
}
|
||||
auto remover = new rm_inode_t();
|
||||
remover->parent = this;
|
||||
remover->inode = cfg["inode"].uint64_value();
|
||||
remover->pool_id = cfg["pool"].uint64_value();
|
||||
remover->inode = inode;
|
||||
remover->pool_id = pool_id;
|
||||
remover->pool_name = pool_name;
|
||||
if (remover->pool_id)
|
||||
{
|
||||
remover->inode = (remover->inode & (((uint64_t)1 << (64-POOL_ID_BITS)) - 1)) | (((uint64_t)remover->pool_id) << (64-POOL_ID_BITS));
|
||||
}
|
||||
remover->down_ok = cfg["down_ok"].bool_value();
|
||||
remover->pool_id = INODE_POOL(remover->inode);
|
||||
bool min_max_ok = true;
|
||||
if (!cfg["min_offset"].is_null())
|
||||
{
|
||||
remover->min_offset = cfg["min_offset"].uint64_value();
|
||||
if (!remover->min_offset && cfg["min_offset"].as_string() != "0")
|
||||
min_max_ok = false;
|
||||
}
|
||||
if (!cfg["max_offset"].is_null())
|
||||
{
|
||||
remover->max_offset = cfg["max_offset"].uint64_value();
|
||||
if (!remover->max_offset && cfg["max_offset"].as_string() != "0")
|
||||
min_max_ok = false;
|
||||
}
|
||||
if (!min_max_ok)
|
||||
{
|
||||
return [](cli_result_t & result)
|
||||
{
|
||||
result = (cli_result_t){ .err = EINVAL, .text = "Invalid min_offset or max_offset" };
|
||||
return true;
|
||||
};
|
||||
}
|
||||
remover->min_offset = min_offset;
|
||||
remover->max_offset = max_offset;
|
||||
return [remover](cli_result_t & result)
|
||||
{
|
||||
remover->loop();
|
||||
|
||||
@@ -170,8 +170,7 @@ struct rm_osd_t
|
||||
}
|
||||
if (still_up.size() && !allow_up)
|
||||
error += (still_up.size() == 1 ? "OSD " : "OSDs ") + implode(", ", still_up) +
|
||||
(still_up.size() == 1 ? " is" : " are") + " still up. Use `vitastor-disk purge` to delete " +
|
||||
(still_up.size() == 1 ? "it" : "them") + ".\n";
|
||||
(still_up.size() == 1 ? "is" : "are") + " still up. Use `vitastor-disk purge` to delete them.\n";
|
||||
if (is_dataloss && !force_dataloss && !dry_run)
|
||||
error += "OSDs not deleted. Please move data to other OSDs or bypass this check with --allow-data-loss if you know what you are doing.\n";
|
||||
else if (is_warning && !force_warning && !dry_run)
|
||||
|
||||
@@ -664,7 +664,7 @@ void kv_cli_t::handle_cmd(const std::vector<std::string> & cmd, std::function<vo
|
||||
kv_cli_list_t *lst = new kv_cli_list_t;
|
||||
std::string start = cmd.size() >= 2 ? cmd[1] : "";
|
||||
std::string end = cmd.size() >= 3 ? cmd[2] : "";
|
||||
lst->handle = db->list_start(start, end);
|
||||
lst->handle = db->list_start(start);
|
||||
lst->db = db;
|
||||
lst->format = opname == "dump" ? 1 : (opname == "dumpjson" ? 2 : 0);
|
||||
lst->cb = std::move(cb);
|
||||
|
||||
@@ -190,8 +190,6 @@ struct kv_op_t
|
||||
kv_db_t *db;
|
||||
int opcode;
|
||||
std::string key, value;
|
||||
// for list: stop before the first key >= list_end, unless list_end is empty
|
||||
std::string list_end;
|
||||
int res;
|
||||
bool in_next = false;
|
||||
bool next_retry_pending = false;
|
||||
@@ -2023,12 +2021,6 @@ void kv_op_t::next_get()
|
||||
}
|
||||
if (kv_it != blk->data.end())
|
||||
{
|
||||
if (list_end != "" && kv_it->first >= list_end)
|
||||
{
|
||||
// End of the listing range
|
||||
finish(-ENOENT);
|
||||
return;
|
||||
}
|
||||
// Send this item
|
||||
assert(blk->type == KV_LEAF || blk->type == KV_LEAF_SPLIT);
|
||||
this->res = 0;
|
||||
@@ -2040,12 +2032,6 @@ void kv_op_t::next_get()
|
||||
// Find next block
|
||||
else if (blk->type == KV_LEAF_SPLIT)
|
||||
{
|
||||
if (list_end != "" && blk->right_half >= list_end)
|
||||
{
|
||||
// The right half is beyond the listing range
|
||||
finish(-ENOENT);
|
||||
return;
|
||||
}
|
||||
// Left half finished, go to the right
|
||||
recheck_policy = KV_RECHECK_LEAF;
|
||||
key = blk->right_half;
|
||||
@@ -2077,12 +2063,6 @@ void kv_op_t::next_go_up()
|
||||
// Block can be updated and key_lt may be lower. Don't rewind it
|
||||
key = blk->key_lt;
|
||||
}
|
||||
if (list_end != "" && key >= list_end)
|
||||
{
|
||||
// Next keys are beyond the listing range, don't read the next block
|
||||
finish(-ENOENT);
|
||||
return;
|
||||
}
|
||||
skip_equal = false;
|
||||
path.pop_back();
|
||||
cur_level--;
|
||||
@@ -2214,11 +2194,6 @@ void vitastorkv_dbw_t::del(const std::string & key, std::function<void(int res)>
|
||||
}
|
||||
|
||||
void* vitastorkv_dbw_t::list_start(const std::string & start)
|
||||
{
|
||||
return list_start(start, "");
|
||||
}
|
||||
|
||||
void* vitastorkv_dbw_t::list_start(const std::string & start, const std::string & end)
|
||||
{
|
||||
if (!db->inode_id || db->closing)
|
||||
return NULL;
|
||||
@@ -2226,7 +2201,6 @@ void* vitastorkv_dbw_t::list_start(const std::string & start, const std::string
|
||||
op->db = db;
|
||||
op->opcode = KV_LIST;
|
||||
op->key = start;
|
||||
op->list_end = end;
|
||||
op->callback = [](kv_op_t *){};
|
||||
op->exec();
|
||||
return op;
|
||||
|
||||
@@ -40,8 +40,6 @@ struct __attribute__((visibility("default"))) vitastorkv_dbw_t
|
||||
std::function<bool(int res, const std::string & value)> cas_compare = NULL);
|
||||
|
||||
void* list_start(const std::string & start);
|
||||
// list keys >= start and < end (all keys >= start if end is empty)
|
||||
void* list_start(const std::string & start, const std::string & end);
|
||||
void list_next(void *handle, std::function<void(int res, const std::string & key, const std::string & value)> cb);
|
||||
void list_close(void *handle);
|
||||
|
||||
|
||||
@@ -110,16 +110,6 @@ std::string kv_direntry_key(uint64_t dir_ino, const std::string & filename)
|
||||
return (char*)key + filename;
|
||||
}
|
||||
|
||||
std::string kv_prefix_end(const std::string & prefix)
|
||||
{
|
||||
std::string end = prefix;
|
||||
while (end.size() && (uint8_t)end.back() == 0xff)
|
||||
end.pop_back();
|
||||
if (end.size())
|
||||
end.back()++;
|
||||
return end;
|
||||
}
|
||||
|
||||
std::string kv_direntry_filename(const std::string & key)
|
||||
{
|
||||
// decode as: d <length> <hex dir_ino> / <filename>
|
||||
|
||||
@@ -130,8 +130,6 @@ int kv_map_type(const std::string & type);
|
||||
fattr3 get_kv_attributes(nfs_proxy_t *proxy, uint64_t ino, json11::Json attrs);
|
||||
std::string kv_direntry_key(uint64_t dir_ino, const std::string & filename);
|
||||
std::string kv_direntry_filename(const std::string & key);
|
||||
// The first key after all keys starting with prefix, to stop a listing at the end of the prefix
|
||||
std::string kv_prefix_end(const std::string & prefix);
|
||||
std::string kv_inode_prefix_key(uint64_t ino, const char *prefix);
|
||||
std::string kv_inode_key(uint64_t ino);
|
||||
uint64_t kv_key_inode(const std::string & key, int prefix_len = 1);
|
||||
|
||||
@@ -317,7 +317,7 @@ void kv_fs_defrag_all_t::run(int st)
|
||||
else if (st == 6)
|
||||
goto resume_6;
|
||||
clock_gettime(CLOCK_REALTIME, &now);
|
||||
list_shared = proxy->db->list_start("shared", kv_prefix_end("shared"));
|
||||
list_shared = proxy->db->list_start("shared");
|
||||
proxy->db->list_next(list_shared, [this](int res, const std::string & key, const std::string & value)
|
||||
{
|
||||
if (res == -ENOENT || key.substr(0, 6) != "shared")
|
||||
@@ -482,7 +482,7 @@ void kv_fs_state_t::upgrade_db(std::function<void(int)> cb)
|
||||
return;
|
||||
}
|
||||
// Create missing shared inode index keys
|
||||
auto list_inodes = proxy->db->list_start("i", kv_prefix_end("i"));
|
||||
auto list_inodes = proxy->db->list_start("i");
|
||||
proxy->db->list_next(list_inodes, [=](int res, const std::string & key, const std::string & value)
|
||||
{
|
||||
if (res == -ENOENT || key.substr(0, 1) != "i" || key == "id")
|
||||
|
||||
@@ -241,7 +241,7 @@ resume_2:
|
||||
st->cookieverf = ((uint64_t)lrand48() | ((uint64_t)lrand48() << 31) | ((uint64_t)lrand48() << 62));
|
||||
}
|
||||
st->getattr_cur = st->entries.size();
|
||||
st->list_handle = st->self->parent->db->list_start(st->start, kv_prefix_end(st->prefix));
|
||||
st->list_handle = st->self->parent->db->list_start(st->start);
|
||||
st->self->parent->db->list_next(st->list_handle, [=](int res, const std::string & key, const std::string & value)
|
||||
{
|
||||
st->res = res;
|
||||
|
||||
@@ -181,7 +181,7 @@ resume_3:
|
||||
if (st->is_rmdir)
|
||||
{
|
||||
// (4) Check if the directory actually is not empty
|
||||
st->list_handle = st->self->parent->db->list_start(kv_direntry_key(st->ino, ""), kv_prefix_end(kv_direntry_key(st->ino, "")));
|
||||
st->list_handle = st->self->parent->db->list_start(kv_direntry_key(st->ino, ""));
|
||||
st->self->parent->db->list_next(st->list_handle, [st](int res, const std::string & key, const std::string & value)
|
||||
{
|
||||
st->res = res;
|
||||
|
||||
@@ -138,7 +138,7 @@ resume_2:
|
||||
{
|
||||
// Check that the destination directory is empty
|
||||
st->new_dir_prefix = kv_direntry_key(st->new_direntry["ino"].uint64_value(), "");
|
||||
st->list_handle = st->self->parent->db->list_start(st->new_dir_prefix, kv_prefix_end(st->new_dir_prefix));
|
||||
st->list_handle = st->self->parent->db->list_start(st->new_dir_prefix);
|
||||
st->self->parent->db->list_next(st->list_handle, [st](int res, const std::string & key, const std::string & value)
|
||||
{
|
||||
st->res = res;
|
||||
|
||||
@@ -558,11 +558,13 @@ void osd_t::scrub_check_results(osd_op_t *cur_op)
|
||||
}
|
||||
else
|
||||
{
|
||||
int total = 0;
|
||||
for (int i = 0; i < op_data->stripe_count; i++)
|
||||
{
|
||||
if (!op_data->stripes[i].not_exists)
|
||||
{
|
||||
// use "missing" flag to distinguish actual read errors and inconsistent chunks
|
||||
total++;
|
||||
op_data->stripes[i].missing = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,7 +371,7 @@ int main(int narg, char *args[])
|
||||
auto ringloop = new ring_loop_t(RINGLOOP_DEFAULT_SIZE);
|
||||
auto epmgr = new epoll_manager_t(ringloop);
|
||||
auto cli = cluster_client_t::create(ringloop, epmgr->tfd, cfg);
|
||||
test_data_t data = { .cli = cli, .ringloop = ringloop, .inode = inode };
|
||||
test_data_t data = { .cli = cli, .ringloop = ringloop, inode = inode };
|
||||
// Wait for init
|
||||
cli->on_ready([&]() { data.done = true; });
|
||||
data.wait();
|
||||
|
||||
@@ -1122,37 +1122,6 @@ void test_pg_config_disappears()
|
||||
printf("[ok] test_pg_config_disappears\n");
|
||||
}
|
||||
|
||||
void test_sync_released_during_continue_ops()
|
||||
{
|
||||
json11::Json config;
|
||||
timerfd_manager_t *tfd = new timerfd_manager_t([](int fd, bool wr, std::function<void(int, int)> callback){});
|
||||
etcd_mock_t etcd_store;
|
||||
etcd_state_client_mock_t *mock = new etcd_state_client_mock_t(etcd_store);
|
||||
mock->pause();
|
||||
cluster_client_t *cli = new cluster_client_t(NULL, tfd, config, std::unique_ptr<etcd_state_client_t>(mock));
|
||||
|
||||
configure_single_pg_pool(mock);
|
||||
mock->resume();
|
||||
// The OSD isn't connected: the write waits, and the SYNC waits for the write
|
||||
int *r1 = test_write(cli, 0, 4096, 0x55);
|
||||
int *r2 = test_sync(cli);
|
||||
|
||||
// The write fails in continue_ops(), and the SYNC it releases completes and is freed at once:
|
||||
// continue_ops() must not go on from the write to the SYNC
|
||||
can_complete(r1);
|
||||
can_complete(r2);
|
||||
mock->set("/vitastor/pg/config", json11::Json::object {
|
||||
{ "items", json11::Json::object { } }
|
||||
});
|
||||
assert(*r1 == -EINVAL);
|
||||
check_completed(r2);
|
||||
delete r1;
|
||||
|
||||
delete cli;
|
||||
delete tfd;
|
||||
printf("[ok] test_sync_released_during_continue_ops\n");
|
||||
}
|
||||
|
||||
void test_snapshot_over_nonexistent_pool()
|
||||
{
|
||||
json11::Json config;
|
||||
@@ -1357,7 +1326,6 @@ int main(int narg, char *args[])
|
||||
test_sync_fail_dirty_state_loss();
|
||||
cluster_client_test_t::test_vault();
|
||||
test_pg_config_disappears();
|
||||
test_sync_released_during_continue_ops();
|
||||
test_snapshot_over_nonexistent_pool();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@ SCHEME=ec ./test_change_pg_count.sh
|
||||
|
||||
./test_create_nomaxid.sh
|
||||
|
||||
./test_modify_pool.sh
|
||||
|
||||
./test_etcd_fail.sh
|
||||
ANTIETCD=1 ./test_etcd_fail.sh
|
||||
|
||||
@@ -66,10 +64,7 @@ OLD=1 ./test_move_reappear.sh
|
||||
|
||||
./test_rm.sh
|
||||
|
||||
./test_modify_image_resize.sh
|
||||
|
||||
./test_rm_degraded.sh
|
||||
./test_describe.sh
|
||||
|
||||
./test_snapshot_chain.sh
|
||||
SCHEME=ec ./test_snapshot_chain.sh
|
||||
@@ -85,7 +80,6 @@ OLD=1 SCHEME=ec ./test_snapshot_down.sh
|
||||
|
||||
./test_kv_stress.sh
|
||||
IMMEDIATE_COMMIT=1 ./test_kv_stress.sh
|
||||
./test_kv_list_bounds.sh
|
||||
|
||||
./test_splitbrain.sh
|
||||
|
||||
@@ -101,8 +95,6 @@ OLD=1 SCHEME=ec IMMEDIATE_COMMIT=1 ./test_rebalance_verify.sh
|
||||
|
||||
./test_dd.sh
|
||||
|
||||
./test_cli_help.sh
|
||||
|
||||
./test_root_node.sh
|
||||
|
||||
./test_switch_primary.sh
|
||||
@@ -130,7 +122,6 @@ OLD=1 ./test_corrupt_all.sh
|
||||
|
||||
./test_reweight_half.sh
|
||||
./test_snapshot_pool2.sh
|
||||
./test_ls_tree.sh
|
||||
./test_snapshot_read_bitmap.sh
|
||||
|
||||
TEST_NAME=csum_32k_dmj OSD_ARGS="--data_csum_type crc32c --csum_block_size 32k --inmemory_metadata false --inmemory_journal false" OFFSET_ARGS=$OSD_ARGS ./test_heal.sh
|
||||
@@ -185,7 +176,6 @@ TEST_NAME=old_csum_4k OLD=1 OSD_ARGS="--data_csum_type crc32c" OFFSET_ARGS=
|
||||
./test_nfs.sh
|
||||
./test_nfs_unaligned_append.sh
|
||||
./test_nfs_long_readdir.sh
|
||||
./test_nfs_list_bounds.sh
|
||||
|
||||
./test_write_encrypted.sh
|
||||
SCHEME=ec ./test_write_encrypted.sh
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
# Test that vitastor-cli --help knows every command name and alias the CLI accepts
|
||||
|
||||
. `dirname $0`/common.sh
|
||||
|
||||
for cmd in $(grep -o 'cmd\[0\] == "[^"]*"' src/cmd/cli.cpp | sed 's/.*== "//; s/"$//'); do
|
||||
build/src/cmd/vitastor-cli --help $cmd > ./testdata/help.txt 2>&1 || true
|
||||
if grep -q '^Unknown command' ./testdata/help.txt; then
|
||||
format_error "vitastor-cli --help $cmd does not know the command"
|
||||
fi
|
||||
done
|
||||
|
||||
format_green OK
|
||||
@@ -24,85 +24,4 @@ $VITASTOR_CLI dd iimg=testimg2 iodepth=4 bs=1M count=128 > ./testdata/testfile1
|
||||
diff ./testdata/testfile ./testdata/testfile1
|
||||
rm ./testdata/testfile1
|
||||
|
||||
# size= sets the output size, the input size is still detected
|
||||
$VITASTOR_CLI dd iimg=testimg oimg=testimg3 iodepth=4 size=192M
|
||||
$VITASTOR_CLI dd if=./testdata/testfile oimg=testimg4 iodepth=4 size=192M
|
||||
for img in testimg3 testimg4; do
|
||||
size=$($VITASTOR_CLI ls --json | jq -r '.[] | select(.name == "'$img'") | .size')
|
||||
[[ "$size" = $((192*1024*1024)) ]] || format_error "dd size=192M created $img with size $size"
|
||||
$VITASTOR_CLI dd iimg=$img iodepth=4 bs=1M count=128 > ./testdata/testfile1
|
||||
diff ./testdata/testfile ./testdata/testfile1
|
||||
rm ./testdata/testfile1
|
||||
done
|
||||
|
||||
# skip beyond the end of input fails before the output image is created
|
||||
rc=0
|
||||
$VITASTOR_CLI dd iimg=testimg oimg=testimg5 bs=1M skip=200 count=1 || rc=$?
|
||||
[[ $rc = 22 ]] || format_error "dd skip beyond the end of input returned $rc instead of EINVAL"
|
||||
if $VITASTOR_CLI ls --json | jq -e '.[] | select(.name == "testimg5")'; then
|
||||
format_error "dd skip beyond the end of input created the output image"
|
||||
fi
|
||||
|
||||
# status=none prints nothing, also with the default --progress 1
|
||||
$VITASTOR_CLI dd iimg=testimg of=./testdata/testfile1 iodepth=4 status=none 2>./testdata/dd.err
|
||||
[[ ! -s ./testdata/dd.err ]] || format_error "dd status=none printed: $(cat ./testdata/dd.err)"
|
||||
diff ./testdata/testfile ./testdata/testfile1
|
||||
rm ./testdata/testfile1
|
||||
|
||||
# status=noxfer drops the final statistics line
|
||||
$VITASTOR_CLI --progress 0 dd iimg=testimg of=./testdata/testfile1 iodepth=4 status=noxfer 2>./testdata/dd.err
|
||||
[[ ! -s ./testdata/dd.err ]] || format_error "dd status=noxfer printed: $(cat ./testdata/dd.err)"
|
||||
rm ./testdata/testfile1
|
||||
# with --progress 1 the progress line, if it was shown at all, is cleared at the end
|
||||
clear_line=$'\r\033[K'
|
||||
for count in 128 4; do
|
||||
$VITASTOR_CLI dd iimg=testimg of=./testdata/testfile1 iodepth=4 bs=1M count=$count status=noxfer 2>./testdata/dd.err
|
||||
if [[ $(wc -l < ./testdata/dd.err) != 0 || ( -s ./testdata/dd.err && "$(cat ./testdata/dd.err)" != *copied*"$clear_line" ) ]]; then
|
||||
format_error "dd status=noxfer count=$count printed: $(cat -v ./testdata/dd.err)"
|
||||
fi
|
||||
head -c ${count}M ./testdata/testfile | cmp - ./testdata/testfile1
|
||||
rm ./testdata/testfile1
|
||||
done
|
||||
|
||||
# oflag=append appends to the output file
|
||||
cp ./testdata/over ./testdata/testfile1
|
||||
$VITASTOR_CLI dd iimg=testimg of=./testdata/testfile1 iodepth=4 bs=1M count=8 oflag=append
|
||||
(cat ./testdata/over; head -c 8M ./testdata/testfile) | cmp - ./testdata/testfile1 || format_error "dd oflag=append did not append to the output file"
|
||||
rm ./testdata/testfile1
|
||||
# conv=trunc contradicts oflag=append: fail without touching the output file
|
||||
cp ./testdata/over ./testdata/testfile1
|
||||
rc=0
|
||||
$VITASTOR_CLI dd iimg=testimg of=./testdata/testfile1 iodepth=4 bs=1M count=8 oflag=append conv=trunc || rc=$?
|
||||
[[ $rc = 22 ]] || format_error "dd conv=trunc oflag=append returned $rc instead of EINVAL"
|
||||
cmp ./testdata/over ./testdata/testfile1 || format_error "dd conv=trunc oflag=append changed the output file"
|
||||
rm ./testdata/testfile1
|
||||
|
||||
# pipe in without count - copy to the end of input
|
||||
$VITASTOR_CLI dd iimg=testimg iodepth=4 | $VITASTOR_CLI dd of=./testdata/testfile1 iodepth=4
|
||||
diff ./testdata/testfile ./testdata/testfile1
|
||||
rm ./testdata/testfile1
|
||||
$VITASTOR_CLI dd iimg=testimg iodepth=4 | $VITASTOR_CLI dd of=./testdata/testfile1 iodepth=4 bs=1M seek=4
|
||||
(head -c 4M /dev/zero; cat ./testdata/testfile) | cmp - ./testdata/testfile1 || format_error "dd from a pipe with seek= did not copy the whole input"
|
||||
rm ./testdata/testfile1
|
||||
|
||||
# unaligned image I/O fails with EINVAL before the output image is created or resized
|
||||
for args in "if=./testdata/testfile oimg=testimg6 seek=512B" "if=./testdata/testfile oimg=testimg2 seek=512B" "iimg=testimg oimg=testimg7 skip=512B"; do
|
||||
rc=0
|
||||
$VITASTOR_CLI dd iodepth=4 $args || rc=$?
|
||||
[[ $rc = 22 ]] || format_error "dd $args returned $rc instead of EINVAL"
|
||||
done
|
||||
if $VITASTOR_CLI ls --json | jq -e '.[] | select(.name == "testimg6" or .name == "testimg7")'; then
|
||||
format_error "dd with unaligned image I/O created the output image"
|
||||
fi
|
||||
size=$($VITASTOR_CLI ls --json | jq -r '.[] | select(.name == "testimg2") | .size')
|
||||
[[ "$size" = $((128*1024*1024)) ]] || format_error "dd with unaligned seek resized testimg2 to $size"
|
||||
|
||||
# a character device is read like a pipe, up to count= or size=
|
||||
$VITASTOR_CLI dd if=/dev/zero oimg=testimg8 size=32M
|
||||
size=$($VITASTOR_CLI ls --json | jq -r '.[] | select(.name == "testimg8") | .size')
|
||||
[[ "$size" = $((32*1024*1024)) ]] || format_error "dd if=/dev/zero size=32M created testimg8 with size $size"
|
||||
$VITASTOR_CLI dd if=/dev/zero of=./testdata/testfile1 bs=1M count=4 conv=nosparse
|
||||
head -c 4M /dev/zero | cmp - ./testdata/testfile1 || format_error "dd if=/dev/zero count=4 did not write 4 MiB of zeros"
|
||||
rm ./testdata/testfile1
|
||||
|
||||
format_green OK
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
# Test vitastor-cli describe and fix on degraded objects and with down OSDs
|
||||
|
||||
PG_COUNT=4
|
||||
PG_SIZE=3
|
||||
PG_MINSIZE=2
|
||||
# Keep degraded objects until the checks are done
|
||||
GLOBAL_CONFIG=',"no_recovery":true'
|
||||
. `dirname $0`/run_3osds.sh
|
||||
|
||||
$VITASTOR_CLI create -s 32M testimg
|
||||
|
||||
# Data written while OSD 3 is down is degraded when it comes back
|
||||
kill -9 $OSD3_PID
|
||||
$ETCDCTL del /vitastor/osd/state/3
|
||||
wait_condition 60 "$ETCDCTL get --prefix /vitastor/pg/state/ --print-value-only | jq -s -e '[ .[] | select(.state | index(\"degraded\")) ] | length == $PG_COUNT'" "PG peering without OSD 3"
|
||||
$VITASTOR_CLI dd if=/dev/urandom oimg=testimg bs=1M count=32
|
||||
start_osd 3
|
||||
wait_condition 60 "$ETCDCTL get --prefix /vitastor/pg/state/ --print-value-only | jq -s -e '[ .[] | select((.state | index(\"active\")) and (.state | index(\"has_degraded\"))) ] | length == $PG_COUNT'" "PG peering with OSD 3"
|
||||
|
||||
INODE_ID=$($VITASTOR_CLI ls --json | jq -r '.[] | select(.name == "testimg") | .inode_id')
|
||||
INODE=$(printf '0x%x' $INODE_ID)
|
||||
INODE_NUM=$((INODE_ID & 0xffffffffffff))
|
||||
|
||||
# --inode lists the whole inode, not only its first object
|
||||
all=$($VITASTOR_CLI describe --json | jq -c '[ .[] | select(.inode == "'$INODE'") | .stripe ] | unique')
|
||||
if [[ $(echo "$all" | jq length) -le 1 ]]; then
|
||||
format_error "describe should list more than one degraded object of testimg: $all"
|
||||
fi
|
||||
one=$($VITASTOR_CLI describe --pool 1 --inode $INODE_NUM --json | jq -c '[ .[].stripe ] | unique')
|
||||
if [[ "$one" != "$all" ]]; then
|
||||
format_error "describe --inode lists objects $one instead of $all"
|
||||
fi
|
||||
|
||||
# --min-offset and --max-offset narrow --inode, and --max-inode includes its whole inode
|
||||
second=$(echo "$all" | jq -r '.[1]')
|
||||
part=$($VITASTOR_CLI describe --pool 1 --inode $INODE_NUM --min-offset $second --max-offset $second --json | jq -c '[ .[].stripe ] | unique')
|
||||
if [[ "$part" != "[\"$second\"]" ]]; then
|
||||
format_error "describe --inode --min-offset $second --max-offset $second lists objects $part"
|
||||
fi
|
||||
range=$($VITASTOR_CLI describe --min-inode $INODE --max-inode $INODE --json | jq -c '[ .[].stripe ] | unique')
|
||||
if [[ "$range" != "$all" ]]; then
|
||||
format_error "describe --min-inode $INODE --max-inode $INODE lists objects $range instead of $all"
|
||||
fi
|
||||
|
||||
# describe and fix must not wait for an OSD which is down.
|
||||
# Stop the monitor so that PGs keep the down OSD as their primary,
|
||||
# and stop a primary OSD which has the latest data, so other PGs stay degraded.
|
||||
VICTIM=$($VITASTOR_CLI ls-pgs --json | jq -r '[ .[].cur_primary | select(. == 1 or . == 2) ] | first')
|
||||
VICTIM_PG=$($VITASTOR_CLI ls-pgs --json | jq -r '[ .[] | select(.cur_primary == '$VICTIM') | .pg_num ] | first')
|
||||
OTHER_PG=$($VITASTOR_CLI ls-pgs --json | jq -r '[ .[] | select(.cur_primary != '$VICTIM') | .pg_num ] | first')
|
||||
OTHER_PG_COUNT=$($VITASTOR_CLI ls-pgs --json | jq '[ .[] | select(.cur_primary != '$VICTIM') ] | length')
|
||||
if [[ "$VICTIM" = null || "$OTHER_PG_COUNT" = 0 ]]; then
|
||||
format_error "Expected PGs with primary OSD 1 or 2 and PGs with other primary OSDs"
|
||||
fi
|
||||
VICTIM_OBJ=$($VITASTOR_CLI describe --pool 1 --pg $VICTIM_PG --json | jq -r '.[0] | .inode+":"+.stripe')
|
||||
OTHER_OBJ=$($VITASTOR_CLI describe --pool 1 --pg $OTHER_PG --json | jq -r '.[0] | .inode+":"+.stripe')
|
||||
|
||||
kill -9 $MON_PID
|
||||
eval kill -9 \$OSD${VICTIM}_PID
|
||||
$ETCDCTL del /vitastor/osd/state/$VICTIM
|
||||
wait_condition 60 "$ETCDCTL get --prefix /vitastor/pg/state/ --print-value-only | jq -s -e '[ .[] | select(.primary != $VICTIM and (.state | index(\"degraded\"))) ] | length == $OTHER_PG_COUNT'" "PG peering without OSD $VICTIM"
|
||||
# Keep PG 1/$VICTIM_PG active with the down primary, as it is until the OSD's etcd lease expires
|
||||
$ETCDCTL put /vitastor/pg/state/1/$VICTIM_PG '{"peers":[1,2,3],"primary":'$VICTIM',"state":["active"]}'
|
||||
|
||||
# A listing with skipped OSDs or PGs is incomplete: EIO, but still valid JSON on stdout
|
||||
for args in "" "--osds 1,2,3" "--pool 1 --pg $VICTIM_PG"; do
|
||||
rc=0
|
||||
timeout 30 $VITASTOR_CLI describe $args --json > ./testdata/describe.json 2> ./testdata/describe.err || rc=$?
|
||||
[[ $rc == 5 ]] || format_error "describe $args exited with $rc instead of EIO with OSD $VICTIM down"
|
||||
grep -q "OSD $VICTIM is down, skipping \(it\|PG 1/$VICTIM_PG\)" ./testdata/describe.err || \
|
||||
format_error "describe $args should warn about OSD $VICTIM: $(cat ./testdata/describe.err)"
|
||||
grep -q "The listing is incomplete" ./testdata/describe.err || \
|
||||
format_error "describe $args should say that the listing is incomplete: $(cat ./testdata/describe.err)"
|
||||
count=$(jq length ./testdata/describe.json) || format_error "describe $args printed invalid JSON"
|
||||
if [[ "$args" == --pool* ]]; then
|
||||
[[ $count == 0 ]] || format_error "describe $args should list nothing"
|
||||
else
|
||||
[[ $count -gt 0 ]] || format_error "describe $args should still list objects from the other OSDs"
|
||||
fi
|
||||
done
|
||||
|
||||
if timeout 30 $VITASTOR_CLI fix --bad-osds $VICTIM --objects $OTHER_OBJ 2> ./testdata/fix.err; then
|
||||
format_error "fix should refuse to work with bad OSD $VICTIM down"
|
||||
fi
|
||||
grep -q "Bad OSD(s) $VICTIM are down" ./testdata/fix.err || format_error "fix printed: $(cat ./testdata/fix.err)"
|
||||
|
||||
# An object which is skipped is not fixed: EIO
|
||||
rc=0
|
||||
timeout 30 $VITASTOR_CLI fix --bad-osds 3 --objects $VICTIM_OBJ 2> ./testdata/fix.err || rc=$?
|
||||
[[ $rc == 5 ]] || format_error "fix exited with $rc instead of EIO on an object from PG 1/$VICTIM_PG with primary OSD $VICTIM down"
|
||||
grep -q "is from PG 1/$VICTIM_PG whose primary OSD $VICTIM is down" ./testdata/fix.err || \
|
||||
format_error "fix should skip the object from PG 1/$VICTIM_PG: $(cat ./testdata/fix.err)"
|
||||
grep -q "Some objects were not fixed" ./testdata/fix.err || \
|
||||
format_error "fix should say that it skipped objects: $(cat ./testdata/fix.err)"
|
||||
|
||||
format_green OK
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/bin/bash -ex
|
||||
# Test K/V listings with an end key: they must return exactly the keys of their range,
|
||||
# also when the range spans several blocks or ends in the middle of a block.
|
||||
|
||||
PG_COUNT=16
|
||||
. `dirname $0`/run_3osds.sh
|
||||
|
||||
$VITASTOR_CLI create -s 1G kvlist
|
||||
KV="build/src/kv/vitastor-kv --config_path $VITASTOR_CFG kvlist"
|
||||
|
||||
# 1000 keys with 100-byte values span several 4 KB blocks
|
||||
VALUE=$(printf 'v%.0s' $(seq 1 100))
|
||||
(
|
||||
for i in $(seq -f '%03g' 0 999); do
|
||||
echo "set t/a$i $VALUE"
|
||||
done
|
||||
echo "set t/b0 v"
|
||||
) | $KV > ./testdata/kv_list_set.log 2>&1
|
||||
if [ "$(grep -c '^OK' ./testdata/kv_list_set.log)" -ne 1001 ]; then
|
||||
format_error "Failed to set the keys"
|
||||
fi
|
||||
|
||||
list_keys()
|
||||
{
|
||||
$KV list "$@" | sed 's/ = .*//'
|
||||
}
|
||||
|
||||
if [ "$(list_keys t/a t/b)" != "$(seq -f 't/a%03g' 0 999)" ]; then
|
||||
format_error "list t/a t/b did not return exactly t/a000..t/a999"
|
||||
fi
|
||||
if [ "$(list_keys t/a5 t/a6)" != "$(seq -f 't/a%03g' 500 599)" ]; then
|
||||
format_error "list t/a5 t/a6 did not return exactly t/a500..t/a599"
|
||||
fi
|
||||
if [ -n "$(list_keys t/c t/d)" ]; then
|
||||
format_error "list t/c t/d returned keys"
|
||||
fi
|
||||
if [ "$($KV dump t/a5 t/a6 | grep -c '^set ')" -ne 100 ]; then
|
||||
format_error "dump t/a5 t/a6 did not return 100 keys"
|
||||
fi
|
||||
# Without an end, the listing goes on past the range
|
||||
if [ "$(list_keys t/a | head -n 1001)" != "$(seq -f 't/a%03g' 0 999; echo t/b0)" ]; then
|
||||
format_error "list t/a did not return t/a000..t/a999, then t/b0"
|
||||
fi
|
||||
|
||||
format_green OK
|
||||
@@ -36,23 +36,4 @@ $ETCDCTL get --prefix /vitastor/pg/config --print-value-only | \
|
||||
(. | map_values((. - 1) / 2 | floor) | unique | length) == 6 and
|
||||
(. | map_values((. - 1) / 4 | floor) | unique | length) == 3) ] | length) == 32'
|
||||
|
||||
# A malformed --level_placement is refused, an empty one removes the rules
|
||||
r=0
|
||||
$VITASTOR_CLI modify-pool testpool --level_placement rack || r=$?
|
||||
if [[ $r != 22 ]]; then
|
||||
format_error "modify-pool --level_placement rack should fail with EINVAL (22), but exited with $r"
|
||||
fi
|
||||
$ETCDCTL get --print-value-only /vitastor/config/pools | jq -e '.["1"].level_placement == {"rack":"112233"}' || \
|
||||
format_error "level_placement should stay rack=112233 after a refused change"
|
||||
$VITASTOR_CLI modify-pool testpool --level_placement ''
|
||||
$ETCDCTL get --print-value-only /vitastor/config/pools | jq -e '.["1"] | has("level_placement") | not' || \
|
||||
format_error "modify-pool --level_placement '' should remove level_placement"
|
||||
sleep 2
|
||||
if grep -q 'configuration is invalid' ./testdata/mon.log; then
|
||||
format_error "Monitor refused the pool configuration without level_placement"
|
||||
fi
|
||||
$ETCDCTL get --prefix /vitastor/pg/config --print-value-only | \
|
||||
jq -s -e '([ .[0].items["1"] | .[].osd_set | map_values(. | tonumber) | select(
|
||||
(. | map_values((. - 1) / 2 | floor) | unique | length) == 6) ] | length) == 32'
|
||||
|
||||
format_green OK
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
# Test vitastor-cli ls --tree with filters which leave out parent layers
|
||||
|
||||
. `dirname $0`/run_3osds.sh
|
||||
|
||||
$VITASTOR_CLI create -s 128M testimg
|
||||
$VITASTOR_CLI snap-create testimg@snap1
|
||||
$VITASTOR_CLI create --parent testimg@snap1 testclone
|
||||
|
||||
$VITASTOR_CLI create-pool testpool2 -s 3 -n 4 --failure_domain osd
|
||||
wait_pool_up 30 2 3 4
|
||||
$VITASTOR_CLI create --parent testimg@snap1 -p testpool2 testclone2
|
||||
|
||||
# First column of each row without the header
|
||||
check_tree()
|
||||
{
|
||||
local expected=$1
|
||||
shift
|
||||
local rows=$($VITASTOR_CLI ls --tree "$@" | tail -n +2 | sed 's/ .*//' | tr '\n' ';')
|
||||
if [[ "$rows" != "$expected" ]]; then
|
||||
format_error "ls --tree $* shows '$rows', expected '$expected'"
|
||||
fi
|
||||
}
|
||||
|
||||
$VITASTOR_CLI ls --tree
|
||||
|
||||
check_tree 'testimg@snap1;├─ testclone;├─ testclone2;└─ testimg;'
|
||||
check_tree 'testimg@snap1;└─ testimg;' 'testimg*'
|
||||
# Layers whose parent is filtered out are shown as roots
|
||||
check_tree 'testimg;' testimg
|
||||
check_tree 'testclone;testimg;' testclone testimg
|
||||
check_tree 'testclone;' -n 1
|
||||
check_tree 'testclone2;' -p testpool2
|
||||
|
||||
if ! $VITASTOR_CLI ls --tree testimg | grep -q ' testimg@snap1 *$'; then
|
||||
format_error "ls --tree testimg should show testimg@snap1 as the parent"
|
||||
fi
|
||||
|
||||
format_green OK
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
# Test that modify refuses an invalid --resize value instead of ignoring it
|
||||
|
||||
. `dirname $0`/common.sh
|
||||
|
||||
wait_etcd
|
||||
|
||||
$ETCDCTL put /vitastor/config/pools '{"1":{"name":"testpool","scheme":"replicated","pg_size":2,"pg_minsize":1,"pg_count":1,"failure_domain":"osd"}}'
|
||||
|
||||
$VITASTOR_CLI create -s 1G --pool testpool testimg
|
||||
|
||||
for size in 1.5G 10X abc ''; do
|
||||
r=0
|
||||
$VITASTOR_CLI modify testimg --resize "$size" || r=$?
|
||||
if [[ $r != 22 ]]; then
|
||||
format_error "modify --resize '$size' should fail with EINVAL (22), but exited with $r"
|
||||
fi
|
||||
done
|
||||
|
||||
$ETCDCTL get --print-value-only /vitastor/config/inode/1/1 | jq -e '.size == 1073741824' || \
|
||||
format_error "Image size should stay 1G after refused resizes"
|
||||
|
||||
$VITASTOR_CLI modify testimg --resize 2G
|
||||
$ETCDCTL get --print-value-only /vitastor/config/inode/1/1 | jq -e '.size == 2147483648' || \
|
||||
format_error "Image size should be 2G after --resize 2G"
|
||||
|
||||
format_green OK
|
||||
@@ -1,53 +0,0 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
# Test that modify-pool --name refuses to take the name of another pool
|
||||
|
||||
. `dirname $0`/common.sh
|
||||
|
||||
wait_etcd
|
||||
|
||||
POOL='"scheme":"replicated","pg_size":2,"pg_minsize":1,"pg_count":1,"failure_domain":"osd"'
|
||||
$ETCDCTL put /vitastor/config/pools '{"1":{"name":"pool1",'$POOL'},"2":{"name":"pool2",'$POOL'}}'
|
||||
|
||||
for p in pool2 2; do
|
||||
r=0
|
||||
$VITASTOR_CLI modify-pool $p --name pool1 || r=$?
|
||||
if [[ $r != 17 ]]; then
|
||||
format_error "modify-pool $p --name pool1 should fail with EEXIST (17), but exited with $r"
|
||||
fi
|
||||
done
|
||||
|
||||
names=$($ETCDCTL get --print-value-only /vitastor/config/pools | jq -c '[ .[].name ]')
|
||||
if [[ "$names" != '["pool1","pool2"]' ]]; then
|
||||
format_error "Pool names should stay pool1, pool2, but they are $names"
|
||||
fi
|
||||
|
||||
# A free name and the pool's own name are still accepted
|
||||
$VITASTOR_CLI modify-pool pool2 --name pool3
|
||||
$VITASTOR_CLI modify-pool pool3 --name pool3 --pg_count 2
|
||||
|
||||
cfg=$($ETCDCTL get --print-value-only /vitastor/config/pools | jq -c '[ .[] | [ .name, .pg_count ] ]')
|
||||
if [[ "$cfg" != '[["pool1",1],["pool3",2]]' ]]; then
|
||||
format_error "Pools should be pool1 (1 PG) and pool3 (2 PGs), but they are $cfg"
|
||||
fi
|
||||
|
||||
# Test that create-pool and modify-pool refuse pg_size > 64, which OSDs and clients don't support
|
||||
$ETCDCTL put /vitastor/config/pools '{"1":{"name":"testpool","scheme":"replicated","pg_size":2,"pg_minsize":1,"pg_count":1,"failure_domain":"osd"}}'
|
||||
|
||||
for args in "create-pool bigpool -s 65 -n 1 --force" "create-pool bigpool --ec 62+3 -n 1 --force" "modify-pool testpool -s 65"; do
|
||||
r=0
|
||||
$VITASTOR_CLI $args || r=$?
|
||||
if [[ $r != 22 ]]; then
|
||||
format_error "$args should fail with EINVAL (22), but exited with $r"
|
||||
fi
|
||||
done
|
||||
|
||||
$ETCDCTL get --print-value-only /vitastor/config/pools | jq -e 'keys == ["1"] and .["1"].pg_size == 2' || \
|
||||
format_error "Pool configuration should not change after refused commands"
|
||||
|
||||
# 64 is still accepted
|
||||
$VITASTOR_CLI modify-pool testpool -s 64
|
||||
$ETCDCTL get --print-value-only /vitastor/config/pools | jq -e '.["1"].pg_size == 64' || \
|
||||
format_error "modify-pool -s 64 should set pg_size to 64"
|
||||
|
||||
format_green OK
|
||||
@@ -1,103 +0,0 @@
|
||||
#!/bin/bash -ex
|
||||
# Test that RMDIR does not slow down after many K/V keys were created and deleted,
|
||||
# and that RENAME of a directory over an empty directory succeeds.
|
||||
#
|
||||
# K/V blocks emptied by deletes are never freed. The check that a directory is empty lists
|
||||
# its entries, and the listing used to go on past the end of the directory through all
|
||||
# empty blocks up to the next existing key. The keys below are created and deleted right
|
||||
# after the entries of the newest directories (dD... with 13 hex digits of inode number in
|
||||
# pool 1), so the emptied blocks are exactly where these listings ran.
|
||||
|
||||
PG_COUNT=16
|
||||
. `dirname $0`/run_3osds.sh
|
||||
|
||||
DIR_COUNT=50
|
||||
KEY_COUNT=10000
|
||||
|
||||
$VITASTOR_CLI create -s 10G fsmeta
|
||||
$VITASTOR_CLI modify-pool --used-for-app fs:fsmeta testpool
|
||||
|
||||
start_nfs()
|
||||
{
|
||||
build/src/nfs/vitastor-nfs --config_path $VITASTOR_CFG start --fs fsmeta \
|
||||
--portmap 0 --port 2050 --foreground 1 >>./testdata/nfs.log 2>&1 &
|
||||
NFS_PID=$!
|
||||
for i in $(seq 1 30); do
|
||||
sudo mount localhost:/ ./testdata/nfs -o port=2050,mountport=2050,nfsvers=3,soft,nolock,tcp && return 0
|
||||
sleep 1
|
||||
done
|
||||
format_error "Failed to mount vitastor-nfs"
|
||||
}
|
||||
|
||||
stop_nfs()
|
||||
{
|
||||
sudo umount ./testdata/nfs/
|
||||
kill $NFS_PID
|
||||
wait $NFS_PID || true
|
||||
}
|
||||
|
||||
# Time RMDIR of $DIR_COUNT empty directories, in milliseconds
|
||||
rmdir_ms()
|
||||
{
|
||||
local dir=$MNT/$1 start
|
||||
mkdir $dir
|
||||
for i in $(seq 1 $DIR_COUNT); do
|
||||
mkdir $dir/d$i
|
||||
done
|
||||
start=$(date +%s%N)
|
||||
for i in $(seq 1 $DIR_COUNT); do
|
||||
rmdir $dir/d$i
|
||||
done
|
||||
echo $(( ($(date +%s%N) - start) / 1000000 ))
|
||||
rmdir $dir
|
||||
}
|
||||
|
||||
mkdir -p testdata/nfs
|
||||
MNT=$(pwd)/testdata/nfs
|
||||
trap "sudo umount -f $MNT"' || true; kill -9 $(jobs -p)' EXIT
|
||||
|
||||
start_nfs
|
||||
BEFORE=$(rmdir_ms before)
|
||||
stop_nfs
|
||||
|
||||
# Create and delete keys with 400-byte values after the entries of all directories
|
||||
VALUE=$(printf 'x%.0s' $(seq 1 400))
|
||||
(
|
||||
for i in $(seq 1 $KEY_COUNT); do
|
||||
echo "set dF100000000000000/k$i $VALUE"
|
||||
done
|
||||
for i in $(seq 1 $KEY_COUNT); do
|
||||
echo "del dF100000000000000/k$i"
|
||||
done
|
||||
) | build/src/kv/vitastor-kv --config_path $VITASTOR_CFG fsmeta > ./testdata/kv_churn.log 2>&1
|
||||
if [ "$(grep -c '^OK' ./testdata/kv_churn.log)" -ne $((KEY_COUNT*2)) ]; then
|
||||
format_error "Failed to create and delete $KEY_COUNT keys"
|
||||
fi
|
||||
|
||||
start_nfs
|
||||
AFTER=$(rmdir_ms after)
|
||||
if [ $AFTER -gt $((BEFORE*2)) ]; then
|
||||
format_error "RMDIR of $DIR_COUNT directories took $AFTER ms after creating and deleting $KEY_COUNT keys, $BEFORE ms before"
|
||||
fi
|
||||
format_green "RMDIR of $DIR_COUNT directories: $BEFORE ms before, $AFTER ms after creating and deleting $KEY_COUNT keys"
|
||||
# READDIR still returns exactly the entries of a directory
|
||||
mkdir $MNT/list
|
||||
for i in $(seq 1 20); do
|
||||
mkdir $MNT/list/e$i
|
||||
done
|
||||
if [ "$(ls -f $MNT/list | grep -c '^e')" -ne 20 ]; then
|
||||
format_error "READDIR returned a wrong number of entries"
|
||||
fi
|
||||
# RENAME of a directory over an empty directory succeeds (the emptiness check used to take
|
||||
# the first key after the prefix, which belongs to another directory, for an entry)
|
||||
mkdir $MNT/list/e1/x
|
||||
if ! mv -T $MNT/list/e1 $MNT/list/e2; then
|
||||
format_error "RENAME of a directory over an empty directory failed"
|
||||
fi
|
||||
if [ ! -d $MNT/list/e2/x ]; then
|
||||
format_error "RENAME of a directory over an empty directory lost its contents"
|
||||
fi
|
||||
stop_nfs
|
||||
|
||||
|
||||
format_green OK
|
||||
128
tests/test_rm.sh
128
tests/test_rm.sh
@@ -24,4 +24,132 @@ sz=$(stat -c %s ./testdata/bin/testimg.bin)
|
||||
|
||||
[[ "$sz" = 67108864 ]] || format_error "File size is $sz bytes after resize to 64 M"
|
||||
|
||||
# test rm-data offset parsing and validation
|
||||
|
||||
$VITASTOR_CLI create -s 32M rmimg
|
||||
dd if=/dev/urandom of=./testdata/bin/rmimg.bin bs=1M count=32
|
||||
$VITASTOR_CLI dd iodepth=4 bs=1M if=./testdata/bin/rmimg.bin oimg=rmimg
|
||||
|
||||
check_rmimg()
|
||||
{
|
||||
rm -f ./testdata/bin/check.bin
|
||||
$VITASTOR_CLI dd iodepth=4 bs=1M conv=nosparse iimg=rmimg of=./testdata/bin/check.bin
|
||||
cmp ./testdata/bin/check.bin ./testdata/bin/rmimg.bin || format_error "$1"
|
||||
}
|
||||
|
||||
zero_rmimg()
|
||||
{
|
||||
dd if=/dev/zero of=./testdata/bin/rmimg.bin bs=1M seek=$1 count=$2 conv=notrunc
|
||||
}
|
||||
|
||||
check_rmimg "Image data differs after write"
|
||||
|
||||
RM_DATA="$VITASTOR_CLI rm-data --pool 1 --inode $($VITASTOR_CLI ls --json rmimg | jq -r '.[0].inode_num')"
|
||||
|
||||
# Bad offsets and empty ranges must fail without removing anything
|
||||
while IFS='|' read -r -u 3 args err; do
|
||||
if out=$($RM_DATA $args 2>&1); then
|
||||
format_error "rm-data $args should fail"
|
||||
fi
|
||||
[[ "$out" = "$err" ]] || format_error "rm-data $args failed with an unexpected error: $out"
|
||||
check_rmimg "rm-data $args removed data"
|
||||
done 3<<EOF
|
||||
--min-offset 8M --max-offset 10X|Invalid --max-offset value: 10X
|
||||
--min-offset 8M --max-offset 1.5G|Invalid --max-offset value: 1.5G
|
||||
--min-offset 8M --max-offset 0x|Invalid --max-offset value: 0x
|
||||
--min-offset 8X --max-offset 16M|Invalid --min-offset value: 8X
|
||||
--min-offset 1.5M|Invalid --min-offset value: 1.5M
|
||||
--min-offset 8M --max-offset -8M|Invalid --max-offset value: -8M
|
||||
--min-offset -1|Invalid --min-offset value: -1
|
||||
--min-offset 16777217T|Invalid --min-offset value: 16777217T
|
||||
--min-offset 8M --max-offset 16777217T|Invalid --max-offset value: 16777217T
|
||||
--min-offset 18446744073709551616|Invalid --min-offset value: 18446744073709551616
|
||||
--min-offset 8M --max-offset 0x10000000000000000|Invalid --max-offset value: 0x10000000000000000
|
||||
--min-offset 16M --max-offset 8M|--min-offset must be less than --max-offset
|
||||
--min-offset 8M --max-offset 8M|--min-offset must be less than --max-offset
|
||||
--min-offset 16M --max-offset 0|--max-offset 0 is invalid, omit it to remove data up to the end
|
||||
--max-offset 0M|--max-offset 0 is invalid, omit it to remove data up to the end
|
||||
EOF
|
||||
|
||||
$RM_DATA --min-offset 010M --max-offset 012M
|
||||
zero_rmimg 10 2
|
||||
check_rmimg "rm-data --min-offset 010M --max-offset 012M removed wrong data"
|
||||
|
||||
$RM_DATA --min-offset 8M --max-offset 10M
|
||||
zero_rmimg 8 2
|
||||
check_rmimg "rm-data --min-offset 8M --max-offset 10M removed wrong data"
|
||||
|
||||
$RM_DATA --min-offset $((20*1024*1024)) --max-offset $((24*1024*1024))
|
||||
zero_rmimg 20 4
|
||||
check_rmimg "rm-data with offsets in bytes removed wrong data"
|
||||
|
||||
$RM_DATA --min-offset 0x1a00000 --max-offset 0x1c00000
|
||||
zero_rmimg 26 2
|
||||
check_rmimg "rm-data with hex offsets removed wrong data"
|
||||
|
||||
$RM_DATA --min-offset 30M
|
||||
zero_rmimg 30 2
|
||||
check_rmimg "rm-data --min-offset 30M removed wrong data"
|
||||
|
||||
# test rm-data pool names and hex inode numbers
|
||||
|
||||
$VITASTOR_CLI create-pool testpool2 -s 2 -n 4 --failure_domain osd
|
||||
wait_pool_up 60 2 2 4
|
||||
|
||||
$VITASTOR_CLI create -s 32M -p testpool2 rmimg2
|
||||
dd if=/dev/urandom of=./testdata/bin/rmimg2.bin bs=1M count=32
|
||||
$VITASTOR_CLI dd iodepth=4 bs=1M if=./testdata/bin/rmimg2.bin oimg=rmimg2
|
||||
|
||||
zero_rmimg2()
|
||||
{
|
||||
dd if=/dev/zero of=./testdata/bin/rmimg2.bin bs=1M seek=$1 count=$2 conv=notrunc
|
||||
}
|
||||
|
||||
check_rmimg2()
|
||||
{
|
||||
check_rmimg "$1"
|
||||
rm -f ./testdata/bin/check.bin
|
||||
$VITASTOR_CLI dd iodepth=4 bs=1M conv=nosparse iimg=rmimg2 of=./testdata/bin/check.bin
|
||||
cmp ./testdata/bin/check.bin ./testdata/bin/rmimg2.bin || format_error "$1"
|
||||
}
|
||||
|
||||
check_rmimg2 "Image data differs after write"
|
||||
|
||||
INODE1=$($VITASTOR_CLI ls --json rmimg | jq -r '.[0].inode_num')
|
||||
INODE2=$($VITASTOR_CLI ls --json rmimg2 | jq -r '.[0].inode_num')
|
||||
[[ "$($VITASTOR_CLI ls --json rmimg2 | jq -r '.[0].pool_id')" = 2 ]] || format_error "rmimg2 is not in pool 2"
|
||||
|
||||
# Bad inode numbers and unknown pools must fail without removing anything
|
||||
while IFS='|' read -r -u 3 args err; do
|
||||
if out=$($VITASTOR_CLI rm-data $args 2>&1); then
|
||||
format_error "rm-data $args should fail"
|
||||
fi
|
||||
[[ "$out" = "$err" ]] || format_error "rm-data $args failed with an unexpected error: $out"
|
||||
check_rmimg2 "rm-data $args removed data"
|
||||
done 3<<EOF
|
||||
--pool 2 --inode ${INODE2}x|Invalid --inode value: ${INODE2}x
|
||||
--pool 2 --inode 0x|Invalid --inode value: 0x
|
||||
--pool 2 --inode 0x0x$INODE2|Invalid --inode value: 0x0x$INODE2
|
||||
--pool 2 --inode -1|Invalid --inode value: -1
|
||||
--pool 70000 --inode $INODE2|Invalid --pool value: 70000
|
||||
--pool nosuchpool --inode $(( (1<<48) | INODE1 ))|Pool nosuchpool does not exist
|
||||
EOF
|
||||
|
||||
$VITASTOR_CLI rm-data --pool testpool2 --inode $INODE2 --min-offset 8M --max-offset 12M
|
||||
zero_rmimg2 8 4
|
||||
check_rmimg2 "rm-data --pool testpool2 --inode $INODE2 removed wrong data"
|
||||
|
||||
$VITASTOR_CLI rm-data --pool 2 --inode $(printf 0x%x $INODE2) --min-offset 16M --max-offset 20M
|
||||
zero_rmimg2 16 4
|
||||
check_rmimg2 "rm-data --pool 2 --inode $(printf 0x%x $INODE2) removed wrong data"
|
||||
|
||||
$VITASTOR_CLI rm-data --inode $(printf 0x%x $(( (2<<48) | INODE2 ))) --min-offset 20M --max-offset 24M
|
||||
zero_rmimg2 20 4
|
||||
check_rmimg2 "rm-data --inode $(printf 0x%x $(( (2<<48) | INODE2 ))) removed wrong data"
|
||||
|
||||
# --pool overrides pool bits of --inode
|
||||
$VITASTOR_CLI rm-data --pool testpool --inode $(( (2<<48) | INODE1 )) --min-offset 0 --max-offset 4M
|
||||
zero_rmimg 0 4
|
||||
check_rmimg2 "rm-data --pool testpool --inode $(( (2<<48) | INODE1 )) removed wrong data"
|
||||
|
||||
format_green OK
|
||||
|
||||
Reference in New Issue
Block a user