Compare commits
37 Commits
v0.6.9
...
separate-d
Author | SHA1 | Date | |
---|---|---|---|
5fab6fc5ed | |||
ec2852c598 | |||
b9f5c2a823 | |||
e9d2f79aa7 | |||
0785bdf8b3 | |||
b57e44748b | |||
1bbe62f29c | |||
20a4406acc | |||
f93491bc6c | |||
999bed8514 | |||
3f33095fd7 | |||
dd74c5ce1b | |||
c6d104ecd6 | |||
e544aef7d0 | |||
616c18c786 | |||
fa687d3878 | |||
2c7556e536 | |||
2020608a39 | |||
139b98d80f | |||
f54ff6ad5d | |||
b376ef2ed9 | |||
5a234588b9 | |||
b82c30328f | |||
0ee5e0a7fe | |||
0a1640d169 | |||
3482bb0860 | |||
526995f486 | |||
073b505928 | |||
a8b21a22d0 | |||
0b1ffba62b | |||
8dfbd7943c | |||
39e7f98e54 | |||
3a83a32cb7 | |||
20d5ed799a | |||
b262938bca | |||
7e54242251 | |||
c3c2e68cc1 |
@@ -2,6 +2,6 @@ cmake_minimum_required(VERSION 2.8)
|
|||||||
|
|
||||||
project(vitastor)
|
project(vitastor)
|
||||||
|
|
||||||
set(VERSION "0.6.9")
|
set(VERSION "0.6.11")
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
72
README-ru.md
72
README-ru.md
@@ -51,13 +51,14 @@ Vitastor на данный момент находится в статусе п
|
|||||||
- Базовая поддержка OpenStack: драйвер Cinder, патчи для Nova и libvirt
|
- Базовая поддержка OpenStack: драйвер Cinder, патчи для Nova и libvirt
|
||||||
- Слияние снапшотов (vitastor-cli {snap-rm,flatten,merge})
|
- Слияние снапшотов (vitastor-cli {snap-rm,flatten,merge})
|
||||||
- Консольный интерфейс для управления образами (vitastor-cli {ls,create,modify})
|
- Консольный интерфейс для управления образами (vitastor-cli {ls,create,modify})
|
||||||
|
- Плагин для Proxmox
|
||||||
|
|
||||||
## Планы развития
|
## Планы развития
|
||||||
|
|
||||||
- Поддержка удаления снапшотов (слияния слоёв)
|
- Поддержка удаления снапшотов (слияния слоёв)
|
||||||
- Более корректные скрипты разметки дисков и автоматического запуска OSD
|
- Более корректные скрипты разметки дисков и автоматического запуска OSD
|
||||||
- Другие инструменты администрирования
|
- Другие инструменты администрирования
|
||||||
- Плагины для OpenNebula, Proxmox и других облачных систем
|
- Плагины для OpenNebula и других облачных систем
|
||||||
- iSCSI-прокси
|
- iSCSI-прокси
|
||||||
- Более быстрое переключение при отказах
|
- Более быстрое переключение при отказах
|
||||||
- Фоновая проверка целостности без контрольных сумм (сверка реплик)
|
- Фоновая проверка целостности без контрольных сумм (сверка реплик)
|
||||||
@@ -537,6 +538,75 @@ for i in ./???-*.yaml; do kubectl apply -f $i; done
|
|||||||
|
|
||||||
После этого вы сможете создавать PersistentVolume. Пример смотрите в файле [csi/deploy/example-pvc.yaml](csi/deploy/example-pvc.yaml).
|
После этого вы сможете создавать PersistentVolume. Пример смотрите в файле [csi/deploy/example-pvc.yaml](csi/deploy/example-pvc.yaml).
|
||||||
|
|
||||||
|
### OpenStack
|
||||||
|
|
||||||
|
Чтобы подключить Vitastor к OpenStack:
|
||||||
|
|
||||||
|
- Установите пакеты vitastor-client, libvirt и QEMU из DEB или RPM репозитория Vitastor
|
||||||
|
- Примените патч `patches/nova-21.diff` или `patches/nova-23.diff` к вашей инсталляции Nova.
|
||||||
|
nova-21.diff подходит для Nova 21-22, nova-23.diff подходит для Nova 23-24.
|
||||||
|
- Скопируйте `patches/cinder-vitastor.py` в инсталляцию Cinder как `cinder/volume/drivers/vitastor.py`
|
||||||
|
- Создайте тип томов в cinder.conf (см. ниже)
|
||||||
|
- Обязательно заблокируйте доступ от виртуальных машин к сети Vitastor (OSD и etcd), т.к. Vitastor (пока) не поддерживает аутентификацию
|
||||||
|
- Перезапустите Cinder и Nova
|
||||||
|
|
||||||
|
Пример конфигурации Cinder:
|
||||||
|
|
||||||
|
```
|
||||||
|
[DEFAULT]
|
||||||
|
enabled_backends = lvmdriver-1, vitastor-testcluster
|
||||||
|
# ...
|
||||||
|
|
||||||
|
[vitastor-testcluster]
|
||||||
|
volume_driver = cinder.volume.drivers.vitastor.VitastorDriver
|
||||||
|
volume_backend_name = vitastor-testcluster
|
||||||
|
image_volume_cache_enabled = True
|
||||||
|
volume_clear = none
|
||||||
|
vitastor_etcd_address = 192.168.7.2:2379
|
||||||
|
vitastor_etcd_prefix =
|
||||||
|
vitastor_config_path = /etc/vitastor/vitastor.conf
|
||||||
|
vitastor_pool_id = 1
|
||||||
|
image_upload_use_cinder_backend = True
|
||||||
|
```
|
||||||
|
|
||||||
|
Чтобы помещать в Vitastor Glance-образы, нужно использовать
|
||||||
|
[https://docs.openstack.org/cinder/pike/admin/blockstorage-volume-backed-image.html](образы на основе томов Cinder),
|
||||||
|
однако, поддержка этой функции ещё не проверялась.
|
||||||
|
|
||||||
|
### Proxmox
|
||||||
|
|
||||||
|
Чтобы подключить Vitastor к Proxmox Virtual Environment (поддерживаются версии 6.4 и 7.1):
|
||||||
|
|
||||||
|
- Добавьте соответствующий Debian-репозиторий Vitastor в sources.list на хостах Proxmox
|
||||||
|
(buster для 6.4, bullseye для 7.1)
|
||||||
|
- Установите пакеты vitastor-client, pve-qemu-kvm, pve-storage-vitastor (* или см. сноску) из репозитория Vitastor
|
||||||
|
- Определите тип хранилища в `/etc/pve/storage.cfg` (см. ниже)
|
||||||
|
- Обязательно заблокируйте доступ от виртуальных машин к сети Vitastor (OSD и etcd), т.к. Vitastor (пока) не поддерживает аутентификацию
|
||||||
|
- Перезапустите демон Proxmox: `systemctl restart pvedaemon`
|
||||||
|
|
||||||
|
Пример `/etc/pve/storage.cfg` (единственная обязательная опция - vitastor_pool, все остальные
|
||||||
|
перечислены внизу для понимания значений по умолчанию):
|
||||||
|
|
||||||
|
```
|
||||||
|
vitastor: vitastor
|
||||||
|
# Пул, в который будут помещаться образы дисков
|
||||||
|
vitastor_pool testpool
|
||||||
|
# Путь к файлу конфигурации
|
||||||
|
vitastor_config_path /etc/vitastor/vitastor.conf
|
||||||
|
# Адрес(а) etcd, нужны, только если не указаны в vitastor.conf
|
||||||
|
vitastor_etcd_address 192.168.7.2:2379/v3
|
||||||
|
# Префикс ключей метаданных в etcd
|
||||||
|
vitastor_etcd_prefix /vitastor
|
||||||
|
# Префикс имён образов
|
||||||
|
vitastor_prefix pve/
|
||||||
|
# Монтировать образы через NBD прокси, через ядро (нужно только для контейнеров)
|
||||||
|
vitastor_nbd 0
|
||||||
|
```
|
||||||
|
|
||||||
|
\* Примечание: вместо установки пакета pve-storage-vitastor вы можете вручную скопировать файл
|
||||||
|
[patches/PVE_VitastorPlugin.pm](patches/PVE_VitastorPlugin.pm) на хосты Proxmox как
|
||||||
|
`/usr/share/perl5/PVE/Storage/Custom/VitastorPlugin.pm`.
|
||||||
|
|
||||||
## Известные проблемы
|
## Известные проблемы
|
||||||
|
|
||||||
- Запросы удаления объектов могут в данный момент приводить к "неполным" объектам в EC-пулах,
|
- Запросы удаления объектов могут в данный момент приводить к "неполным" объектам в EC-пулах,
|
||||||
|
68
README.md
68
README.md
@@ -45,6 +45,7 @@ breaking changes in the future. However, the following is implemented:
|
|||||||
- Basic OpenStack support: Cinder driver, Nova and libvirt patches
|
- Basic OpenStack support: Cinder driver, Nova and libvirt patches
|
||||||
- Snapshot merge tool (vitastor-cli {snap-rm,flatten,merge})
|
- Snapshot merge tool (vitastor-cli {snap-rm,flatten,merge})
|
||||||
- Image management CLI (vitastor-cli {ls,create,modify})
|
- Image management CLI (vitastor-cli {ls,create,modify})
|
||||||
|
- Proxmox storage plugin
|
||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
@@ -486,6 +487,73 @@ for i in ./???-*.yaml; do kubectl apply -f $i; done
|
|||||||
|
|
||||||
After that you'll be able to create PersistentVolumes. See example in [csi/deploy/example-pvc.yaml](csi/deploy/example-pvc.yaml).
|
After that you'll be able to create PersistentVolumes. See example in [csi/deploy/example-pvc.yaml](csi/deploy/example-pvc.yaml).
|
||||||
|
|
||||||
|
### OpenStack
|
||||||
|
|
||||||
|
To enable Vitastor support in an OpenStack installation:
|
||||||
|
|
||||||
|
- Install vitastor-client, patched QEMU and libvirt packages from Vitastor DEB or RPM repository
|
||||||
|
- Use `patches/nova-21.diff` or `patches/nova-23.diff` to patch your Nova installation.
|
||||||
|
Patch 21 fits Nova 21-22, patch 23 fits Nova 23-24.
|
||||||
|
- Install `patches/cinder-vitastor.py` as `..../cinder/volume/drivers/vitastor.py`
|
||||||
|
- Define a volume type in cinder.conf (see below)
|
||||||
|
- Block network access from VMs to Vitastor network (to OSDs and etcd), because Vitastor doesn't support authentication (yet)
|
||||||
|
- Restart Cinder and Nova
|
||||||
|
|
||||||
|
Cinder volume type configuration example:
|
||||||
|
|
||||||
|
```
|
||||||
|
[DEFAULT]
|
||||||
|
enabled_backends = lvmdriver-1, vitastor-testcluster
|
||||||
|
# ...
|
||||||
|
|
||||||
|
[vitastor-testcluster]
|
||||||
|
volume_driver = cinder.volume.drivers.vitastor.VitastorDriver
|
||||||
|
volume_backend_name = vitastor-testcluster
|
||||||
|
image_volume_cache_enabled = True
|
||||||
|
volume_clear = none
|
||||||
|
vitastor_etcd_address = 192.168.7.2:2379
|
||||||
|
vitastor_etcd_prefix =
|
||||||
|
vitastor_config_path = /etc/vitastor/vitastor.conf
|
||||||
|
vitastor_pool_id = 1
|
||||||
|
image_upload_use_cinder_backend = True
|
||||||
|
```
|
||||||
|
|
||||||
|
To put Glance images in Vitastor, use [https://docs.openstack.org/cinder/pike/admin/blockstorage-volume-backed-image.html](volume-backed images),
|
||||||
|
although the support has not been verified yet.
|
||||||
|
|
||||||
|
### Proxmox
|
||||||
|
|
||||||
|
To enable Vitastor support in Proxmox Virtual Environment (6.4 and 7.1 are supported):
|
||||||
|
|
||||||
|
- Add the corresponding Vitastor Debian repository into sources.list on Proxmox hosts
|
||||||
|
(buster for 6.4, bullseye for 7.1)
|
||||||
|
- Install vitastor-client, pve-qemu-kvm, pve-storage-vitastor (* or see note) packages from Vitastor repository
|
||||||
|
- Define storage in `/etc/pve/storage.cfg` (see below)
|
||||||
|
- Block network access from VMs to Vitastor network (to OSDs and etcd), because Vitastor doesn't support authentication (yet)
|
||||||
|
- Restart pvedaemon: `systemctl restart pvedaemon`
|
||||||
|
|
||||||
|
`/etc/pve/storage.cfg` example (the only required option is vitastor_pool, all others
|
||||||
|
are listed below with their default values):
|
||||||
|
|
||||||
|
```
|
||||||
|
vitastor: vitastor
|
||||||
|
# pool to put new images into
|
||||||
|
vitastor_pool testpool
|
||||||
|
# path to the configuration file
|
||||||
|
vitastor_config_path /etc/vitastor/vitastor.conf
|
||||||
|
# etcd address(es), required only if missing in the configuration file
|
||||||
|
vitastor_etcd_address 192.168.7.2:2379/v3
|
||||||
|
# prefix for keys in etcd
|
||||||
|
vitastor_etcd_prefix /vitastor
|
||||||
|
# prefix for images
|
||||||
|
vitastor_prefix pve/
|
||||||
|
# use NBD mounter (only required for containers)
|
||||||
|
vitastor_nbd 0
|
||||||
|
```
|
||||||
|
|
||||||
|
\* Note: you can also manually copy [patches/PVE_VitastorPlugin.pm](patches/PVE_VitastorPlugin.pm) to Proxmox hosts
|
||||||
|
as `/usr/share/perl5/PVE/Storage/Custom/VitastorPlugin.pm` instead of installing pve-storage-vitastor.
|
||||||
|
|
||||||
## Known Problems
|
## Known Problems
|
||||||
|
|
||||||
- Object deletion requests may currently lead to 'incomplete' objects in EC pools
|
- Object deletion requests may currently lead to 'incomplete' objects in EC pools
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
VERSION ?= v0.6.9
|
VERSION ?= v0.6.11
|
||||||
|
|
||||||
all: build push
|
all: build push
|
||||||
|
|
||||||
|
@@ -49,7 +49,7 @@ spec:
|
|||||||
capabilities:
|
capabilities:
|
||||||
add: ["SYS_ADMIN"]
|
add: ["SYS_ADMIN"]
|
||||||
allowPrivilegeEscalation: true
|
allowPrivilegeEscalation: true
|
||||||
image: vitalif/vitastor-csi:v0.6.9
|
image: vitalif/vitastor-csi:v0.6.11
|
||||||
args:
|
args:
|
||||||
- "--node=$(NODE_ID)"
|
- "--node=$(NODE_ID)"
|
||||||
- "--endpoint=$(CSI_ENDPOINT)"
|
- "--endpoint=$(CSI_ENDPOINT)"
|
||||||
|
@@ -116,7 +116,7 @@ spec:
|
|||||||
privileged: true
|
privileged: true
|
||||||
capabilities:
|
capabilities:
|
||||||
add: ["SYS_ADMIN"]
|
add: ["SYS_ADMIN"]
|
||||||
image: vitalif/vitastor-csi:v0.6.9
|
image: vitalif/vitastor-csi:v0.6.11
|
||||||
args:
|
args:
|
||||||
- "--node=$(NODE_ID)"
|
- "--node=$(NODE_ID)"
|
||||||
- "--endpoint=$(CSI_ENDPOINT)"
|
- "--endpoint=$(CSI_ENDPOINT)"
|
||||||
|
@@ -5,7 +5,7 @@ package vitastor
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
vitastorCSIDriverName = "csi.vitastor.io"
|
vitastorCSIDriverName = "csi.vitastor.io"
|
||||||
vitastorCSIDriverVersion = "0.6.9"
|
vitastorCSIDriverVersion = "0.6.11"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config struct fills the parameters of request or user input
|
// Config struct fills the parameters of request or user input
|
||||||
|
2
debian/changelog
vendored
2
debian/changelog
vendored
@@ -1,4 +1,4 @@
|
|||||||
vitastor (0.6.9-1) unstable; urgency=medium
|
vitastor (0.6.11-1) unstable; urgency=medium
|
||||||
|
|
||||||
* RDMA support
|
* RDMA support
|
||||||
* Bugfixes
|
* Bugfixes
|
||||||
|
6
debian/control
vendored
6
debian/control
vendored
@@ -47,3 +47,9 @@ Architecture: amd64
|
|||||||
Depends: ${shlibs:Depends}, ${misc:Depends}, vitastor-client (= ${binary:Version}), fio (= ${dep:fio})
|
Depends: ${shlibs:Depends}, ${misc:Depends}, vitastor-client (= ${binary:Version}), fio (= ${dep:fio})
|
||||||
Description: Vitastor, a fast software-defined clustered block storage - fio drivers
|
Description: Vitastor, a fast software-defined clustered block storage - fio drivers
|
||||||
Vitastor fio drivers for benchmarking.
|
Vitastor fio drivers for benchmarking.
|
||||||
|
|
||||||
|
Package: pve-storage-vitastor
|
||||||
|
Architecture: amd64
|
||||||
|
Depends: ${shlibs:Depends}, ${misc:Depends}, vitastor-client (= ${binary:Version})
|
||||||
|
Description: Vitastor Proxmox Virtual Environment storage plugin
|
||||||
|
Vitastor storage plugin for Proxmox Virtual Environment.
|
||||||
|
1
debian/pve-storage-vitastor.install
vendored
Normal file
1
debian/pve-storage-vitastor.install
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
patches/PVE_VitastorPlugin.pm usr/share/perl5/PVE/Storage/Custom/VitastorPlugin.pm
|
8
debian/vitastor.Dockerfile
vendored
8
debian/vitastor.Dockerfile
vendored
@@ -33,8 +33,8 @@ RUN set -e -x; \
|
|||||||
mkdir -p /root/packages/vitastor-$REL; \
|
mkdir -p /root/packages/vitastor-$REL; \
|
||||||
rm -rf /root/packages/vitastor-$REL/*; \
|
rm -rf /root/packages/vitastor-$REL/*; \
|
||||||
cd /root/packages/vitastor-$REL; \
|
cd /root/packages/vitastor-$REL; \
|
||||||
cp -r /root/vitastor vitastor-0.6.9; \
|
cp -r /root/vitastor vitastor-0.6.11; \
|
||||||
cd vitastor-0.6.9; \
|
cd vitastor-0.6.11; \
|
||||||
ln -s /root/fio-build/fio-*/ ./fio; \
|
ln -s /root/fio-build/fio-*/ ./fio; \
|
||||||
FIO=$(head -n1 fio/debian/changelog | perl -pe 's/^.*\((.*?)\).*$/$1/'); \
|
FIO=$(head -n1 fio/debian/changelog | perl -pe 's/^.*\((.*?)\).*$/$1/'); \
|
||||||
ls /usr/include/linux/raw.h || cp ./debian/raw.h /usr/include/linux/raw.h; \
|
ls /usr/include/linux/raw.h || cp ./debian/raw.h /usr/include/linux/raw.h; \
|
||||||
@@ -47,8 +47,8 @@ RUN set -e -x; \
|
|||||||
rm -rf a b; \
|
rm -rf a b; \
|
||||||
echo "dep:fio=$FIO" > debian/fio_version; \
|
echo "dep:fio=$FIO" > debian/fio_version; \
|
||||||
cd /root/packages/vitastor-$REL; \
|
cd /root/packages/vitastor-$REL; \
|
||||||
tar --sort=name --mtime='2020-01-01' --owner=0 --group=0 --exclude=debian -cJf vitastor_0.6.9.orig.tar.xz vitastor-0.6.9; \
|
tar --sort=name --mtime='2020-01-01' --owner=0 --group=0 --exclude=debian -cJf vitastor_0.6.11.orig.tar.xz vitastor-0.6.11; \
|
||||||
cd vitastor-0.6.9; \
|
cd vitastor-0.6.11; \
|
||||||
V=$(head -n1 debian/changelog | perl -pe 's/^.*\((.*?)\).*$/$1/'); \
|
V=$(head -n1 debian/changelog | perl -pe 's/^.*\((.*?)\).*$/$1/'); \
|
||||||
DEBFULLNAME="Vitaliy Filippov <vitalif@yourcmc.ru>" dch -D $REL -v "$V""$REL" "Rebuild for $REL"; \
|
DEBFULLNAME="Vitaliy Filippov <vitalif@yourcmc.ru>" dch -D $REL -v "$V""$REL" "Rebuild for $REL"; \
|
||||||
DEB_BUILD_OPTIONS=nocheck dpkg-buildpackage --jobs=auto -sa; \
|
DEB_BUILD_OPTIONS=nocheck dpkg-buildpackage --jobs=auto -sa; \
|
||||||
|
15
mon/mon.js
15
mon/mon.js
@@ -709,10 +709,13 @@ class Mon
|
|||||||
for (const node_id in this.state.config.node_placement||{})
|
for (const node_id in this.state.config.node_placement||{})
|
||||||
{
|
{
|
||||||
const node_cfg = this.state.config.node_placement[node_id];
|
const node_cfg = this.state.config.node_placement[node_id];
|
||||||
if (!node_id || /^\d/.exec(node_id) ||
|
if (/^\d+$/.exec(node_id))
|
||||||
!node_cfg.level || !levels[node_cfg.level])
|
|
||||||
{
|
{
|
||||||
// All nodes must have non-empty non-numeric IDs and valid levels
|
node_cfg.level = 'osd';
|
||||||
|
}
|
||||||
|
if (!node_id || !node_cfg.level || !levels[node_cfg.level])
|
||||||
|
{
|
||||||
|
// All nodes must have non-empty IDs and valid levels
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
tree[node_id] = { id: node_id, level: node_cfg.level, parent: node_cfg.parent, children: [] };
|
tree[node_id] = { id: node_id, level: node_cfg.level, parent: node_cfg.parent, children: [] };
|
||||||
@@ -745,10 +748,10 @@ class Mon
|
|||||||
.reduce((a, c) => { a[c] = true; return a; }, {});
|
.reduce((a, c) => { a[c] = true; return a; }, {});
|
||||||
}
|
}
|
||||||
delete tree[osd_num].children;
|
delete tree[osd_num].children;
|
||||||
if (!tree[tree[osd_num].parent])
|
if (!tree[stat.host])
|
||||||
{
|
{
|
||||||
tree[tree[osd_num].parent] = {
|
tree[stat.host] = {
|
||||||
id: tree[osd_num].parent,
|
id: stat.host,
|
||||||
level: 'host',
|
level: 'host',
|
||||||
parent: null,
|
parent: null,
|
||||||
children: [],
|
children: [],
|
||||||
|
503
patches/PVE_VitastorPlugin.pm
Normal file
503
patches/PVE_VitastorPlugin.pm
Normal file
@@ -0,0 +1,503 @@
|
|||||||
|
# Install as /usr/share/perl5/PVE/Storage/Custom/VitastorPlugin.pm
|
||||||
|
|
||||||
|
# Proxmox Vitastor Driver
|
||||||
|
# Copyright (c) Vitaliy Filippov, 2021+
|
||||||
|
# License: VNPL-1.1 or GNU AGPLv3.0
|
||||||
|
|
||||||
|
package PVE::Storage::Custom::VitastorPlugin;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use JSON;
|
||||||
|
|
||||||
|
use PVE::Storage::Plugin;
|
||||||
|
use PVE::Tools qw(run_command);
|
||||||
|
|
||||||
|
use base qw(PVE::Storage::Plugin);
|
||||||
|
|
||||||
|
sub api
|
||||||
|
{
|
||||||
|
# Trick it :)
|
||||||
|
return PVE::Storage->APIVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub run_cli
|
||||||
|
{
|
||||||
|
my ($scfg, $cmd, %args) = @_;
|
||||||
|
my $retval;
|
||||||
|
my $stderr = '';
|
||||||
|
my $errmsg = $args{errmsg} ? $args{errmsg}.": " : "vitastor-cli error: ";
|
||||||
|
my $json = delete $args{json};
|
||||||
|
$json = 1 if !defined $json;
|
||||||
|
my $binary = delete $args{binary};
|
||||||
|
$binary = '/usr/bin/vitastor-cli' if !defined $binary;
|
||||||
|
if (!exists($args{errfunc}))
|
||||||
|
{
|
||||||
|
$args{errfunc} = sub
|
||||||
|
{
|
||||||
|
my $line = shift;
|
||||||
|
print STDERR $line;
|
||||||
|
*STDERR->flush();
|
||||||
|
$stderr .= $line;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!exists($args{outfunc}))
|
||||||
|
{
|
||||||
|
$retval = '';
|
||||||
|
$args{outfunc} = sub { $retval .= shift };
|
||||||
|
if ($json)
|
||||||
|
{
|
||||||
|
unshift @$cmd, '--json';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($scfg->{vitastor_etcd_address})
|
||||||
|
{
|
||||||
|
unshift @$cmd, '--etcd_address', $scfg->{vitastor_etcd_address};
|
||||||
|
}
|
||||||
|
if ($scfg->{vitastor_config_path})
|
||||||
|
{
|
||||||
|
unshift @$cmd, '--config_path', $scfg->{vitastor_config_path};
|
||||||
|
}
|
||||||
|
unshift @$cmd, $binary;
|
||||||
|
eval { run_command($cmd, %args); };
|
||||||
|
if (my $err = $@)
|
||||||
|
{
|
||||||
|
die "Error invoking vitastor-cli: $err";
|
||||||
|
}
|
||||||
|
if (defined $retval)
|
||||||
|
{
|
||||||
|
# untaint
|
||||||
|
$retval =~ /^(.*)$/s;
|
||||||
|
if ($json)
|
||||||
|
{
|
||||||
|
eval { $retval = JSON::decode_json($1); };
|
||||||
|
if ($@)
|
||||||
|
{
|
||||||
|
die "vitastor-cli returned bad JSON: $@";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$retval = $1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
sub type
|
||||||
|
{
|
||||||
|
return 'vitastor';
|
||||||
|
}
|
||||||
|
|
||||||
|
sub plugindata
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
content => [ { images => 1, rootdir => 1 }, { images => 1 } ],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub properties
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
vitastor_etcd_address => {
|
||||||
|
description => 'IP address(es) of etcd.',
|
||||||
|
type => 'string',
|
||||||
|
format => 'pve-storage-portal-dns-list',
|
||||||
|
},
|
||||||
|
vitastor_etcd_prefix => {
|
||||||
|
description => 'Prefix for Vitastor etcd metadata',
|
||||||
|
type => 'string',
|
||||||
|
},
|
||||||
|
vitastor_config_path => {
|
||||||
|
description => 'Path to Vitastor configuration file',
|
||||||
|
type => 'string',
|
||||||
|
},
|
||||||
|
vitastor_prefix => {
|
||||||
|
description => 'Image name prefix',
|
||||||
|
type => 'string',
|
||||||
|
},
|
||||||
|
vitastor_pool => {
|
||||||
|
description => 'Default pool to use for images',
|
||||||
|
type => 'string',
|
||||||
|
},
|
||||||
|
vitastor_nbd => {
|
||||||
|
description => 'Use kernel NBD devices (slower)',
|
||||||
|
type => 'boolean',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub options
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
nodes => { optional => 1 },
|
||||||
|
disable => { optional => 1 },
|
||||||
|
vitastor_etcd_address => { optional => 1},
|
||||||
|
vitastor_etcd_prefix => { optional => 1 },
|
||||||
|
vitastor_config_path => { optional => 1 },
|
||||||
|
vitastor_prefix => { optional => 1 },
|
||||||
|
vitastor_pool => {},
|
||||||
|
vitastor_nbd => { optional => 1 },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
# Storage implementation
|
||||||
|
|
||||||
|
sub parse_volname
|
||||||
|
{
|
||||||
|
my ($class, $volname) = @_;
|
||||||
|
if ($volname =~ m/^((base-(\d+)-\S+)\/)?((?:(base)|(vm))-(\d+)-\S+)$/)
|
||||||
|
{
|
||||||
|
# ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $format)
|
||||||
|
return ('images', $4, $7, $2, $3, $5, 'raw');
|
||||||
|
}
|
||||||
|
die "unable to parse vitastor volume name '$volname'\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _qemu_option
|
||||||
|
{
|
||||||
|
my ($k, $v) = @_;
|
||||||
|
if (defined $v && $v ne "")
|
||||||
|
{
|
||||||
|
$v =~ s/:/\\:/gso;
|
||||||
|
return ":$k=$v";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub path
|
||||||
|
{
|
||||||
|
my ($class, $scfg, $volname, $storeid, $snapname) = @_;
|
||||||
|
my $prefix = defined $scfg->{vitastor_prefix} ? $scfg->{vitastor_prefix} : 'pve/';
|
||||||
|
my ($vtype, $name, $vmid) = $class->parse_volname($volname);
|
||||||
|
$name .= '@'.$snapname if $snapname;
|
||||||
|
if ($scfg->{vitastor_nbd})
|
||||||
|
{
|
||||||
|
my $mapped = run_cli($scfg, [ 'ls' ], binary => '/usr/bin/vitastor-nbd');
|
||||||
|
my ($kerneldev) = grep { $mapped->{$_}->{image} eq $prefix.$name } keys %$mapped;
|
||||||
|
die "Image not mapped via NBD" if !$kerneldev;
|
||||||
|
return ($kerneldev, $vmid, $vtype);
|
||||||
|
}
|
||||||
|
my $path = "vitastor";
|
||||||
|
$path .= _qemu_option('config_path', $scfg->{vitastor_config_path});
|
||||||
|
# FIXME This is the only exception: etcd_address -> etcd_host for qemu
|
||||||
|
$path .= _qemu_option('etcd_host', $scfg->{vitastor_etcd_address});
|
||||||
|
$path .= _qemu_option('etcd_prefix', $scfg->{vitastor_etcd_prefix});
|
||||||
|
$path .= _qemu_option('image', $prefix.$name);
|
||||||
|
return ($path, $vmid, $vtype);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _find_free_diskname
|
||||||
|
{
|
||||||
|
my ($class, $storeid, $scfg, $vmid, $fmt, $add_fmt_suffix) = @_;
|
||||||
|
my $list = _process_list($scfg, $storeid, run_cli($scfg, [ 'ls' ]));
|
||||||
|
$list = [ map { $_->{name} } @$list ];
|
||||||
|
return PVE::Storage::Plugin::get_next_vm_diskname($list, $storeid, $vmid, undef, $scfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Used only in "Create Template" and, in fact, converts a VM into a template
|
||||||
|
# As a consequence, this is always invoked with the VM powered off
|
||||||
|
# So we just rename vm-xxx to base-xxx and make it a readonly base layer
|
||||||
|
sub create_base
|
||||||
|
{
|
||||||
|
my ($class, $storeid, $scfg, $volname) = @_;
|
||||||
|
my $prefix = defined $scfg->{vitastor_prefix} ? $scfg->{vitastor_prefix} : 'pve/';
|
||||||
|
|
||||||
|
my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) = $class->parse_volname($volname);
|
||||||
|
die "create_base not possible with base image\n" if $isBase;
|
||||||
|
|
||||||
|
my $info = _process_list($scfg, $storeid, run_cli($scfg, [ 'ls', $prefix.$name ]))->[0];
|
||||||
|
die "image $name does not exist\n" if !$info;
|
||||||
|
|
||||||
|
die "volname '$volname' contains wrong information about parent {$info->{parent}} $basename\n"
|
||||||
|
if $basename && (!$info->{parent} || $info->{parent} ne $basename);
|
||||||
|
|
||||||
|
my $newname = $name;
|
||||||
|
$newname =~ s/^vm-/base-/;
|
||||||
|
|
||||||
|
my $newvolname = $basename ? "$basename/$newname" : "$newname";
|
||||||
|
run_cli($scfg, [ 'modify', '--rename', $prefix.$newname, '--readonly', $prefix.$name ], json => 0);
|
||||||
|
|
||||||
|
return $newvolname;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub clone_image
|
||||||
|
{
|
||||||
|
my ($class, $scfg, $storeid, $volname, $vmid, $snapname) = @_;
|
||||||
|
my $prefix = defined $scfg->{vitastor_prefix} ? $scfg->{vitastor_prefix} : 'pve/';
|
||||||
|
|
||||||
|
my $snap = '';
|
||||||
|
$snap = '@'.$snapname if length $snapname;
|
||||||
|
|
||||||
|
my ($vtype, $basename, $basevmid, undef, undef, $isBase) = $class->parse_volname($volname);
|
||||||
|
die "$volname is not a base image and snapname is not provided\n" if !$isBase && !length($snapname);
|
||||||
|
|
||||||
|
my $name = $class->find_free_diskname($storeid, $scfg, $vmid);
|
||||||
|
|
||||||
|
warn "clone $volname: $basename snapname $snap to $name\n";
|
||||||
|
|
||||||
|
my $newvol = "$basename/$name";
|
||||||
|
$newvol = $name if length($snapname);
|
||||||
|
|
||||||
|
run_cli($scfg, [ 'create', '--parent', $prefix.$basename.$snap, $prefix.$name ], json => 0);
|
||||||
|
|
||||||
|
return $newvol;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub alloc_image
|
||||||
|
{
|
||||||
|
# $size is in kb in this method
|
||||||
|
my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_;
|
||||||
|
my $prefix = defined $scfg->{vitastor_prefix} ? $scfg->{vitastor_prefix} : 'pve/';
|
||||||
|
die "illegal name '$name' - should be 'vm-$vmid-*'\n" if $name && $name !~ m/^vm-$vmid-/;
|
||||||
|
$name = $class->find_free_diskname($storeid, $scfg, $vmid) if !$name;
|
||||||
|
run_cli($scfg, [ 'create', '--size', (int(($size+3)/4)*4).'k', '--pool', $scfg->{vitastor_pool}, $prefix.$name ], json => 0);
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub free_image
|
||||||
|
{
|
||||||
|
my ($class, $storeid, $scfg, $volname, $isBase) = @_;
|
||||||
|
my $prefix = defined $scfg->{vitastor_prefix} ? $scfg->{vitastor_prefix} : 'pve/';
|
||||||
|
my ($vtype, $name, $vmid, undef, undef, undef) = $class->parse_volname($volname);
|
||||||
|
$class->deactivate_volume($storeid, $scfg, $volname);
|
||||||
|
my $full_list = run_cli($scfg, [ 'ls', '-l' ]);
|
||||||
|
my $list = _process_list($scfg, $storeid, $full_list);
|
||||||
|
# Remove image and all its snapshots
|
||||||
|
my $rm_names = {
|
||||||
|
map { ($prefix.$_->{name} => 1) }
|
||||||
|
grep { $_->{name} eq $name || substr($_->{name}, 0, length($name)+1) eq ($name.'@') }
|
||||||
|
@$list
|
||||||
|
};
|
||||||
|
my $children = [ grep { $_->{parent_name} && $rm_names->{$_->{parent_name}} } @$full_list ];
|
||||||
|
die "Image has children: ".join(', ', map {
|
||||||
|
substr($_->{name}, 0, length $prefix) eq $prefix
|
||||||
|
? substr($_->name, length $prefix)
|
||||||
|
: $_->{name}
|
||||||
|
} @$children)."\n" if @$children;
|
||||||
|
my $to_remove = [ grep { $rm_names->{$_->{name}} } @$full_list ];
|
||||||
|
for my $rmi (@$to_remove)
|
||||||
|
{
|
||||||
|
run_cli($scfg, [ 'rm-data', '--pool', $rmi->{pool_id}, '--inode', $rmi->{inode_num} ], json => 0);
|
||||||
|
}
|
||||||
|
for my $rmi (@$to_remove)
|
||||||
|
{
|
||||||
|
run_cli($scfg, [ 'rm', $rmi->{name} ], json => 0);
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _process_list
|
||||||
|
{
|
||||||
|
my ($scfg, $storeid, $result) = @_;
|
||||||
|
my $prefix = defined $scfg->{vitastor_prefix} ? $scfg->{vitastor_prefix} : 'pve/';
|
||||||
|
my $list = [];
|
||||||
|
foreach my $el (@$result)
|
||||||
|
{
|
||||||
|
next if !$el->{name} || length($prefix) && substr($el->{name}, 0, length $prefix) ne $prefix;
|
||||||
|
my $name = substr($el->{name}, length $prefix);
|
||||||
|
next if $name =~ /@/;
|
||||||
|
my ($owner) = $name =~ /^(?:vm|base)-(\d+)-/s;
|
||||||
|
next if !defined $owner;
|
||||||
|
my $parent = !defined $el->{parent_name}
|
||||||
|
? undef
|
||||||
|
: ($prefix eq '' || substr($el->{parent_name}, 0, length $prefix) eq $prefix
|
||||||
|
? substr($el->{parent_name}, length $prefix) : '');
|
||||||
|
my $volid = $parent && $parent =~ /^(base-\d+-\S+)$/s
|
||||||
|
? "$storeid:$1/$name" : "$storeid:$name";
|
||||||
|
push @$list, {
|
||||||
|
format => 'raw',
|
||||||
|
volid => $volid,
|
||||||
|
name => $name,
|
||||||
|
size => $el->{size},
|
||||||
|
parent => $parent,
|
||||||
|
vmid => $owner,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub list_images
|
||||||
|
{
|
||||||
|
my ($class, $storeid, $scfg, $vmid, $vollist, $cache) = @_;
|
||||||
|
my $list = _process_list($scfg, $storeid, run_cli($scfg, [ 'ls', '-l' ]));
|
||||||
|
if ($vollist)
|
||||||
|
{
|
||||||
|
my $h = { map { ($_ => 1) } @$vollist };
|
||||||
|
$list = [ grep { $h->{$_->{volid}} } @$list ]
|
||||||
|
}
|
||||||
|
elsif (defined $vmid)
|
||||||
|
{
|
||||||
|
$list = [ grep { $_->{vmid} eq $vmid } @$list ];
|
||||||
|
}
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub status
|
||||||
|
{
|
||||||
|
my ($class, $storeid, $scfg, $cache) = @_;
|
||||||
|
my $stats = [ grep { $_->{name} eq $scfg->{vitastor_pool} } @{ run_cli($scfg, [ 'df' ]) } ]->[0];
|
||||||
|
my $free = $stats ? $stats->{max_available} : 0;
|
||||||
|
my $used = $stats ? $stats->{used_raw}/($stats->{raw_to_usable}||1) : 0;
|
||||||
|
my $total = $free+$used;
|
||||||
|
my $active = $stats ? 1 : 0;
|
||||||
|
return ($total, $free, $used, $active);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub activate_storage
|
||||||
|
{
|
||||||
|
my ($class, $storeid, $scfg, $cache) = @_;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub deactivate_storage
|
||||||
|
{
|
||||||
|
my ($class, $storeid, $scfg, $cache) = @_;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub map_volume
|
||||||
|
{
|
||||||
|
my ($class, $storeid, $scfg, $volname, $snapname) = @_;
|
||||||
|
my $prefix = defined $scfg->{vitastor_prefix} ? $scfg->{vitastor_prefix} : 'pve/';
|
||||||
|
|
||||||
|
my ($vtype, $img_name, $vmid) = $class->parse_volname($volname);
|
||||||
|
my $name = $img_name;
|
||||||
|
$name .= '@'.$snapname if $snapname;
|
||||||
|
|
||||||
|
my $mapped = run_cli($scfg, [ 'ls' ], binary => '/usr/bin/vitastor-nbd');
|
||||||
|
my ($kerneldev) = grep { $mapped->{$_}->{image} eq $prefix.$name } keys %$mapped;
|
||||||
|
return $kerneldev if $kerneldev && -b $kerneldev; # already mapped
|
||||||
|
|
||||||
|
$kerneldev = run_cli($scfg, [ 'map', '--image', $prefix.$name ], binary => '/usr/bin/vitastor-nbd', json => 0);
|
||||||
|
return $kerneldev;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub unmap_volume
|
||||||
|
{
|
||||||
|
my ($class, $storeid, $scfg, $volname, $snapname) = @_;
|
||||||
|
my $prefix = defined $scfg->{vitastor_prefix} ? $scfg->{vitastor_prefix} : 'pve/';
|
||||||
|
|
||||||
|
return 1 if !$scfg->{vitastor_nbd};
|
||||||
|
|
||||||
|
my ($vtype, $name, $vmid) = $class->parse_volname($volname);
|
||||||
|
$name .= '@'.$snapname if $snapname;
|
||||||
|
|
||||||
|
my $mapped = run_cli($scfg, [ 'ls' ], binary => '/usr/bin/vitastor-nbd');
|
||||||
|
my ($kerneldev) = grep { $mapped->{$_}->{image} eq $prefix.$name } keys %$mapped;
|
||||||
|
if ($kerneldev && -b $kerneldev)
|
||||||
|
{
|
||||||
|
run_cli($scfg, [ 'unmap', $kerneldev ], binary => '/usr/bin/vitastor-nbd', json => 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub activate_volume
|
||||||
|
{
|
||||||
|
my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
|
||||||
|
$class->map_volume($storeid, $scfg, $volname, $snapname) if $scfg->{vitastor_nbd};
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub deactivate_volume
|
||||||
|
{
|
||||||
|
my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
|
||||||
|
$class->unmap_volume($storeid, $scfg, $volname, $snapname);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub volume_size_info
|
||||||
|
{
|
||||||
|
my ($class, $scfg, $storeid, $volname, $timeout) = @_;
|
||||||
|
my $prefix = defined $scfg->{vitastor_prefix} ? $scfg->{vitastor_prefix} : 'pve/';
|
||||||
|
my ($vtype, $name, $vmid) = $class->parse_volname($volname);
|
||||||
|
my $info = _process_list($scfg, $storeid, run_cli($scfg, [ 'ls', $prefix.$name ]))->[0];
|
||||||
|
#return wantarray ? ($size, $format, $used, $parent, $st->ctime) : $size;
|
||||||
|
return $info->{size};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub volume_resize
|
||||||
|
{
|
||||||
|
my ($class, $scfg, $storeid, $volname, $size, $running) = @_;
|
||||||
|
my $prefix = defined $scfg->{vitastor_prefix} ? $scfg->{vitastor_prefix} : 'pve/';
|
||||||
|
my ($vtype, $name, $vmid) = $class->parse_volname($volname);
|
||||||
|
# $size is in bytes in this method
|
||||||
|
run_cli($scfg, [ 'modify', '--resize', (int(($size+4095)/4096)*4).'k', $prefix.$name ], json => 0);
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub volume_snapshot
|
||||||
|
{
|
||||||
|
my ($class, $scfg, $storeid, $volname, $snap) = @_;
|
||||||
|
my $prefix = defined $scfg->{vitastor_prefix} ? $scfg->{vitastor_prefix} : 'pve/';
|
||||||
|
my ($vtype, $name, $vmid) = $class->parse_volname($volname);
|
||||||
|
run_cli($scfg, [ 'create', '--snapshot', $snap, $prefix.$name ], json => 0);
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub volume_snapshot_rollback
|
||||||
|
{
|
||||||
|
my ($class, $scfg, $storeid, $volname, $snap) = @_;
|
||||||
|
my $prefix = defined $scfg->{vitastor_prefix} ? $scfg->{vitastor_prefix} : 'pve/';
|
||||||
|
my ($vtype, $name, $vmid) = $class->parse_volname($volname);
|
||||||
|
run_cli($scfg, [ 'rm', $prefix.$name ], json => 0);
|
||||||
|
run_cli($scfg, [ 'create', '--parent', $prefix.$name.'@'.$snap, $prefix.$name ], json => 0);
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub volume_snapshot_delete
|
||||||
|
{
|
||||||
|
my ($class, $scfg, $storeid, $volname, $snap, $running) = @_;
|
||||||
|
my $prefix = defined $scfg->{vitastor_prefix} ? $scfg->{vitastor_prefix} : 'pve/';
|
||||||
|
my ($vtype, $name, $vmid) = $class->parse_volname($volname);
|
||||||
|
run_cli($scfg, [ 'rm', $prefix.$name.'@'.$snap ], json => 0);
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub volume_snapshot_needs_fsfreeze
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub volume_has_feature
|
||||||
|
{
|
||||||
|
my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_;
|
||||||
|
my $features = {
|
||||||
|
snapshot => { current => 1, snap => 1 },
|
||||||
|
clone => { base => 1, snap => 1 },
|
||||||
|
template => { current => 1 },
|
||||||
|
copy => { base => 1, current => 1, snap => 1 },
|
||||||
|
sparseinit => { base => 1, current => 1 },
|
||||||
|
rename => { current => 1 },
|
||||||
|
};
|
||||||
|
my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) = $class->parse_volname($volname);
|
||||||
|
my $key = undef;
|
||||||
|
if ($snapname)
|
||||||
|
{
|
||||||
|
$key = 'snap';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$key = $isBase ? 'base' : 'current';
|
||||||
|
}
|
||||||
|
return 1 if $features->{$feature}->{$key};
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub rename_volume
|
||||||
|
{
|
||||||
|
my ($class, $scfg, $storeid, $source_volname, $target_vmid, $target_volname) = @_;
|
||||||
|
my $prefix = defined $scfg->{vitastor_prefix} ? $scfg->{vitastor_prefix} : 'pve/';
|
||||||
|
my (undef, $source_image, $source_vmid, $base_name, $base_vmid, undef, $format) =
|
||||||
|
$class->parse_volname($source_volname);
|
||||||
|
$target_volname = $class->find_free_diskname($storeid, $scfg, $target_vmid, $format) if !$target_volname;
|
||||||
|
run_cli($scfg, [ 'modify', '--rename', $prefix.$target_volname, $prefix.$source_image ], json => 0);
|
||||||
|
$base_name = $base_name ? "${base_name}/" : '';
|
||||||
|
return "${storeid}:${base_name}${target_volname}";
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
@@ -50,7 +50,7 @@ from cinder.volume import configuration
|
|||||||
from cinder.volume import driver
|
from cinder.volume import driver
|
||||||
from cinder.volume import volume_utils
|
from cinder.volume import volume_utils
|
||||||
|
|
||||||
VERSION = '0.6.9'
|
VERSION = '0.6.11'
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
288
patches/nova-21.diff
Normal file
288
patches/nova-21.diff
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
diff --git a/nova/virt/image/model.py b/nova/virt/image/model.py
|
||||||
|
index 971f7e9c07..ec3fca72cb 100644
|
||||||
|
--- a/nova/virt/image/model.py
|
||||||
|
+++ b/nova/virt/image/model.py
|
||||||
|
@@ -129,3 +129,22 @@ class RBDImage(Image):
|
||||||
|
self.user = user
|
||||||
|
self.password = password
|
||||||
|
self.servers = servers
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+class VitastorImage(Image):
|
||||||
|
+ """Class for images in a remote Vitastor cluster"""
|
||||||
|
+
|
||||||
|
+ def __init__(self, name, etcd_address = None, etcd_prefix = None, config_path = None):
|
||||||
|
+ """Create a new Vitastor image object
|
||||||
|
+
|
||||||
|
+ :param name: name of the image
|
||||||
|
+ :param etcd_address: etcd URL(s) (optional)
|
||||||
|
+ :param etcd_prefix: etcd prefix (optional)
|
||||||
|
+ :param config_path: path to the configuration (optional)
|
||||||
|
+ """
|
||||||
|
+ super(VitastorImage, self).__init__(FORMAT_RAW)
|
||||||
|
+
|
||||||
|
+ self.name = name
|
||||||
|
+ self.etcd_address = etcd_address
|
||||||
|
+ self.etcd_prefix = etcd_prefix
|
||||||
|
+ self.config_path = config_path
|
||||||
|
diff --git a/nova/virt/images.py b/nova/virt/images.py
|
||||||
|
index 5358f3766a..ebe3d6effb 100644
|
||||||
|
--- a/nova/virt/images.py
|
||||||
|
+++ b/nova/virt/images.py
|
||||||
|
@@ -41,7 +41,7 @@ IMAGE_API = glance.API()
|
||||||
|
|
||||||
|
def qemu_img_info(path, format=None):
|
||||||
|
"""Return an object containing the parsed output from qemu-img info."""
|
||||||
|
- if not os.path.exists(path) and not path.startswith('rbd:'):
|
||||||
|
+ if not os.path.exists(path) and not path.startswith('rbd:') and not path.startswith('vitastor:'):
|
||||||
|
raise exception.DiskNotFound(location=path)
|
||||||
|
|
||||||
|
info = nova.privsep.qemu.unprivileged_qemu_img_info(path, format=format)
|
||||||
|
@@ -50,7 +50,7 @@ def qemu_img_info(path, format=None):
|
||||||
|
|
||||||
|
def privileged_qemu_img_info(path, format=None, output_format='json'):
|
||||||
|
"""Return an object containing the parsed output from qemu-img info."""
|
||||||
|
- if not os.path.exists(path) and not path.startswith('rbd:'):
|
||||||
|
+ if not os.path.exists(path) and not path.startswith('rbd:') and not path.startswith('vitastor:'):
|
||||||
|
raise exception.DiskNotFound(location=path)
|
||||||
|
|
||||||
|
info = nova.privsep.qemu.privileged_qemu_img_info(path, format=format)
|
||||||
|
diff --git a/nova/virt/libvirt/config.py b/nova/virt/libvirt/config.py
|
||||||
|
index ea525648b3..d7aa798954 100644
|
||||||
|
--- a/nova/virt/libvirt/config.py
|
||||||
|
+++ b/nova/virt/libvirt/config.py
|
||||||
|
@@ -1005,6 +1005,8 @@ class LibvirtConfigGuestDisk(LibvirtConfigGuestDevice):
|
||||||
|
self.driver_iommu = False
|
||||||
|
self.source_path = None
|
||||||
|
self.source_protocol = None
|
||||||
|
+ self.source_query = None
|
||||||
|
+ self.source_config = None
|
||||||
|
self.source_name = None
|
||||||
|
self.source_hosts = []
|
||||||
|
self.source_ports = []
|
||||||
|
@@ -1133,6 +1135,10 @@ class LibvirtConfigGuestDisk(LibvirtConfigGuestDevice):
|
||||||
|
source = etree.Element("source", protocol=self.source_protocol)
|
||||||
|
if self.source_name is not None:
|
||||||
|
source.set('name', self.source_name)
|
||||||
|
+ if self.source_query is not None:
|
||||||
|
+ source.set('query', self.source_query)
|
||||||
|
+ if self.source_config is not None:
|
||||||
|
+ source.append(etree.Element('config', file=self.source_config))
|
||||||
|
hosts_info = zip(self.source_hosts, self.source_ports)
|
||||||
|
for name, port in hosts_info:
|
||||||
|
host = etree.Element('host', name=name)
|
||||||
|
diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py
|
||||||
|
index fbd033690a..74dc59ce87 100644
|
||||||
|
--- a/nova/virt/libvirt/driver.py
|
||||||
|
+++ b/nova/virt/libvirt/driver.py
|
||||||
|
@@ -180,6 +180,7 @@ libvirt_volume_drivers = [
|
||||||
|
'local=nova.virt.libvirt.volume.volume.LibvirtVolumeDriver',
|
||||||
|
'fake=nova.virt.libvirt.volume.volume.LibvirtFakeVolumeDriver',
|
||||||
|
'rbd=nova.virt.libvirt.volume.net.LibvirtNetVolumeDriver',
|
||||||
|
+ 'vitastor=nova.virt.libvirt.volume.vitastor.LibvirtVitastorVolumeDriver',
|
||||||
|
'nfs=nova.virt.libvirt.volume.nfs.LibvirtNFSVolumeDriver',
|
||||||
|
'smbfs=nova.virt.libvirt.volume.smbfs.LibvirtSMBFSVolumeDriver',
|
||||||
|
'fibre_channel='
|
||||||
|
@@ -287,10 +288,10 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||||
|
# This prevents the risk of one test setting a capability
|
||||||
|
# which bleeds over into other tests.
|
||||||
|
|
||||||
|
- # LVM and RBD require raw images. If we are not configured to
|
||||||
|
+ # LVM, RBD, Vitastor require raw images. If we are not configured to
|
||||||
|
# force convert images into raw format, then we _require_ raw
|
||||||
|
# images only.
|
||||||
|
- raw_only = ('rbd', 'lvm')
|
||||||
|
+ raw_only = ('rbd', 'lvm', 'vitastor')
|
||||||
|
requires_raw_image = (CONF.libvirt.images_type in raw_only and
|
||||||
|
not CONF.force_raw_images)
|
||||||
|
requires_ploop_image = CONF.libvirt.virt_type == 'parallels'
|
||||||
|
@@ -703,12 +704,12 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||||
|
# Some imagebackends are only able to import raw disk images,
|
||||||
|
# and will fail if given any other format. See the bug
|
||||||
|
# https://bugs.launchpad.net/nova/+bug/1816686 for more details.
|
||||||
|
- if CONF.libvirt.images_type in ('rbd',):
|
||||||
|
+ if CONF.libvirt.images_type in ('rbd', 'vitastor'):
|
||||||
|
if not CONF.force_raw_images:
|
||||||
|
msg = _("'[DEFAULT]/force_raw_images = False' is not "
|
||||||
|
- "allowed with '[libvirt]/images_type = rbd'. "
|
||||||
|
+ "allowed with '[libvirt]/images_type = rbd' or 'vitastor'. "
|
||||||
|
"Please check the two configs and if you really "
|
||||||
|
- "do want to use rbd as images_type, set "
|
||||||
|
+ "do want to use rbd or vitastor as images_type, set "
|
||||||
|
"force_raw_images to True.")
|
||||||
|
raise exception.InvalidConfiguration(msg)
|
||||||
|
|
||||||
|
@@ -2165,6 +2166,16 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||||
|
if connection_info['data'].get('auth_enabled'):
|
||||||
|
username = connection_info['data']['auth_username']
|
||||||
|
path = f"rbd:{volume_name}:id={username}"
|
||||||
|
+ elif connection_info['driver_volume_type'] == 'vitastor':
|
||||||
|
+ volume_name = connection_info['data']['name']
|
||||||
|
+ path = 'vitastor:image='+volume_name.replace(':', '\\:')
|
||||||
|
+ for k in [ 'config_path', 'etcd_address', 'etcd_prefix' ]:
|
||||||
|
+ if k in connection_info['data']:
|
||||||
|
+ kk = k
|
||||||
|
+ if kk == 'etcd_address':
|
||||||
|
+ # FIXME use etcd_address in qemu driver
|
||||||
|
+ kk = 'etcd_host'
|
||||||
|
+ path += ":"+kk.replace('_', '-')+"="+connection_info['data'][k].replace(':', '\\:')
|
||||||
|
else:
|
||||||
|
path = 'unknown'
|
||||||
|
raise exception.DiskNotFound(location='unknown')
|
||||||
|
@@ -2440,8 +2451,8 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||||
|
|
||||||
|
image_format = CONF.libvirt.snapshot_image_format or source_type
|
||||||
|
|
||||||
|
- # NOTE(bfilippov): save lvm and rbd as raw
|
||||||
|
- if image_format == 'lvm' or image_format == 'rbd':
|
||||||
|
+ # NOTE(bfilippov): save lvm and rbd and vitastor as raw
|
||||||
|
+ if image_format == 'lvm' or image_format == 'rbd' or image_format == 'vitastor':
|
||||||
|
image_format = 'raw'
|
||||||
|
|
||||||
|
metadata = self._create_snapshot_metadata(instance.image_meta,
|
||||||
|
@@ -2512,7 +2523,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||||
|
expected_state=task_states.IMAGE_UPLOADING)
|
||||||
|
|
||||||
|
# TODO(nic): possibly abstract this out to the root_disk
|
||||||
|
- if source_type == 'rbd' and live_snapshot:
|
||||||
|
+ if (source_type == 'rbd' or source_type == 'vitastor') and live_snapshot:
|
||||||
|
# Standard snapshot uses qemu-img convert from RBD which is
|
||||||
|
# not safe to run with live_snapshot.
|
||||||
|
live_snapshot = False
|
||||||
|
@@ -3715,7 +3726,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||||
|
# cleanup rescue volume
|
||||||
|
lvm.remove_volumes([lvmdisk for lvmdisk in self._lvm_disks(instance)
|
||||||
|
if lvmdisk.endswith('.rescue')])
|
||||||
|
- if CONF.libvirt.images_type == 'rbd':
|
||||||
|
+ if CONF.libvirt.images_type == 'rbd' or CONF.libvirt.images_type == 'vitastor':
|
||||||
|
filter_fn = lambda disk: (disk.startswith(instance.uuid) and
|
||||||
|
disk.endswith('.rescue'))
|
||||||
|
rbd_utils.RBDDriver().cleanup_volumes(filter_fn)
|
||||||
|
@@ -3972,6 +3983,8 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||||
|
# TODO(mikal): there is a bug here if images_type has
|
||||||
|
# changed since creation of the instance, but I am pretty
|
||||||
|
# sure that this bug already exists.
|
||||||
|
+ if CONF.libvirt.images_type == 'vitastor':
|
||||||
|
+ return 'vitastor'
|
||||||
|
return 'rbd' if CONF.libvirt.images_type == 'rbd' else 'raw'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@@ -4370,10 +4383,10 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||||
|
finally:
|
||||||
|
# NOTE(mikal): if the config drive was imported into RBD,
|
||||||
|
# then we no longer need the local copy
|
||||||
|
- if CONF.libvirt.images_type == 'rbd':
|
||||||
|
+ if CONF.libvirt.images_type == 'rbd' or CONF.libvirt.images_type == 'vitastor':
|
||||||
|
LOG.info('Deleting local config drive %(path)s '
|
||||||
|
- 'because it was imported into RBD.',
|
||||||
|
- {'path': config_disk_local_path},
|
||||||
|
+ 'because it was imported into %(type).',
|
||||||
|
+ {'path': config_disk_local_path, 'type': CONF.libvirt.images_type},
|
||||||
|
instance=instance)
|
||||||
|
os.unlink(config_disk_local_path)
|
||||||
|
|
||||||
|
diff --git a/nova/virt/libvirt/utils.py b/nova/virt/libvirt/utils.py
|
||||||
|
index c1dc34daf4..263965912f 100644
|
||||||
|
--- a/nova/virt/libvirt/utils.py
|
||||||
|
+++ b/nova/virt/libvirt/utils.py
|
||||||
|
@@ -399,6 +399,10 @@ def find_disk(guest: libvirt_guest.Guest) -> ty.Tuple[str, ty.Optional[str]]:
|
||||||
|
disk_path = disk.source_name
|
||||||
|
if disk_path:
|
||||||
|
disk_path = 'rbd:' + disk_path
|
||||||
|
+ elif not disk_path and disk.source_protocol == 'vitastor':
|
||||||
|
+ disk_path = disk.source_name
|
||||||
|
+ if disk_path:
|
||||||
|
+ disk_path = 'vitastor:' + disk_path
|
||||||
|
|
||||||
|
if not disk_path:
|
||||||
|
raise RuntimeError(_("Can't retrieve root device path "
|
||||||
|
@@ -417,6 +421,8 @@ def get_disk_type_from_path(path: str) -> ty.Optional[str]:
|
||||||
|
return 'lvm'
|
||||||
|
elif path.startswith('rbd:'):
|
||||||
|
return 'rbd'
|
||||||
|
+ elif path.startswith('vitastor:'):
|
||||||
|
+ return 'vitastor'
|
||||||
|
elif (os.path.isdir(path) and
|
||||||
|
os.path.exists(os.path.join(path, "DiskDescriptor.xml"))):
|
||||||
|
return 'ploop'
|
||||||
|
diff --git a/nova/virt/libvirt/volume/vitastor.py b/nova/virt/libvirt/volume/vitastor.py
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000..0256df62c1
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/nova/virt/libvirt/volume/vitastor.py
|
||||||
|
@@ -0,0 +1,75 @@
|
||||||
|
+# Copyright (c) 2021+, Vitaliy Filippov <vitalif@yourcmc.ru>
|
||||||
|
+#
|
||||||
|
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
+# not use this file except in compliance with the License. You may obtain
|
||||||
|
+# a copy of the License at
|
||||||
|
+#
|
||||||
|
+# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
+#
|
||||||
|
+# Unless required by applicable law or agreed to in writing, software
|
||||||
|
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
+# License for the specific language governing permissions and limitations
|
||||||
|
+# under the License.
|
||||||
|
+
|
||||||
|
+from os_brick import exception as os_brick_exception
|
||||||
|
+from os_brick import initiator
|
||||||
|
+from os_brick.initiator import connector
|
||||||
|
+from oslo_log import log as logging
|
||||||
|
+
|
||||||
|
+import nova.conf
|
||||||
|
+from nova import utils
|
||||||
|
+from nova.virt.libvirt.volume import volume as libvirt_volume
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+CONF = nova.conf.CONF
|
||||||
|
+LOG = logging.getLogger(__name__)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+class LibvirtVitastorVolumeDriver(libvirt_volume.LibvirtBaseVolumeDriver):
|
||||||
|
+ """Driver to attach Vitastor volumes to libvirt."""
|
||||||
|
+ def __init__(self, host):
|
||||||
|
+ super(LibvirtVitastorVolumeDriver, self).__init__(host, is_block_dev=False)
|
||||||
|
+
|
||||||
|
+ def connect_volume(self, connection_info, instance):
|
||||||
|
+ pass
|
||||||
|
+
|
||||||
|
+ def disconnect_volume(self, connection_info, instance):
|
||||||
|
+ pass
|
||||||
|
+
|
||||||
|
+ def get_config(self, connection_info, disk_info):
|
||||||
|
+ """Returns xml for libvirt."""
|
||||||
|
+ conf = super(LibvirtVitastorVolumeDriver, self).get_config(connection_info, disk_info)
|
||||||
|
+ conf.source_type = 'network'
|
||||||
|
+ conf.source_protocol = 'vitastor'
|
||||||
|
+ conf.source_name = connection_info['data'].get('name')
|
||||||
|
+ conf.source_query = connection_info['data'].get('etcd_prefix') or None
|
||||||
|
+ conf.source_config = connection_info['data'].get('config_path') or None
|
||||||
|
+ conf.source_hosts = []
|
||||||
|
+ conf.source_ports = []
|
||||||
|
+ addresses = connection_info['data'].get('etcd_address', '')
|
||||||
|
+ if addresses:
|
||||||
|
+ if not isinstance(addresses, list):
|
||||||
|
+ addresses = addresses.split(',')
|
||||||
|
+ for addr in addresses:
|
||||||
|
+ if addr.startswith('https://'):
|
||||||
|
+ raise NotImplementedError('Vitastor block driver does not support SSL for etcd communication yet')
|
||||||
|
+ if addr.startswith('http://'):
|
||||||
|
+ addr = addr[7:]
|
||||||
|
+ addr = addr.rstrip('/')
|
||||||
|
+ if addr.endswith('/v3'):
|
||||||
|
+ addr = addr[0:-3]
|
||||||
|
+ p = addr.find('/')
|
||||||
|
+ if p > 0:
|
||||||
|
+ raise NotImplementedError('libvirt does not support custom URL paths for Vitastor etcd yet. Use /etc/vitastor/vitastor.conf')
|
||||||
|
+ p = addr.find(':')
|
||||||
|
+ port = '2379'
|
||||||
|
+ if p > 0:
|
||||||
|
+ port = addr[p+1:]
|
||||||
|
+ addr = addr[0:p]
|
||||||
|
+ conf.source_hosts.append(addr)
|
||||||
|
+ conf.source_ports.append(port)
|
||||||
|
+ return conf
|
||||||
|
+
|
||||||
|
+ def extend_volume(self, connection_info, instance, requested_size):
|
||||||
|
+ raise NotImplementedError
|
175
patches/pve-qemu-5.1-vitastor.patch
Normal file
175
patches/pve-qemu-5.1-vitastor.patch
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
Index: pve-qemu-kvm-5.1.0/qapi/block-core.json
|
||||||
|
===================================================================
|
||||||
|
--- pve-qemu-kvm-5.1.0.orig/qapi/block-core.json
|
||||||
|
+++ pve-qemu-kvm-5.1.0/qapi/block-core.json
|
||||||
|
@@ -3041,7 +3041,7 @@
|
||||||
|
'luks', 'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels',
|
||||||
|
'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'rbd',
|
||||||
|
{ 'name': 'replication', 'if': 'defined(CONFIG_REPLICATION)' },
|
||||||
|
- 'sheepdog', 'pbs',
|
||||||
|
+ 'sheepdog', 'pbs', 'vitastor',
|
||||||
|
'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] }
|
||||||
|
|
||||||
|
##
|
||||||
|
@@ -3889,6 +3889,28 @@
|
||||||
|
'*tag': 'str' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
+# @BlockdevOptionsVitastor:
|
||||||
|
+#
|
||||||
|
+# Driver specific block device options for vitastor
|
||||||
|
+#
|
||||||
|
+# @image: Image name
|
||||||
|
+# @inode: Inode number
|
||||||
|
+# @pool: Pool ID
|
||||||
|
+# @size: Desired image size in bytes
|
||||||
|
+# @config-path: Path to Vitastor configuration
|
||||||
|
+# @etcd-host: etcd connection address(es)
|
||||||
|
+# @etcd-prefix: etcd key/value prefix
|
||||||
|
+##
|
||||||
|
+{ 'struct': 'BlockdevOptionsVitastor',
|
||||||
|
+ 'data': { '*inode': 'uint64',
|
||||||
|
+ '*pool': 'uint64',
|
||||||
|
+ '*size': 'uint64',
|
||||||
|
+ '*image': 'str',
|
||||||
|
+ '*config-path': 'str',
|
||||||
|
+ '*etcd-host': 'str',
|
||||||
|
+ '*etcd-prefix': 'str' } }
|
||||||
|
+
|
||||||
|
+##
|
||||||
|
# @ReplicationMode:
|
||||||
|
#
|
||||||
|
# An enumeration of replication modes.
|
||||||
|
@@ -4234,6 +4256,7 @@
|
||||||
|
'replication': { 'type': 'BlockdevOptionsReplication',
|
||||||
|
'if': 'defined(CONFIG_REPLICATION)' },
|
||||||
|
'sheepdog': 'BlockdevOptionsSheepdog',
|
||||||
|
+ 'vitastor': 'BlockdevOptionsVitastor',
|
||||||
|
'ssh': 'BlockdevOptionsSsh',
|
||||||
|
'throttle': 'BlockdevOptionsThrottle',
|
||||||
|
'vdi': 'BlockdevOptionsGenericFormat',
|
||||||
|
@@ -4623,6 +4646,17 @@
|
||||||
|
'*cluster-size' : 'size' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
+# @BlockdevCreateOptionsVitastor:
|
||||||
|
+#
|
||||||
|
+# Driver specific image creation options for Vitastor.
|
||||||
|
+#
|
||||||
|
+# @size: Size of the virtual disk in bytes
|
||||||
|
+##
|
||||||
|
+{ 'struct': 'BlockdevCreateOptionsVitastor',
|
||||||
|
+ 'data': { 'location': 'BlockdevOptionsVitastor',
|
||||||
|
+ 'size': 'size' } }
|
||||||
|
+
|
||||||
|
+##
|
||||||
|
# @BlockdevVmdkSubformat:
|
||||||
|
#
|
||||||
|
# Subformat options for VMDK images
|
||||||
|
@@ -4884,6 +4918,7 @@
|
||||||
|
'qed': 'BlockdevCreateOptionsQed',
|
||||||
|
'rbd': 'BlockdevCreateOptionsRbd',
|
||||||
|
'sheepdog': 'BlockdevCreateOptionsSheepdog',
|
||||||
|
+ 'vitastor': 'BlockdevCreateOptionsVitastor',
|
||||||
|
'ssh': 'BlockdevCreateOptionsSsh',
|
||||||
|
'vdi': 'BlockdevCreateOptionsVdi',
|
||||||
|
'vhdx': 'BlockdevCreateOptionsVhdx',
|
||||||
|
Index: pve-qemu-kvm-5.1.0/configure
|
||||||
|
===================================================================
|
||||||
|
--- pve-qemu-kvm-5.1.0.orig/configure
|
||||||
|
+++ pve-qemu-kvm-5.1.0/configure
|
||||||
|
@@ -446,6 +446,7 @@ trace_backends="log"
|
||||||
|
trace_file="trace"
|
||||||
|
spice=""
|
||||||
|
rbd=""
|
||||||
|
+vitastor=""
|
||||||
|
smartcard=""
|
||||||
|
libusb=""
|
||||||
|
usb_redir=""
|
||||||
|
@@ -1383,6 +1384,10 @@ for opt do
|
||||||
|
;;
|
||||||
|
--enable-rbd) rbd="yes"
|
||||||
|
;;
|
||||||
|
+ --disable-vitastor) vitastor="no"
|
||||||
|
+ ;;
|
||||||
|
+ --enable-vitastor) vitastor="yes"
|
||||||
|
+ ;;
|
||||||
|
--disable-xfsctl) xfs="no"
|
||||||
|
;;
|
||||||
|
--enable-xfsctl) xfs="yes"
|
||||||
|
@@ -1901,6 +1906,7 @@ disabled with --disable-FEATURE, default
|
||||||
|
vhost-vdpa vhost-vdpa kernel backend support
|
||||||
|
spice spice
|
||||||
|
rbd rados block device (rbd)
|
||||||
|
+ vitastor vitastor block device
|
||||||
|
libiscsi iscsi support
|
||||||
|
libnfs nfs support
|
||||||
|
smartcard smartcard support (libcacard)
|
||||||
|
@@ -4234,6 +4240,27 @@ EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
##########################################
|
||||||
|
+# vitastor probe
|
||||||
|
+if test "$vitastor" != "no" ; then
|
||||||
|
+ cat > $TMPC <<EOF
|
||||||
|
+#include <vitastor_c.h>
|
||||||
|
+int main(void) {
|
||||||
|
+ vitastor_c_create_qemu(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+EOF
|
||||||
|
+ vitastor_libs="-lvitastor_client"
|
||||||
|
+ if compile_prog "" "$vitastor_libs" ; then
|
||||||
|
+ vitastor=yes
|
||||||
|
+ else
|
||||||
|
+ if test "$vitastor" = "yes" ; then
|
||||||
|
+ feature_not_found "vitastor block device" "Install vitastor-client-dev"
|
||||||
|
+ fi
|
||||||
|
+ vitastor=no
|
||||||
|
+ fi
|
||||||
|
+fi
|
||||||
|
+
|
||||||
|
+##########################################
|
||||||
|
# libssh probe
|
||||||
|
if test "$libssh" != "no" ; then
|
||||||
|
if $pkg_config --exists libssh; then
|
||||||
|
@@ -6969,6 +6996,7 @@ echo "Trace output file $trace_file-<pid
|
||||||
|
fi
|
||||||
|
echo "spice support $spice $(echo_version $spice $spice_protocol_version/$spice_server_version)"
|
||||||
|
echo "rbd support $rbd"
|
||||||
|
+echo "vitastor support $vitastor"
|
||||||
|
echo "xfsctl support $xfs"
|
||||||
|
echo "smartcard support $smartcard"
|
||||||
|
echo "libusb $libusb"
|
||||||
|
@@ -7644,6 +7672,10 @@ if test "$rbd" = "yes" ; then
|
||||||
|
echo "RBD_CFLAGS=$rbd_cflags" >> $config_host_mak
|
||||||
|
echo "RBD_LIBS=$rbd_libs" >> $config_host_mak
|
||||||
|
fi
|
||||||
|
+if test "$vitastor" = "yes" ; then
|
||||||
|
+ echo "CONFIG_VITASTOR=y" >> $config_host_mak
|
||||||
|
+ echo "VITASTOR_LIBS=$vitastor_libs" >> $config_host_mak
|
||||||
|
+fi
|
||||||
|
|
||||||
|
echo "CONFIG_COROUTINE_BACKEND=$coroutine" >> $config_host_mak
|
||||||
|
if test "$coroutine_pool" = "yes" ; then
|
||||||
|
Index: pve-qemu-kvm-5.1.0/block/Makefile.objs
|
||||||
|
===================================================================
|
||||||
|
--- pve-qemu-kvm-5.1.0.orig/block/Makefile.objs
|
||||||
|
+++ pve-qemu-kvm-5.1.0/block/Makefile.objs
|
||||||
|
@@ -32,6 +32,7 @@ block-obj-$(if $(CONFIG_LIBISCSI),y,n) +
|
||||||
|
block-obj-$(CONFIG_LIBNFS) += nfs.o
|
||||||
|
block-obj-$(CONFIG_CURL) += curl.o
|
||||||
|
block-obj-$(CONFIG_RBD) += rbd.o
|
||||||
|
+block-obj-$(CONFIG_VITASTOR) += vitastor.o
|
||||||
|
block-obj-$(CONFIG_GLUSTERFS) += gluster.o
|
||||||
|
block-obj-$(CONFIG_LIBSSH) += ssh.o
|
||||||
|
block-obj-y += backup-dump.o
|
||||||
|
@@ -61,6 +62,8 @@ curl.o-cflags := $(CURL_CFLAGS)
|
||||||
|
curl.o-libs := $(CURL_LIBS)
|
||||||
|
rbd.o-cflags := $(RBD_CFLAGS)
|
||||||
|
rbd.o-libs := $(RBD_LIBS)
|
||||||
|
+vitastor.o-cflags := $(VITASTOR_CFLAGS)
|
||||||
|
+vitastor.o-libs := $(VITASTOR_LIBS)
|
||||||
|
gluster.o-cflags := $(GLUSTERFS_CFLAGS)
|
||||||
|
gluster.o-libs := $(GLUSTERFS_LIBS)
|
||||||
|
ssh.o-cflags := $(LIBSSH_CFLAGS)
|
181
patches/pve-qemu-5.2-vitastor.patch
Normal file
181
patches/pve-qemu-5.2-vitastor.patch
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
Index: pve-qemu-kvm-5.2.0/qapi/block-core.json
|
||||||
|
===================================================================
|
||||||
|
--- pve-qemu-kvm-5.2.0.orig/qapi/block-core.json
|
||||||
|
+++ pve-qemu-kvm-5.2.0/qapi/block-core.json
|
||||||
|
@@ -3076,7 +3076,7 @@
|
||||||
|
'luks', 'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels',
|
||||||
|
'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'rbd',
|
||||||
|
{ 'name': 'replication', 'if': 'defined(CONFIG_REPLICATION)' },
|
||||||
|
- 'sheepdog', 'pbs',
|
||||||
|
+ 'sheepdog', 'pbs', 'vitastor',
|
||||||
|
'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] }
|
||||||
|
|
||||||
|
##
|
||||||
|
@@ -3924,6 +3924,28 @@
|
||||||
|
'*tag': 'str' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
+# @BlockdevOptionsVitastor:
|
||||||
|
+#
|
||||||
|
+# Driver specific block device options for vitastor
|
||||||
|
+#
|
||||||
|
+# @image: Image name
|
||||||
|
+# @inode: Inode number
|
||||||
|
+# @pool: Pool ID
|
||||||
|
+# @size: Desired image size in bytes
|
||||||
|
+# @config-path: Path to Vitastor configuration
|
||||||
|
+# @etcd-host: etcd connection address(es)
|
||||||
|
+# @etcd-prefix: etcd key/value prefix
|
||||||
|
+##
|
||||||
|
+{ 'struct': 'BlockdevOptionsVitastor',
|
||||||
|
+ 'data': { '*inode': 'uint64',
|
||||||
|
+ '*pool': 'uint64',
|
||||||
|
+ '*size': 'uint64',
|
||||||
|
+ '*image': 'str',
|
||||||
|
+ '*config-path': 'str',
|
||||||
|
+ '*etcd-host': 'str',
|
||||||
|
+ '*etcd-prefix': 'str' } }
|
||||||
|
+
|
||||||
|
+##
|
||||||
|
# @ReplicationMode:
|
||||||
|
#
|
||||||
|
# An enumeration of replication modes.
|
||||||
|
@@ -4272,6 +4294,7 @@
|
||||||
|
'replication': { 'type': 'BlockdevOptionsReplication',
|
||||||
|
'if': 'defined(CONFIG_REPLICATION)' },
|
||||||
|
'sheepdog': 'BlockdevOptionsSheepdog',
|
||||||
|
+ 'vitastor': 'BlockdevOptionsVitastor',
|
||||||
|
'ssh': 'BlockdevOptionsSsh',
|
||||||
|
'throttle': 'BlockdevOptionsThrottle',
|
||||||
|
'vdi': 'BlockdevOptionsGenericFormat',
|
||||||
|
@@ -4662,6 +4685,17 @@
|
||||||
|
'*cluster-size' : 'size' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
+# @BlockdevCreateOptionsVitastor:
|
||||||
|
+#
|
||||||
|
+# Driver specific image creation options for Vitastor.
|
||||||
|
+#
|
||||||
|
+# @size: Size of the virtual disk in bytes
|
||||||
|
+##
|
||||||
|
+{ 'struct': 'BlockdevCreateOptionsVitastor',
|
||||||
|
+ 'data': { 'location': 'BlockdevOptionsVitastor',
|
||||||
|
+ 'size': 'size' } }
|
||||||
|
+
|
||||||
|
+##
|
||||||
|
# @BlockdevVmdkSubformat:
|
||||||
|
#
|
||||||
|
# Subformat options for VMDK images
|
||||||
|
@@ -4923,6 +4957,7 @@
|
||||||
|
'qed': 'BlockdevCreateOptionsQed',
|
||||||
|
'rbd': 'BlockdevCreateOptionsRbd',
|
||||||
|
'sheepdog': 'BlockdevCreateOptionsSheepdog',
|
||||||
|
+ 'vitastor': 'BlockdevCreateOptionsVitastor',
|
||||||
|
'ssh': 'BlockdevCreateOptionsSsh',
|
||||||
|
'vdi': 'BlockdevCreateOptionsVdi',
|
||||||
|
'vhdx': 'BlockdevCreateOptionsVhdx',
|
||||||
|
Index: pve-qemu-kvm-5.2.0/block/meson.build
|
||||||
|
===================================================================
|
||||||
|
--- pve-qemu-kvm-5.2.0.orig/block/meson.build
|
||||||
|
+++ pve-qemu-kvm-5.2.0/block/meson.build
|
||||||
|
@@ -89,6 +89,7 @@ foreach m : [
|
||||||
|
['CONFIG_LIBNFS', 'nfs', libnfs, 'nfs.c'],
|
||||||
|
['CONFIG_LIBSSH', 'ssh', libssh, 'ssh.c'],
|
||||||
|
['CONFIG_RBD', 'rbd', rbd, 'rbd.c'],
|
||||||
|
+ ['CONFIG_VITASTOR', 'vitastor', vitastor, 'vitastor.c'],
|
||||||
|
]
|
||||||
|
if config_host.has_key(m[0])
|
||||||
|
if enable_modules
|
||||||
|
Index: pve-qemu-kvm-5.2.0/configure
|
||||||
|
===================================================================
|
||||||
|
--- pve-qemu-kvm-5.2.0.orig/configure
|
||||||
|
+++ pve-qemu-kvm-5.2.0/configure
|
||||||
|
@@ -372,6 +372,7 @@ trace_backends="log"
|
||||||
|
trace_file="trace"
|
||||||
|
spice=""
|
||||||
|
rbd=""
|
||||||
|
+vitastor=""
|
||||||
|
smartcard=""
|
||||||
|
u2f="auto"
|
||||||
|
libusb=""
|
||||||
|
@@ -1264,6 +1265,10 @@ for opt do
|
||||||
|
;;
|
||||||
|
--enable-rbd) rbd="yes"
|
||||||
|
;;
|
||||||
|
+ --disable-vitastor) vitastor="no"
|
||||||
|
+ ;;
|
||||||
|
+ --enable-vitastor) vitastor="yes"
|
||||||
|
+ ;;
|
||||||
|
--disable-xfsctl) xfs="no"
|
||||||
|
;;
|
||||||
|
--enable-xfsctl) xfs="yes"
|
||||||
|
@@ -1807,6 +1812,7 @@ disabled with --disable-FEATURE, default
|
||||||
|
vhost-vdpa vhost-vdpa kernel backend support
|
||||||
|
spice spice
|
||||||
|
rbd rados block device (rbd)
|
||||||
|
+ vitastor vitastor block device
|
||||||
|
libiscsi iscsi support
|
||||||
|
libnfs nfs support
|
||||||
|
smartcard smartcard support (libcacard)
|
||||||
|
@@ -3700,6 +3706,27 @@ EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
##########################################
|
||||||
|
+# vitastor probe
|
||||||
|
+if test "$vitastor" != "no" ; then
|
||||||
|
+ cat > $TMPC <<EOF
|
||||||
|
+#include <vitastor_c.h>
|
||||||
|
+int main(void) {
|
||||||
|
+ vitastor_c_create_qemu(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+EOF
|
||||||
|
+ vitastor_libs="-lvitastor_client"
|
||||||
|
+ if compile_prog "" "$vitastor_libs" ; then
|
||||||
|
+ vitastor=yes
|
||||||
|
+ else
|
||||||
|
+ if test "$vitastor" = "yes" ; then
|
||||||
|
+ feature_not_found "vitastor block device" "Install vitastor-client-dev"
|
||||||
|
+ fi
|
||||||
|
+ vitastor=no
|
||||||
|
+ fi
|
||||||
|
+fi
|
||||||
|
+
|
||||||
|
+##########################################
|
||||||
|
# libssh probe
|
||||||
|
if test "$libssh" != "no" ; then
|
||||||
|
if $pkg_config --exists libssh; then
|
||||||
|
@@ -6437,6 +6464,10 @@ if test "$rbd" = "yes" ; then
|
||||||
|
echo "CONFIG_RBD=y" >> $config_host_mak
|
||||||
|
echo "RBD_LIBS=$rbd_libs" >> $config_host_mak
|
||||||
|
fi
|
||||||
|
+if test "$vitastor" = "yes" ; then
|
||||||
|
+ echo "CONFIG_VITASTOR=y" >> $config_host_mak
|
||||||
|
+ echo "VITASTOR_LIBS=$vitastor_libs" >> $config_host_mak
|
||||||
|
+fi
|
||||||
|
|
||||||
|
echo "CONFIG_COROUTINE_BACKEND=$coroutine" >> $config_host_mak
|
||||||
|
if test "$coroutine_pool" = "yes" ; then
|
||||||
|
Index: pve-qemu-kvm-5.2.0/meson.build
|
||||||
|
===================================================================
|
||||||
|
--- pve-qemu-kvm-5.2.0.orig/meson.build
|
||||||
|
+++ pve-qemu-kvm-5.2.0/meson.build
|
||||||
|
@@ -596,6 +596,10 @@ rbd = not_found
|
||||||
|
if 'CONFIG_RBD' in config_host
|
||||||
|
rbd = declare_dependency(link_args: config_host['RBD_LIBS'].split())
|
||||||
|
endif
|
||||||
|
+vitastor = not_found
|
||||||
|
+if 'CONFIG_VITASTOR' in config_host
|
||||||
|
+ vitastor = declare_dependency(link_args: config_host['VITASTOR_LIBS'].split())
|
||||||
|
+endif
|
||||||
|
glusterfs = not_found
|
||||||
|
if 'CONFIG_GLUSTERFS' in config_host
|
||||||
|
glusterfs = declare_dependency(compile_args: config_host['GLUSTERFS_CFLAGS'].split(),
|
||||||
|
@@ -2151,6 +2155,7 @@ endif
|
||||||
|
# TODO: add back protocol and server version
|
||||||
|
summary_info += {'spice support': config_host.has_key('CONFIG_SPICE')}
|
||||||
|
summary_info += {'rbd support': config_host.has_key('CONFIG_RBD')}
|
||||||
|
+summary_info += {'vitastor support': config_host.has_key('CONFIG_VITASTOR')}
|
||||||
|
summary_info += {'xfsctl support': config_host.has_key('CONFIG_XFS')}
|
||||||
|
summary_info += {'smartcard support': config_host.has_key('CONFIG_SMARTCARD')}
|
||||||
|
summary_info += {'U2F support': u2f.found()}
|
188
patches/pve-qemu-6.1-vitastor.patch
Normal file
188
patches/pve-qemu-6.1-vitastor.patch
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
Index: pve-qemu-kvm-6.1.0/qapi/block-core.json
|
||||||
|
===================================================================
|
||||||
|
--- pve-qemu-kvm-6.1.0.orig/qapi/block-core.json
|
||||||
|
+++ pve-qemu-kvm-6.1.0/qapi/block-core.json
|
||||||
|
@@ -3084,7 +3084,7 @@
|
||||||
|
'preallocate', 'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'rbd',
|
||||||
|
{ 'name': 'replication', 'if': 'defined(CONFIG_REPLICATION)' },
|
||||||
|
'pbs',
|
||||||
|
- 'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] }
|
||||||
|
+ 'ssh', 'throttle', 'vdi', 'vhdx', 'vitastor', 'vmdk', 'vpc', 'vvfat' ] }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @BlockdevOptionsFile:
|
||||||
|
@@ -4020,6 +4020,28 @@
|
||||||
|
'*server': ['InetSocketAddressBase'] } }
|
||||||
|
|
||||||
|
##
|
||||||
|
+# @BlockdevOptionsVitastor:
|
||||||
|
+#
|
||||||
|
+# Driver specific block device options for vitastor
|
||||||
|
+#
|
||||||
|
+# @image: Image name
|
||||||
|
+# @inode: Inode number
|
||||||
|
+# @pool: Pool ID
|
||||||
|
+# @size: Desired image size in bytes
|
||||||
|
+# @config-path: Path to Vitastor configuration
|
||||||
|
+# @etcd-host: etcd connection address(es)
|
||||||
|
+# @etcd-prefix: etcd key/value prefix
|
||||||
|
+##
|
||||||
|
+{ 'struct': 'BlockdevOptionsVitastor',
|
||||||
|
+ 'data': { '*inode': 'uint64',
|
||||||
|
+ '*pool': 'uint64',
|
||||||
|
+ '*size': 'uint64',
|
||||||
|
+ '*image': 'str',
|
||||||
|
+ '*config-path': 'str',
|
||||||
|
+ '*etcd-host': 'str',
|
||||||
|
+ '*etcd-prefix': 'str' } }
|
||||||
|
+
|
||||||
|
+##
|
||||||
|
# @ReplicationMode:
|
||||||
|
#
|
||||||
|
# An enumeration of replication modes.
|
||||||
|
@@ -4392,6 +4414,7 @@
|
||||||
|
'throttle': 'BlockdevOptionsThrottle',
|
||||||
|
'vdi': 'BlockdevOptionsGenericFormat',
|
||||||
|
'vhdx': 'BlockdevOptionsGenericFormat',
|
||||||
|
+ 'vitastor': 'BlockdevOptionsVitastor',
|
||||||
|
'vmdk': 'BlockdevOptionsGenericCOWFormat',
|
||||||
|
'vpc': 'BlockdevOptionsGenericFormat',
|
||||||
|
'vvfat': 'BlockdevOptionsVVFAT'
|
||||||
|
@@ -4782,6 +4805,17 @@
|
||||||
|
'*encrypt' : 'RbdEncryptionCreateOptions' } }
|
||||||
|
|
||||||
|
##
|
||||||
|
+# @BlockdevCreateOptionsVitastor:
|
||||||
|
+#
|
||||||
|
+# Driver specific image creation options for Vitastor.
|
||||||
|
+#
|
||||||
|
+# @size: Size of the virtual disk in bytes
|
||||||
|
+##
|
||||||
|
+{ 'struct': 'BlockdevCreateOptionsVitastor',
|
||||||
|
+ 'data': { 'location': 'BlockdevOptionsVitastor',
|
||||||
|
+ 'size': 'size' } }
|
||||||
|
+
|
||||||
|
+##
|
||||||
|
# @BlockdevVmdkSubformat:
|
||||||
|
#
|
||||||
|
# Subformat options for VMDK images
|
||||||
|
@@ -4977,6 +5011,7 @@
|
||||||
|
'ssh': 'BlockdevCreateOptionsSsh',
|
||||||
|
'vdi': 'BlockdevCreateOptionsVdi',
|
||||||
|
'vhdx': 'BlockdevCreateOptionsVhdx',
|
||||||
|
+ 'vitastor': 'BlockdevCreateOptionsVitastor',
|
||||||
|
'vmdk': 'BlockdevCreateOptionsVmdk',
|
||||||
|
'vpc': 'BlockdevCreateOptionsVpc'
|
||||||
|
} }
|
||||||
|
Index: pve-qemu-kvm-6.1.0/block/meson.build
|
||||||
|
===================================================================
|
||||||
|
--- pve-qemu-kvm-6.1.0.orig/block/meson.build
|
||||||
|
+++ pve-qemu-kvm-6.1.0/block/meson.build
|
||||||
|
@@ -91,6 +91,7 @@ foreach m : [
|
||||||
|
[libnfs, 'nfs', files('nfs.c')],
|
||||||
|
[libssh, 'ssh', files('ssh.c')],
|
||||||
|
[rbd, 'rbd', files('rbd.c')],
|
||||||
|
+ [vitastor, 'vitastor', files('vitastor.c')],
|
||||||
|
]
|
||||||
|
if m[0].found()
|
||||||
|
module_ss = ss.source_set()
|
||||||
|
Index: pve-qemu-kvm-6.1.0/configure
|
||||||
|
===================================================================
|
||||||
|
--- pve-qemu-kvm-6.1.0.orig/configure
|
||||||
|
+++ pve-qemu-kvm-6.1.0/configure
|
||||||
|
@@ -375,6 +375,7 @@ trace_file="trace"
|
||||||
|
spice="$default_feature"
|
||||||
|
spice_protocol="auto"
|
||||||
|
rbd="auto"
|
||||||
|
+vitastor="auto"
|
||||||
|
smartcard="auto"
|
||||||
|
u2f="auto"
|
||||||
|
libusb="auto"
|
||||||
|
@@ -1293,6 +1294,10 @@ for opt do
|
||||||
|
;;
|
||||||
|
--enable-rbd) rbd="enabled"
|
||||||
|
;;
|
||||||
|
+ --disable-vitastor) vitastor="disabled"
|
||||||
|
+ ;;
|
||||||
|
+ --enable-vitastor) vitastor="enabled"
|
||||||
|
+ ;;
|
||||||
|
--disable-xfsctl) xfs="no"
|
||||||
|
;;
|
||||||
|
--enable-xfsctl) xfs="yes"
|
||||||
|
@@ -1921,6 +1926,7 @@ disabled with --disable-FEATURE, default
|
||||||
|
spice spice
|
||||||
|
spice-protocol spice-protocol
|
||||||
|
rbd rados block device (rbd)
|
||||||
|
+ vitastor vitastor block device
|
||||||
|
libiscsi iscsi support
|
||||||
|
libnfs nfs support
|
||||||
|
smartcard smartcard support (libcacard)
|
||||||
|
@@ -5211,7 +5217,7 @@ if test "$skip_meson" = no; then
|
||||||
|
-Dcapstone=$capstone -Dslirp=$slirp -Dfdt=$fdt -Dbrlapi=$brlapi \
|
||||||
|
-Dcurl=$curl -Dglusterfs=$glusterfs -Dbzip2=$bzip2 -Dlibiscsi=$libiscsi \
|
||||||
|
-Dlibnfs=$libnfs -Diconv=$iconv -Dcurses=$curses -Dlibudev=$libudev\
|
||||||
|
- -Drbd=$rbd -Dlzo=$lzo -Dsnappy=$snappy -Dlzfse=$lzfse -Dlibxml2=$libxml2 \
|
||||||
|
+ -Drbd=$rbd -Dvitastor=$vitastor -Dlzo=$lzo -Dsnappy=$snappy -Dlzfse=$lzfse -Dlibxml2=$libxml2 \
|
||||||
|
-Dlibdaxctl=$libdaxctl -Dlibpmem=$libpmem -Dlinux_io_uring=$linux_io_uring \
|
||||||
|
-Dgnutls=$gnutls -Dnettle=$nettle -Dgcrypt=$gcrypt -Dauth_pam=$auth_pam \
|
||||||
|
-Dzstd=$zstd -Dseccomp=$seccomp -Dvirtfs=$virtfs -Dcap_ng=$cap_ng \
|
||||||
|
Index: pve-qemu-kvm-6.1.0/meson.build
|
||||||
|
===================================================================
|
||||||
|
--- pve-qemu-kvm-6.1.0.orig/meson.build
|
||||||
|
+++ pve-qemu-kvm-6.1.0/meson.build
|
||||||
|
@@ -729,6 +729,26 @@ if not get_option('rbd').auto() or have_
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
+vitastor = not_found
|
||||||
|
+if not get_option('vitastor').auto() or have_block
|
||||||
|
+ libvitastor_client = cc.find_library('vitastor_client', has_headers: ['vitastor_c.h'],
|
||||||
|
+ required: get_option('vitastor'), kwargs: static_kwargs)
|
||||||
|
+ if libvitastor_client.found()
|
||||||
|
+ if cc.links('''
|
||||||
|
+ #include <vitastor_c.h>
|
||||||
|
+ int main(void) {
|
||||||
|
+ vitastor_c_create_qemu(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||||
|
+ return 0;
|
||||||
|
+ }''', dependencies: libvitastor_client)
|
||||||
|
+ vitastor = declare_dependency(dependencies: libvitastor_client)
|
||||||
|
+ elif get_option('vitastor').enabled()
|
||||||
|
+ error('could not link libvitastor_client')
|
||||||
|
+ else
|
||||||
|
+ warning('could not link libvitastor_client, disabling')
|
||||||
|
+ endif
|
||||||
|
+ endif
|
||||||
|
+endif
|
||||||
|
+
|
||||||
|
glusterfs = not_found
|
||||||
|
glusterfs_ftruncate_has_stat = false
|
||||||
|
glusterfs_iocb_has_stat = false
|
||||||
|
@@ -1268,6 +1288,7 @@ config_host_data.set('CONFIG_LIBNFS', li
|
||||||
|
config_host_data.set('CONFIG_LINUX_IO_URING', linux_io_uring.found())
|
||||||
|
config_host_data.set('CONFIG_LIBPMEM', libpmem.found())
|
||||||
|
config_host_data.set('CONFIG_RBD', rbd.found())
|
||||||
|
+config_host_data.set('CONFIG_VITASTOR', vitastor.found())
|
||||||
|
config_host_data.set('CONFIG_SDL', sdl.found())
|
||||||
|
config_host_data.set('CONFIG_SDL_IMAGE', sdl_image.found())
|
||||||
|
config_host_data.set('CONFIG_SECCOMP', seccomp.found())
|
||||||
|
@@ -3087,6 +3108,7 @@ summary_info += {'bpf support': libbpf.f
|
||||||
|
# TODO: add back protocol and server version
|
||||||
|
summary_info += {'spice support': config_host.has_key('CONFIG_SPICE')}
|
||||||
|
summary_info += {'rbd support': rbd.found()}
|
||||||
|
+summary_info += {'vitastor support': vitastor.found()}
|
||||||
|
summary_info += {'xfsctl support': config_host.has_key('CONFIG_XFS')}
|
||||||
|
summary_info += {'smartcard support': cacard.found()}
|
||||||
|
summary_info += {'U2F support': u2f.found()}
|
||||||
|
Index: pve-qemu-kvm-6.1.0/meson_options.txt
|
||||||
|
===================================================================
|
||||||
|
--- pve-qemu-kvm-6.1.0.orig/meson_options.txt
|
||||||
|
+++ pve-qemu-kvm-6.1.0/meson_options.txt
|
||||||
|
@@ -102,6 +102,8 @@ option('lzo', type : 'feature', value :
|
||||||
|
description: 'lzo compression support')
|
||||||
|
option('rbd', type : 'feature', value : 'auto',
|
||||||
|
description: 'Ceph block device driver')
|
||||||
|
+option('vitastor', type : 'feature', value : 'auto',
|
||||||
|
+ description: 'Vitastor block device driver')
|
||||||
|
option('gtk', type : 'feature', value : 'auto',
|
||||||
|
description: 'GTK+ user interface')
|
||||||
|
option('sdl', type : 'feature', value : 'auto',
|
15
patches/qemu-make-patches.sh
Executable file
15
patches/qemu-make-patches.sh
Executable file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# QEMU patches don't include the `block/vitastor.c` file to not duplicate it in sources
|
||||||
|
# Run this script to append its creation to all QEMU patches
|
||||||
|
|
||||||
|
DIR=$(dirname $0)
|
||||||
|
for i in "$DIR"/qemu-*-vitastor.patch "$DIR"/pve-qemu-*-vitastor.patch; do
|
||||||
|
if ! grep -qP '^\+\+\+ .*block/vitastor\.c' $i; then
|
||||||
|
echo 'Index: a/block/vitastor.c' >> $i
|
||||||
|
echo '===================================================================' >> $i
|
||||||
|
echo '--- /dev/null' >> $i
|
||||||
|
echo '+++ a/block/vitastor.c' >> $i
|
||||||
|
echo '@@ -0,0 +1,'$(wc -l "$DIR"/../src/qemu_driver.c)' @@' >> $i
|
||||||
|
cat "$DIR"/../src/qemu_driver.c | sed 's/^/+/' >> $i
|
||||||
|
fi
|
||||||
|
done
|
@@ -25,4 +25,4 @@ rm fio
|
|||||||
mv fio-copy fio
|
mv fio-copy fio
|
||||||
FIO=`rpm -qi fio | perl -e 'while(<>) { /^Epoch[\s:]+(\S+)/ && print "$1:"; /^Version[\s:]+(\S+)/ && print $1; /^Release[\s:]+(\S+)/ && print "-$1"; }'`
|
FIO=`rpm -qi fio | perl -e 'while(<>) { /^Epoch[\s:]+(\S+)/ && print "$1:"; /^Version[\s:]+(\S+)/ && print $1; /^Release[\s:]+(\S+)/ && print "-$1"; }'`
|
||||||
perl -i -pe 's/(Requires:\s*fio)([^\n]+)?/$1 = '$FIO'/' $VITASTOR/rpm/vitastor-el$EL.spec
|
perl -i -pe 's/(Requires:\s*fio)([^\n]+)?/$1 = '$FIO'/' $VITASTOR/rpm/vitastor-el$EL.spec
|
||||||
tar --transform 's#^#vitastor-0.6.9/#' --exclude 'rpm/*.rpm' -czf $VITASTOR/../vitastor-0.6.9$(rpm --eval '%dist').tar.gz *
|
tar --transform 's#^#vitastor-0.6.11/#' --exclude 'rpm/*.rpm' -czf $VITASTOR/../vitastor-0.6.11$(rpm --eval '%dist').tar.gz *
|
||||||
|
@@ -34,7 +34,7 @@ ADD . /root/vitastor
|
|||||||
RUN set -e; \
|
RUN set -e; \
|
||||||
cd /root/vitastor/rpm; \
|
cd /root/vitastor/rpm; \
|
||||||
sh build-tarball.sh; \
|
sh build-tarball.sh; \
|
||||||
cp /root/vitastor-0.6.9.el7.tar.gz ~/rpmbuild/SOURCES; \
|
cp /root/vitastor-0.6.11.el7.tar.gz ~/rpmbuild/SOURCES; \
|
||||||
cp vitastor-el7.spec ~/rpmbuild/SPECS/vitastor.spec; \
|
cp vitastor-el7.spec ~/rpmbuild/SPECS/vitastor.spec; \
|
||||||
cd ~/rpmbuild/SPECS/; \
|
cd ~/rpmbuild/SPECS/; \
|
||||||
rpmbuild -ba vitastor.spec; \
|
rpmbuild -ba vitastor.spec; \
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
Name: vitastor
|
Name: vitastor
|
||||||
Version: 0.6.9
|
Version: 0.6.11
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Vitastor, a fast software-defined clustered block storage
|
Summary: Vitastor, a fast software-defined clustered block storage
|
||||||
|
|
||||||
License: Vitastor Network Public License 1.1
|
License: Vitastor Network Public License 1.1
|
||||||
URL: https://vitastor.io/
|
URL: https://vitastor.io/
|
||||||
Source0: vitastor-0.6.9.el7.tar.gz
|
Source0: vitastor-0.6.11.el7.tar.gz
|
||||||
|
|
||||||
BuildRequires: liburing-devel >= 0.6
|
BuildRequires: liburing-devel >= 0.6
|
||||||
BuildRequires: gperftools-devel
|
BuildRequires: gperftools-devel
|
||||||
|
@@ -33,7 +33,7 @@ ADD . /root/vitastor
|
|||||||
RUN set -e; \
|
RUN set -e; \
|
||||||
cd /root/vitastor/rpm; \
|
cd /root/vitastor/rpm; \
|
||||||
sh build-tarball.sh; \
|
sh build-tarball.sh; \
|
||||||
cp /root/vitastor-0.6.9.el8.tar.gz ~/rpmbuild/SOURCES; \
|
cp /root/vitastor-0.6.11.el8.tar.gz ~/rpmbuild/SOURCES; \
|
||||||
cp vitastor-el8.spec ~/rpmbuild/SPECS/vitastor.spec; \
|
cp vitastor-el8.spec ~/rpmbuild/SPECS/vitastor.spec; \
|
||||||
cd ~/rpmbuild/SPECS/; \
|
cd ~/rpmbuild/SPECS/; \
|
||||||
rpmbuild -ba vitastor.spec; \
|
rpmbuild -ba vitastor.spec; \
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
Name: vitastor
|
Name: vitastor
|
||||||
Version: 0.6.9
|
Version: 0.6.11
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Vitastor, a fast software-defined clustered block storage
|
Summary: Vitastor, a fast software-defined clustered block storage
|
||||||
|
|
||||||
License: Vitastor Network Public License 1.1
|
License: Vitastor Network Public License 1.1
|
||||||
URL: https://vitastor.io/
|
URL: https://vitastor.io/
|
||||||
Source0: vitastor-0.6.9.el8.tar.gz
|
Source0: vitastor-0.6.11.el8.tar.gz
|
||||||
|
|
||||||
BuildRequires: liburing-devel >= 0.6
|
BuildRequires: liburing-devel >= 0.6
|
||||||
BuildRequires: gperftools-devel
|
BuildRequires: gperftools-devel
|
||||||
|
@@ -15,7 +15,7 @@ if("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/local/?$")
|
|||||||
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
|
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_definitions(-DVERSION="0.6.9")
|
add_definitions(-DVERSION="0.6.11")
|
||||||
add_definitions(-Wall -Wno-sign-compare -Wno-comment -Wno-parentheses -Wno-pointer-arith -fdiagnostics-color=always -I ${CMAKE_SOURCE_DIR}/src)
|
add_definitions(-Wall -Wno-sign-compare -Wno-comment -Wno-parentheses -Wno-pointer-arith -fdiagnostics-color=always -I ${CMAKE_SOURCE_DIR}/src)
|
||||||
if (${WITH_ASAN})
|
if (${WITH_ASAN})
|
||||||
add_definitions(-fsanitize=address -fno-omit-frame-pointer)
|
add_definitions(-fsanitize=address -fno-omit-frame-pointer)
|
||||||
@@ -88,8 +88,8 @@ if (IBVERBS_LIBRARIES)
|
|||||||
set(MSGR_RDMA "msgr_rdma.cpp")
|
set(MSGR_RDMA "msgr_rdma.cpp")
|
||||||
endif (IBVERBS_LIBRARIES)
|
endif (IBVERBS_LIBRARIES)
|
||||||
add_library(vitastor_common STATIC
|
add_library(vitastor_common STATIC
|
||||||
epoll_manager.cpp etcd_state_client.cpp
|
epoll_manager.cpp etcd_state_client.cpp messenger.cpp addr_util.cpp
|
||||||
messenger.cpp msgr_stop.cpp msgr_op.cpp msgr_send.cpp msgr_receive.cpp ringloop.cpp ../json11/json11.cpp
|
msgr_stop.cpp msgr_op.cpp msgr_send.cpp msgr_receive.cpp ringloop.cpp ../json11/json11.cpp
|
||||||
http_client.cpp osd_ops.cpp pg_states.cpp timerfd_manager.cpp base64.cpp ${MSGR_RDMA}
|
http_client.cpp osd_ops.cpp pg_states.cpp timerfd_manager.cpp base64.cpp ${MSGR_RDMA}
|
||||||
)
|
)
|
||||||
target_compile_options(vitastor_common PUBLIC -fPIC)
|
target_compile_options(vitastor_common PUBLIC -fPIC)
|
||||||
@@ -112,6 +112,7 @@ if (${WITH_FIO})
|
|||||||
add_library(fio_vitastor_sec SHARED
|
add_library(fio_vitastor_sec SHARED
|
||||||
fio_sec_osd.cpp
|
fio_sec_osd.cpp
|
||||||
rw_blocking.cpp
|
rw_blocking.cpp
|
||||||
|
addr_util.cpp
|
||||||
)
|
)
|
||||||
target_link_libraries(fio_vitastor_sec
|
target_link_libraries(fio_vitastor_sec
|
||||||
tcmalloc_minimal
|
tcmalloc_minimal
|
||||||
@@ -153,7 +154,7 @@ target_link_libraries(vitastor-nbd
|
|||||||
|
|
||||||
# vitastor-cli
|
# vitastor-cli
|
||||||
add_executable(vitastor-cli
|
add_executable(vitastor-cli
|
||||||
cli.cpp cli_alloc_osd.cpp cli_simple_offsets.cpp
|
cli.cpp cli_alloc_osd.cpp cli_simple_offsets.cpp cli_df.cpp
|
||||||
cli_ls.cpp cli_create.cpp cli_modify.cpp cli_flatten.cpp cli_merge.cpp cli_rm.cpp cli_snap_rm.cpp
|
cli_ls.cpp cli_create.cpp cli_modify.cpp cli_flatten.cpp cli_merge.cpp cli_rm.cpp cli_snap_rm.cpp
|
||||||
)
|
)
|
||||||
target_link_libraries(vitastor-cli
|
target_link_libraries(vitastor-cli
|
||||||
@@ -189,11 +190,11 @@ endif (${WITH_QEMU})
|
|||||||
### Test stubs
|
### Test stubs
|
||||||
|
|
||||||
# stub_osd, stub_bench, osd_test
|
# stub_osd, stub_bench, osd_test
|
||||||
add_executable(stub_osd stub_osd.cpp rw_blocking.cpp)
|
add_executable(stub_osd stub_osd.cpp rw_blocking.cpp addr_util.cpp)
|
||||||
target_link_libraries(stub_osd tcmalloc_minimal)
|
target_link_libraries(stub_osd tcmalloc_minimal)
|
||||||
add_executable(stub_bench stub_bench.cpp rw_blocking.cpp)
|
add_executable(stub_bench stub_bench.cpp rw_blocking.cpp addr_util.cpp)
|
||||||
target_link_libraries(stub_bench tcmalloc_minimal)
|
target_link_libraries(stub_bench tcmalloc_minimal)
|
||||||
add_executable(osd_test osd_test.cpp rw_blocking.cpp)
|
add_executable(osd_test osd_test.cpp rw_blocking.cpp addr_util.cpp)
|
||||||
target_link_libraries(osd_test tcmalloc_minimal)
|
target_link_libraries(osd_test tcmalloc_minimal)
|
||||||
|
|
||||||
# osd_rmw_test
|
# osd_rmw_test
|
||||||
|
60
src/addr_util.cpp
Normal file
60
src/addr_util.cpp
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "addr_util.h"
|
||||||
|
|
||||||
|
bool string_to_addr(std::string str, bool parse_port, int default_port, struct sockaddr *addr)
|
||||||
|
{
|
||||||
|
if (parse_port)
|
||||||
|
{
|
||||||
|
int p = str.rfind(':');
|
||||||
|
if (p != std::string::npos && !(str.length() > 0 && str[p-1] == ']')) // "[ipv6]" which contains ':'
|
||||||
|
{
|
||||||
|
char null_byte = 0;
|
||||||
|
int n = sscanf(str.c_str()+p+1, "%d%c", &default_port, &null_byte);
|
||||||
|
if (n != 1 || default_port >= 0x10000)
|
||||||
|
return false;
|
||||||
|
str = str.substr(0, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (inet_pton(AF_INET, str.c_str(), &((struct sockaddr_in*)addr)->sin_addr) == 1)
|
||||||
|
{
|
||||||
|
addr->sa_family = AF_INET;
|
||||||
|
((struct sockaddr_in*)addr)->sin_port = htons(default_port);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (str.length() >= 2 && str[0] == '[' && str[str.length()-1] == ']')
|
||||||
|
str = str.substr(1, str.length()-2);
|
||||||
|
if (inet_pton(AF_INET6, str.c_str(), &((struct sockaddr_in6*)addr)->sin6_addr) == 1)
|
||||||
|
{
|
||||||
|
addr->sa_family = AF_INET6;
|
||||||
|
((struct sockaddr_in6*)addr)->sin6_port = htons(default_port);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string addr_to_string(const sockaddr &addr)
|
||||||
|
{
|
||||||
|
char peer_str[256];
|
||||||
|
bool ok = false;
|
||||||
|
int port;
|
||||||
|
if (addr.sa_family == AF_INET)
|
||||||
|
{
|
||||||
|
ok = !!inet_ntop(AF_INET, &((sockaddr_in*)&addr)->sin_addr, peer_str, 256);
|
||||||
|
port = ntohs(((sockaddr_in*)&addr)->sin_port);
|
||||||
|
}
|
||||||
|
else if (addr.sa_family == AF_INET6)
|
||||||
|
{
|
||||||
|
ok = !!inet_ntop(AF_INET6, &((sockaddr_in6*)&addr)->sin6_addr, peer_str, 256);
|
||||||
|
port = ntohs(((sockaddr_in6*)&addr)->sin6_port);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw std::runtime_error("Unknown address family "+std::to_string(addr.sa_family));
|
||||||
|
if (!ok)
|
||||||
|
throw std::runtime_error(std::string("inet_ntop: ") + strerror(errno));
|
||||||
|
return std::string(peer_str)+":"+std::to_string(port);
|
||||||
|
}
|
7
src/addr_util.h
Normal file
7
src/addr_util.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
bool string_to_addr(std::string str, bool parse_port, int default_port, struct sockaddr *addr);
|
||||||
|
std::string addr_to_string(const sockaddr &addr);
|
@@ -547,8 +547,9 @@ resume_1:
|
|||||||
clean_disk_entry *new_entry = (clean_disk_entry*)(meta_new.buf + meta_new.pos*bs->clean_entry_size);
|
clean_disk_entry *new_entry = (clean_disk_entry*)(meta_new.buf + meta_new.pos*bs->clean_entry_size);
|
||||||
if (new_entry->oid.inode != 0 && new_entry->oid != cur.oid)
|
if (new_entry->oid.inode != 0 && new_entry->oid != cur.oid)
|
||||||
{
|
{
|
||||||
printf("Fatal error (metadata corruption or bug): tried to delete metadata entry %lu (%lx:%lx) while deleting %lx:%lx\n",
|
printf("Fatal error (metadata corruption or bug): tried to delete metadata entry %lu (%lx:%lx v%lu) while deleting %lx:%lx\n",
|
||||||
clean_loc >> bs->block_order, new_entry->oid.inode, new_entry->oid.stripe, cur.oid.inode, cur.oid.stripe);
|
clean_loc >> bs->block_order, new_entry->oid.inode, new_entry->oid.stripe,
|
||||||
|
new_entry->version, cur.oid.inode, cur.oid.stripe);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
// zero out new metadata entry
|
// zero out new metadata entry
|
||||||
@@ -559,8 +560,9 @@ resume_1:
|
|||||||
clean_disk_entry *new_entry = (clean_disk_entry*)(meta_new.buf + meta_new.pos*bs->clean_entry_size);
|
clean_disk_entry *new_entry = (clean_disk_entry*)(meta_new.buf + meta_new.pos*bs->clean_entry_size);
|
||||||
if (new_entry->oid.inode != 0 && new_entry->oid != cur.oid)
|
if (new_entry->oid.inode != 0 && new_entry->oid != cur.oid)
|
||||||
{
|
{
|
||||||
printf("Fatal error (metadata corruption or bug): tried to overwrite non-zero metadata entry %lu (%lx:%lx) with %lx:%lx\n",
|
printf("Fatal error (metadata corruption or bug): tried to overwrite non-zero metadata entry %lu (%lx:%lx v%lu) with %lx:%lx v%lu\n",
|
||||||
clean_loc >> bs->block_order, new_entry->oid.inode, new_entry->oid.stripe, cur.oid.inode, cur.oid.stripe);
|
clean_loc >> bs->block_order, new_entry->oid.inode, new_entry->oid.stripe, new_entry->version,
|
||||||
|
cur.oid.inode, cur.oid.stripe, cur.version);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
new_entry->oid = cur.oid;
|
new_entry->oid = cur.oid;
|
||||||
|
@@ -235,6 +235,12 @@ void blockstore_impl_t::loop()
|
|||||||
{
|
{
|
||||||
throw std::runtime_error(std::string("io_uring_submit: ") + strerror(-ret));
|
throw std::runtime_error(std::string("io_uring_submit: ") + strerror(-ret));
|
||||||
}
|
}
|
||||||
|
for (auto s: journal.submitting_sectors)
|
||||||
|
{
|
||||||
|
// Mark journal sector writes as submitted
|
||||||
|
journal.sector_info[s].submit_id = 0;
|
||||||
|
}
|
||||||
|
journal.submitting_sectors.clear();
|
||||||
if ((initial_ring_space - ringloop->space_left()) > 0)
|
if ((initial_ring_space - ringloop->space_left()) > 0)
|
||||||
{
|
{
|
||||||
live = true;
|
live = true;
|
||||||
|
@@ -54,6 +54,14 @@
|
|||||||
#define IS_BIG_WRITE(st) (((st) & 0x0F) == BS_ST_BIG_WRITE)
|
#define IS_BIG_WRITE(st) (((st) & 0x0F) == BS_ST_BIG_WRITE)
|
||||||
#define IS_DELETE(st) (((st) & 0x0F) == BS_ST_DELETE)
|
#define IS_DELETE(st) (((st) & 0x0F) == BS_ST_DELETE)
|
||||||
|
|
||||||
|
#define BS_SUBMIT_CHECK_SQES(n) \
|
||||||
|
if (ringloop->space_left() < (n))\
|
||||||
|
{\
|
||||||
|
/* Pause until there are more requests available */\
|
||||||
|
PRIV(op)->wait_for = WAIT_SQE;\
|
||||||
|
return 0;\
|
||||||
|
}
|
||||||
|
|
||||||
#define BS_SUBMIT_GET_SQE(sqe, data) \
|
#define BS_SUBMIT_GET_SQE(sqe, data) \
|
||||||
BS_SUBMIT_GET_ONLY_SQE(sqe); \
|
BS_SUBMIT_GET_ONLY_SQE(sqe); \
|
||||||
struct ring_data_t *data = ((ring_data_t*)sqe->user_data)
|
struct ring_data_t *data = ((ring_data_t*)sqe->user_data)
|
||||||
@@ -170,7 +178,7 @@ struct blockstore_op_private_t
|
|||||||
std::vector<fulfill_read_t> read_vec;
|
std::vector<fulfill_read_t> read_vec;
|
||||||
|
|
||||||
// Sync, write
|
// Sync, write
|
||||||
uint64_t min_flushed_journal_sector, max_flushed_journal_sector;
|
int min_flushed_journal_sector, max_flushed_journal_sector;
|
||||||
|
|
||||||
// Write
|
// Write
|
||||||
struct iovec iov_zerofill[3];
|
struct iovec iov_zerofill[3];
|
||||||
@@ -251,6 +259,7 @@ class blockstore_impl_t
|
|||||||
int data_fd;
|
int data_fd;
|
||||||
uint64_t meta_size, meta_area, meta_len;
|
uint64_t meta_size, meta_area, meta_len;
|
||||||
uint64_t data_size, data_len;
|
uint64_t data_size, data_len;
|
||||||
|
uint64_t data_device_sect, meta_device_sect, journal_device_sect;
|
||||||
|
|
||||||
void *metadata_buffer = NULL;
|
void *metadata_buffer = NULL;
|
||||||
|
|
||||||
@@ -282,6 +291,10 @@ class blockstore_impl_t
|
|||||||
void open_journal();
|
void open_journal();
|
||||||
uint8_t* get_clean_entry_bitmap(uint64_t block_loc, int offset);
|
uint8_t* get_clean_entry_bitmap(uint64_t block_loc, int offset);
|
||||||
|
|
||||||
|
// Journaling
|
||||||
|
void prepare_journal_sector_write(int sector, blockstore_op_t *op);
|
||||||
|
void handle_journal_write(ring_data_t *data, uint64_t flush_id);
|
||||||
|
|
||||||
// Asynchronous init
|
// Asynchronous init
|
||||||
int initialized;
|
int initialized;
|
||||||
int metadata_buf_size;
|
int metadata_buf_size;
|
||||||
@@ -309,21 +322,18 @@ class blockstore_impl_t
|
|||||||
|
|
||||||
// Sync
|
// Sync
|
||||||
int continue_sync(blockstore_op_t *op, bool queue_has_in_progress_sync);
|
int continue_sync(blockstore_op_t *op, bool queue_has_in_progress_sync);
|
||||||
void handle_sync_event(ring_data_t *data, blockstore_op_t *op);
|
|
||||||
void ack_sync(blockstore_op_t *op);
|
void ack_sync(blockstore_op_t *op);
|
||||||
|
|
||||||
// Stabilize
|
// Stabilize
|
||||||
int dequeue_stable(blockstore_op_t *op);
|
int dequeue_stable(blockstore_op_t *op);
|
||||||
int continue_stable(blockstore_op_t *op);
|
int continue_stable(blockstore_op_t *op);
|
||||||
void mark_stable(const obj_ver_id & ov, bool forget_dirty = false);
|
void mark_stable(const obj_ver_id & ov, bool forget_dirty = false);
|
||||||
void handle_stable_event(ring_data_t *data, blockstore_op_t *op);
|
|
||||||
void stabilize_object(object_id oid, uint64_t max_ver);
|
void stabilize_object(object_id oid, uint64_t max_ver);
|
||||||
|
|
||||||
// Rollback
|
// Rollback
|
||||||
int dequeue_rollback(blockstore_op_t *op);
|
int dequeue_rollback(blockstore_op_t *op);
|
||||||
int continue_rollback(blockstore_op_t *op);
|
int continue_rollback(blockstore_op_t *op);
|
||||||
void mark_rolled_back(const obj_ver_id & ov);
|
void mark_rolled_back(const obj_ver_id & ov);
|
||||||
void handle_rollback_event(ring_data_t *data, blockstore_op_t *op);
|
|
||||||
void erase_dirty(blockstore_dirty_db_t::iterator dirty_start, blockstore_dirty_db_t::iterator dirty_end, uint64_t clean_loc);
|
void erase_dirty(blockstore_dirty_db_t::iterator dirty_start, blockstore_dirty_db_t::iterator dirty_end, uint64_t clean_loc);
|
||||||
|
|
||||||
// List
|
// List
|
||||||
|
@@ -153,22 +153,73 @@ journal_entry* prefill_single_journal_entry(journal_t & journal, uint16_t type,
|
|||||||
return je;
|
return je;
|
||||||
}
|
}
|
||||||
|
|
||||||
void prepare_journal_sector_write(journal_t & journal, int cur_sector, io_uring_sqe *sqe, std::function<void(ring_data_t*)> cb)
|
void blockstore_impl_t::prepare_journal_sector_write(int cur_sector, blockstore_op_t *op)
|
||||||
{
|
{
|
||||||
|
// Don't submit the same sector twice in the same batch
|
||||||
|
if (!journal.sector_info[cur_sector].submit_id)
|
||||||
|
{
|
||||||
|
io_uring_sqe *sqe = get_sqe();
|
||||||
|
// Caller must ensure availability of an SQE
|
||||||
|
assert(sqe != NULL);
|
||||||
|
ring_data_t *data = ((ring_data_t*)sqe->user_data);
|
||||||
|
journal.sector_info[cur_sector].written = true;
|
||||||
|
journal.sector_info[cur_sector].submit_id = ++journal.submit_id;
|
||||||
|
journal.submitting_sectors.push_back(cur_sector);
|
||||||
|
journal.sector_info[cur_sector].flush_count++;
|
||||||
|
data->iov = (struct iovec){
|
||||||
|
(journal.inmemory
|
||||||
|
? journal.buffer + journal.sector_info[cur_sector].offset
|
||||||
|
: journal.sector_buf + journal.block_size*cur_sector),
|
||||||
|
journal.block_size
|
||||||
|
};
|
||||||
|
data->callback = [this, flush_id = journal.submit_id](ring_data_t *data) { handle_journal_write(data, flush_id); };
|
||||||
|
my_uring_prep_writev(
|
||||||
|
sqe, journal.fd, &data->iov, 1, journal.offset + journal.sector_info[cur_sector].offset
|
||||||
|
);
|
||||||
|
}
|
||||||
journal.sector_info[cur_sector].dirty = false;
|
journal.sector_info[cur_sector].dirty = false;
|
||||||
journal.sector_info[cur_sector].written = true;
|
// But always remember that this operation has to wait until this exact journal write is finished
|
||||||
journal.sector_info[cur_sector].flush_count++;
|
journal.flushing_ops.insert((pending_journaling_t){
|
||||||
ring_data_t *data = ((ring_data_t*)sqe->user_data);
|
.flush_id = journal.sector_info[cur_sector].submit_id,
|
||||||
data->iov = (struct iovec){
|
.sector = cur_sector,
|
||||||
(journal.inmemory
|
.op = op,
|
||||||
? journal.buffer + journal.sector_info[cur_sector].offset
|
});
|
||||||
: journal.sector_buf + journal.block_size*cur_sector),
|
auto priv = PRIV(op);
|
||||||
journal.block_size
|
priv->pending_ops++;
|
||||||
};
|
if (!priv->min_flushed_journal_sector)
|
||||||
data->callback = cb;
|
priv->min_flushed_journal_sector = 1+cur_sector;
|
||||||
my_uring_prep_writev(
|
priv->max_flushed_journal_sector = 1+cur_sector;
|
||||||
sqe, journal.fd, &data->iov, 1, journal.offset + journal.sector_info[cur_sector].offset
|
}
|
||||||
);
|
|
||||||
|
void blockstore_impl_t::handle_journal_write(ring_data_t *data, uint64_t flush_id)
|
||||||
|
{
|
||||||
|
live = true;
|
||||||
|
if (data->res != data->iov.iov_len)
|
||||||
|
{
|
||||||
|
// FIXME: our state becomes corrupted after a write error. maybe do something better than just die
|
||||||
|
throw std::runtime_error(
|
||||||
|
"journal write failed ("+std::to_string(data->res)+" != "+std::to_string(data->iov.iov_len)+
|
||||||
|
"). in-memory state is corrupted. AAAAAAAaaaaaaaaa!!!111"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
auto fl_it = journal.flushing_ops.upper_bound((pending_journaling_t){ .flush_id = flush_id });
|
||||||
|
if (fl_it != journal.flushing_ops.end() && fl_it->flush_id == flush_id)
|
||||||
|
{
|
||||||
|
journal.sector_info[fl_it->sector].flush_count--;
|
||||||
|
}
|
||||||
|
while (fl_it != journal.flushing_ops.end() && fl_it->flush_id == flush_id)
|
||||||
|
{
|
||||||
|
auto priv = PRIV(fl_it->op);
|
||||||
|
priv->pending_ops--;
|
||||||
|
assert(priv->pending_ops >= 0);
|
||||||
|
if (priv->pending_ops == 0)
|
||||||
|
{
|
||||||
|
release_journal_sectors(fl_it->op);
|
||||||
|
priv->op_state++;
|
||||||
|
ringloop->wakeup();
|
||||||
|
}
|
||||||
|
journal.flushing_ops.erase(fl_it++);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
journal_t::~journal_t()
|
journal_t::~journal_t()
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "crc32c.h"
|
#include "crc32c.h"
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#define MIN_JOURNAL_SIZE 4*1024*1024
|
#define MIN_JOURNAL_SIZE 4*1024*1024
|
||||||
#define JOURNAL_MAGIC 0x4A33
|
#define JOURNAL_MAGIC 0x4A33
|
||||||
@@ -145,8 +146,21 @@ struct journal_sector_info_t
|
|||||||
uint64_t flush_count;
|
uint64_t flush_count;
|
||||||
bool written;
|
bool written;
|
||||||
bool dirty;
|
bool dirty;
|
||||||
|
uint64_t submit_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct pending_journaling_t
|
||||||
|
{
|
||||||
|
uint64_t flush_id;
|
||||||
|
int sector;
|
||||||
|
blockstore_op_t *op;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool operator < (const pending_journaling_t & a, const pending_journaling_t & b)
|
||||||
|
{
|
||||||
|
return a.flush_id < b.flush_id || a.flush_id == b.flush_id && a.op < b.op;
|
||||||
|
}
|
||||||
|
|
||||||
struct journal_t
|
struct journal_t
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
@@ -172,6 +186,9 @@ struct journal_t
|
|||||||
bool no_same_sector_overwrites = false;
|
bool no_same_sector_overwrites = false;
|
||||||
int cur_sector = 0;
|
int cur_sector = 0;
|
||||||
int in_sector_pos = 0;
|
int in_sector_pos = 0;
|
||||||
|
std::vector<int> submitting_sectors;
|
||||||
|
std::set<pending_journaling_t> flushing_ops;
|
||||||
|
uint64_t submit_id = 0;
|
||||||
|
|
||||||
// Used sector map
|
// Used sector map
|
||||||
// May use ~ 80 MB per 1 GB of used journal space in the worst case
|
// May use ~ 80 MB per 1 GB of used journal space in the worst case
|
||||||
@@ -200,5 +217,3 @@ struct blockstore_journal_check_t
|
|||||||
};
|
};
|
||||||
|
|
||||||
journal_entry* prefill_single_journal_entry(journal_t & journal, uint16_t type, uint32_t size);
|
journal_entry* prefill_single_journal_entry(journal_t & journal, uint16_t type, uint32_t size);
|
||||||
|
|
||||||
void prepare_journal_sector_write(journal_t & journal, int sector, io_uring_sqe *sqe, std::function<void(ring_data_t*)> cb);
|
|
||||||
|
@@ -295,9 +295,9 @@ void blockstore_impl_t::calc_lengths()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_size(int fd, uint64_t *size, std::string name)
|
static void check_size(int fd, uint64_t *size, uint64_t *sectsize, std::string name)
|
||||||
{
|
{
|
||||||
int sectsize;
|
int sect;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (fstat(fd, &st) < 0)
|
if (fstat(fd, &st) < 0)
|
||||||
{
|
{
|
||||||
@@ -306,14 +306,21 @@ void check_size(int fd, uint64_t *size, std::string name)
|
|||||||
if (S_ISREG(st.st_mode))
|
if (S_ISREG(st.st_mode))
|
||||||
{
|
{
|
||||||
*size = st.st_size;
|
*size = st.st_size;
|
||||||
|
if (sectsize)
|
||||||
|
{
|
||||||
|
*sectsize = st.st_blksize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (S_ISBLK(st.st_mode))
|
else if (S_ISBLK(st.st_mode))
|
||||||
{
|
{
|
||||||
if (ioctl(fd, BLKSSZGET, §size) < 0 ||
|
if (ioctl(fd, BLKGETSIZE64, size) < 0 ||
|
||||||
ioctl(fd, BLKGETSIZE64, size) < 0 ||
|
ioctl(fd, BLKSSZGET, §) < 0)
|
||||||
sectsize != 512)
|
|
||||||
{
|
{
|
||||||
throw std::runtime_error(name+" sector is not equal to 512 bytes");
|
throw std::runtime_error("failed to get "+name+" size or block size: "+strerror(errno));
|
||||||
|
}
|
||||||
|
if (sectsize)
|
||||||
|
{
|
||||||
|
*sectsize = sect;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -329,7 +336,14 @@ void blockstore_impl_t::open_data()
|
|||||||
{
|
{
|
||||||
throw std::runtime_error("Failed to open data device");
|
throw std::runtime_error("Failed to open data device");
|
||||||
}
|
}
|
||||||
check_size(data_fd, &data_size, "data device");
|
check_size(data_fd, &data_size, &data_device_sect, "data device");
|
||||||
|
if (disk_alignment % data_device_sect)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(
|
||||||
|
"disk_alignment ("+std::to_string(disk_alignment)+
|
||||||
|
") is not a multiple of data device sector size ("+std::to_string(data_device_sect)+")"
|
||||||
|
);
|
||||||
|
}
|
||||||
if (data_offset >= data_size)
|
if (data_offset >= data_size)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("data_offset exceeds device size = "+std::to_string(data_size));
|
throw std::runtime_error("data_offset exceeds device size = "+std::to_string(data_size));
|
||||||
@@ -350,7 +364,7 @@ void blockstore_impl_t::open_meta()
|
|||||||
{
|
{
|
||||||
throw std::runtime_error("Failed to open metadata device");
|
throw std::runtime_error("Failed to open metadata device");
|
||||||
}
|
}
|
||||||
check_size(meta_fd, &meta_size, "metadata device");
|
check_size(meta_fd, &meta_size, &meta_device_sect, "metadata device");
|
||||||
if (meta_offset >= meta_size)
|
if (meta_offset >= meta_size)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("meta_offset exceeds device size = "+std::to_string(meta_size));
|
throw std::runtime_error("meta_offset exceeds device size = "+std::to_string(meta_size));
|
||||||
@@ -363,12 +377,20 @@ void blockstore_impl_t::open_meta()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
meta_fd = data_fd;
|
meta_fd = data_fd;
|
||||||
|
meta_device_sect = data_device_sect;
|
||||||
meta_size = 0;
|
meta_size = 0;
|
||||||
if (meta_offset >= data_size)
|
if (meta_offset >= data_size)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("meta_offset exceeds device size = "+std::to_string(data_size));
|
throw std::runtime_error("meta_offset exceeds device size = "+std::to_string(data_size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (meta_block_size % meta_device_sect)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(
|
||||||
|
"meta_block_size ("+std::to_string(meta_block_size)+
|
||||||
|
") is not a multiple of data device sector size ("+std::to_string(meta_device_sect)+")"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void blockstore_impl_t::open_journal()
|
void blockstore_impl_t::open_journal()
|
||||||
@@ -380,7 +402,7 @@ void blockstore_impl_t::open_journal()
|
|||||||
{
|
{
|
||||||
throw std::runtime_error("Failed to open journal device");
|
throw std::runtime_error("Failed to open journal device");
|
||||||
}
|
}
|
||||||
check_size(journal.fd, &journal.device_size, "journal device");
|
check_size(journal.fd, &journal.device_size, &journal_device_sect, "journal device");
|
||||||
if (!disable_flock && flock(journal.fd, LOCK_EX|LOCK_NB) != 0)
|
if (!disable_flock && flock(journal.fd, LOCK_EX|LOCK_NB) != 0)
|
||||||
{
|
{
|
||||||
throw std::runtime_error(std::string("Failed to lock journal device: ") + strerror(errno));
|
throw std::runtime_error(std::string("Failed to lock journal device: ") + strerror(errno));
|
||||||
@@ -389,6 +411,7 @@ void blockstore_impl_t::open_journal()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
journal.fd = meta_fd;
|
journal.fd = meta_fd;
|
||||||
|
journal_device_sect = meta_device_sect;
|
||||||
journal.device_size = 0;
|
journal.device_size = 0;
|
||||||
if (journal.offset >= data_size)
|
if (journal.offset >= data_size)
|
||||||
{
|
{
|
||||||
@@ -406,4 +429,11 @@ void blockstore_impl_t::open_journal()
|
|||||||
if (!journal.sector_buf)
|
if (!journal.sector_buf)
|
||||||
throw std::bad_alloc();
|
throw std::bad_alloc();
|
||||||
}
|
}
|
||||||
|
if (journal_block_size % journal_device_sect)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(
|
||||||
|
"journal_block_size ("+std::to_string(journal_block_size)+
|
||||||
|
") is not a multiple of journal device sector size ("+std::to_string(journal_device_sect)+")"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -74,24 +74,17 @@ skip_ov:
|
|||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// There is sufficient space. Get SQEs
|
// There is sufficient space. Check SQEs
|
||||||
struct io_uring_sqe *sqe[space_check.sectors_to_write];
|
BS_SUBMIT_CHECK_SQES(space_check.sectors_to_write);
|
||||||
for (i = 0; i < space_check.sectors_to_write; i++)
|
|
||||||
{
|
|
||||||
BS_SUBMIT_GET_SQE_DECL(sqe[i]);
|
|
||||||
}
|
|
||||||
// Prepare and submit journal entries
|
// Prepare and submit journal entries
|
||||||
auto cb = [this, op](ring_data_t *data) { handle_rollback_event(data, op); };
|
int s = 0;
|
||||||
int s = 0, cur_sector = -1;
|
|
||||||
for (i = 0, v = (obj_ver_id*)op->buf; i < op->len; i++, v++)
|
for (i = 0, v = (obj_ver_id*)op->buf; i < op->len; i++, v++)
|
||||||
{
|
{
|
||||||
if (!journal.entry_fits(sizeof(journal_entry_rollback)) &&
|
if (!journal.entry_fits(sizeof(journal_entry_rollback)) &&
|
||||||
journal.sector_info[journal.cur_sector].dirty)
|
journal.sector_info[journal.cur_sector].dirty)
|
||||||
{
|
{
|
||||||
if (cur_sector == -1)
|
prepare_journal_sector_write(journal.cur_sector, op);
|
||||||
PRIV(op)->min_flushed_journal_sector = 1 + journal.cur_sector;
|
s++;
|
||||||
prepare_journal_sector_write(journal, journal.cur_sector, sqe[s++], cb);
|
|
||||||
cur_sector = journal.cur_sector;
|
|
||||||
}
|
}
|
||||||
journal_entry_rollback *je = (journal_entry_rollback*)
|
journal_entry_rollback *je = (journal_entry_rollback*)
|
||||||
prefill_single_journal_entry(journal, JE_ROLLBACK, sizeof(journal_entry_rollback));
|
prefill_single_journal_entry(journal, JE_ROLLBACK, sizeof(journal_entry_rollback));
|
||||||
@@ -100,12 +93,9 @@ skip_ov:
|
|||||||
je->crc32 = je_crc32((journal_entry*)je);
|
je->crc32 = je_crc32((journal_entry*)je);
|
||||||
journal.crc32_last = je->crc32;
|
journal.crc32_last = je->crc32;
|
||||||
}
|
}
|
||||||
prepare_journal_sector_write(journal, journal.cur_sector, sqe[s++], cb);
|
prepare_journal_sector_write(journal.cur_sector, op);
|
||||||
|
s++;
|
||||||
assert(s == space_check.sectors_to_write);
|
assert(s == space_check.sectors_to_write);
|
||||||
if (cur_sector == -1)
|
|
||||||
PRIV(op)->min_flushed_journal_sector = 1 + journal.cur_sector;
|
|
||||||
PRIV(op)->max_flushed_journal_sector = 1 + journal.cur_sector;
|
|
||||||
PRIV(op)->pending_ops = s;
|
|
||||||
PRIV(op)->op_state = 1;
|
PRIV(op)->op_state = 1;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -114,30 +104,23 @@ int blockstore_impl_t::continue_rollback(blockstore_op_t *op)
|
|||||||
{
|
{
|
||||||
if (PRIV(op)->op_state == 2)
|
if (PRIV(op)->op_state == 2)
|
||||||
goto resume_2;
|
goto resume_2;
|
||||||
else if (PRIV(op)->op_state == 3)
|
else if (PRIV(op)->op_state == 4)
|
||||||
goto resume_3;
|
goto resume_4;
|
||||||
else if (PRIV(op)->op_state == 5)
|
|
||||||
goto resume_5;
|
|
||||||
else
|
else
|
||||||
return 1;
|
return 1;
|
||||||
resume_2:
|
resume_2:
|
||||||
// Release used journal sectors
|
|
||||||
release_journal_sectors(op);
|
|
||||||
resume_3:
|
|
||||||
if (!disable_journal_fsync)
|
if (!disable_journal_fsync)
|
||||||
{
|
{
|
||||||
io_uring_sqe *sqe;
|
BS_SUBMIT_GET_SQE(sqe, data);
|
||||||
BS_SUBMIT_GET_SQE_DECL(sqe);
|
|
||||||
ring_data_t *data = ((ring_data_t*)sqe->user_data);
|
|
||||||
my_uring_prep_fsync(sqe, journal.fd, IORING_FSYNC_DATASYNC);
|
my_uring_prep_fsync(sqe, journal.fd, IORING_FSYNC_DATASYNC);
|
||||||
data->iov = { 0 };
|
data->iov = { 0 };
|
||||||
data->callback = [this, op](ring_data_t *data) { handle_rollback_event(data, op); };
|
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
||||||
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 0;
|
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 0;
|
||||||
PRIV(op)->pending_ops = 1;
|
PRIV(op)->pending_ops = 1;
|
||||||
PRIV(op)->op_state = 4;
|
PRIV(op)->op_state = 3;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
resume_5:
|
resume_4:
|
||||||
obj_ver_id* v;
|
obj_ver_id* v;
|
||||||
int i;
|
int i;
|
||||||
for (i = 0, v = (obj_ver_id*)op->buf; i < op->len; i++, v++)
|
for (i = 0, v = (obj_ver_id*)op->buf; i < op->len; i++, v++)
|
||||||
@@ -196,24 +179,6 @@ void blockstore_impl_t::mark_rolled_back(const obj_ver_id & ov)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void blockstore_impl_t::handle_rollback_event(ring_data_t *data, blockstore_op_t *op)
|
|
||||||
{
|
|
||||||
live = true;
|
|
||||||
if (data->res != data->iov.iov_len)
|
|
||||||
{
|
|
||||||
throw std::runtime_error(
|
|
||||||
"write operation failed ("+std::to_string(data->res)+" != "+std::to_string(data->iov.iov_len)+
|
|
||||||
"). in-memory state is corrupted. AAAAAAAaaaaaaaaa!!!111"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
PRIV(op)->pending_ops--;
|
|
||||||
if (PRIV(op)->pending_ops == 0)
|
|
||||||
{
|
|
||||||
PRIV(op)->op_state++;
|
|
||||||
ringloop->wakeup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void blockstore_impl_t::erase_dirty(blockstore_dirty_db_t::iterator dirty_start, blockstore_dirty_db_t::iterator dirty_end, uint64_t clean_loc)
|
void blockstore_impl_t::erase_dirty(blockstore_dirty_db_t::iterator dirty_start, blockstore_dirty_db_t::iterator dirty_end, uint64_t clean_loc)
|
||||||
{
|
{
|
||||||
if (dirty_end == dirty_start)
|
if (dirty_end == dirty_start)
|
||||||
|
@@ -97,25 +97,18 @@ int blockstore_impl_t::dequeue_stable(blockstore_op_t *op)
|
|||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// There is sufficient space. Get SQEs
|
// There is sufficient space. Check SQEs
|
||||||
struct io_uring_sqe *sqe[space_check.sectors_to_write];
|
BS_SUBMIT_CHECK_SQES(space_check.sectors_to_write);
|
||||||
for (i = 0; i < space_check.sectors_to_write; i++)
|
|
||||||
{
|
|
||||||
BS_SUBMIT_GET_SQE_DECL(sqe[i]);
|
|
||||||
}
|
|
||||||
// Prepare and submit journal entries
|
// Prepare and submit journal entries
|
||||||
auto cb = [this, op](ring_data_t *data) { handle_stable_event(data, op); };
|
int s = 0;
|
||||||
int s = 0, cur_sector = -1;
|
|
||||||
for (i = 0, v = (obj_ver_id*)op->buf; i < op->len; i++, v++)
|
for (i = 0, v = (obj_ver_id*)op->buf; i < op->len; i++, v++)
|
||||||
{
|
{
|
||||||
// FIXME: Only stabilize versions that aren't stable yet
|
// FIXME: Only stabilize versions that aren't stable yet
|
||||||
if (!journal.entry_fits(sizeof(journal_entry_stable)) &&
|
if (!journal.entry_fits(sizeof(journal_entry_stable)) &&
|
||||||
journal.sector_info[journal.cur_sector].dirty)
|
journal.sector_info[journal.cur_sector].dirty)
|
||||||
{
|
{
|
||||||
if (cur_sector == -1)
|
prepare_journal_sector_write(journal.cur_sector, op);
|
||||||
PRIV(op)->min_flushed_journal_sector = 1 + journal.cur_sector;
|
s++;
|
||||||
prepare_journal_sector_write(journal, journal.cur_sector, sqe[s++], cb);
|
|
||||||
cur_sector = journal.cur_sector;
|
|
||||||
}
|
}
|
||||||
journal_entry_stable *je = (journal_entry_stable*)
|
journal_entry_stable *je = (journal_entry_stable*)
|
||||||
prefill_single_journal_entry(journal, JE_STABLE, sizeof(journal_entry_stable));
|
prefill_single_journal_entry(journal, JE_STABLE, sizeof(journal_entry_stable));
|
||||||
@@ -124,12 +117,9 @@ int blockstore_impl_t::dequeue_stable(blockstore_op_t *op)
|
|||||||
je->crc32 = je_crc32((journal_entry*)je);
|
je->crc32 = je_crc32((journal_entry*)je);
|
||||||
journal.crc32_last = je->crc32;
|
journal.crc32_last = je->crc32;
|
||||||
}
|
}
|
||||||
prepare_journal_sector_write(journal, journal.cur_sector, sqe[s++], cb);
|
prepare_journal_sector_write(journal.cur_sector, op);
|
||||||
|
s++;
|
||||||
assert(s == space_check.sectors_to_write);
|
assert(s == space_check.sectors_to_write);
|
||||||
if (cur_sector == -1)
|
|
||||||
PRIV(op)->min_flushed_journal_sector = 1 + journal.cur_sector;
|
|
||||||
PRIV(op)->max_flushed_journal_sector = 1 + journal.cur_sector;
|
|
||||||
PRIV(op)->pending_ops = s;
|
|
||||||
PRIV(op)->op_state = 1;
|
PRIV(op)->op_state = 1;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -138,30 +128,23 @@ int blockstore_impl_t::continue_stable(blockstore_op_t *op)
|
|||||||
{
|
{
|
||||||
if (PRIV(op)->op_state == 2)
|
if (PRIV(op)->op_state == 2)
|
||||||
goto resume_2;
|
goto resume_2;
|
||||||
else if (PRIV(op)->op_state == 3)
|
else if (PRIV(op)->op_state == 4)
|
||||||
goto resume_3;
|
goto resume_4;
|
||||||
else if (PRIV(op)->op_state == 5)
|
|
||||||
goto resume_5;
|
|
||||||
else
|
else
|
||||||
return 1;
|
return 1;
|
||||||
resume_2:
|
resume_2:
|
||||||
// Release used journal sectors
|
|
||||||
release_journal_sectors(op);
|
|
||||||
resume_3:
|
|
||||||
if (!disable_journal_fsync)
|
if (!disable_journal_fsync)
|
||||||
{
|
{
|
||||||
io_uring_sqe *sqe;
|
BS_SUBMIT_GET_SQE(sqe, data);
|
||||||
BS_SUBMIT_GET_SQE_DECL(sqe);
|
|
||||||
ring_data_t *data = ((ring_data_t*)sqe->user_data);
|
|
||||||
my_uring_prep_fsync(sqe, journal.fd, IORING_FSYNC_DATASYNC);
|
my_uring_prep_fsync(sqe, journal.fd, IORING_FSYNC_DATASYNC);
|
||||||
data->iov = { 0 };
|
data->iov = { 0 };
|
||||||
data->callback = [this, op](ring_data_t *data) { handle_stable_event(data, op); };
|
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
||||||
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 0;
|
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 0;
|
||||||
PRIV(op)->pending_ops = 1;
|
PRIV(op)->pending_ops = 1;
|
||||||
PRIV(op)->op_state = 4;
|
PRIV(op)->op_state = 3;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
resume_5:
|
resume_4:
|
||||||
// Mark dirty_db entries as stable, acknowledge op completion
|
// Mark dirty_db entries as stable, acknowledge op completion
|
||||||
obj_ver_id* v;
|
obj_ver_id* v;
|
||||||
int i;
|
int i;
|
||||||
@@ -257,21 +240,3 @@ void blockstore_impl_t::mark_stable(const obj_ver_id & v, bool forget_dirty)
|
|||||||
unstable_writes.erase(unstab_it);
|
unstable_writes.erase(unstab_it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void blockstore_impl_t::handle_stable_event(ring_data_t *data, blockstore_op_t *op)
|
|
||||||
{
|
|
||||||
live = true;
|
|
||||||
if (data->res != data->iov.iov_len)
|
|
||||||
{
|
|
||||||
throw std::runtime_error(
|
|
||||||
"write operation failed ("+std::to_string(data->res)+" != "+std::to_string(data->iov.iov_len)+
|
|
||||||
"). in-memory state is corrupted. AAAAAAAaaaaaaaaa!!!111"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
PRIV(op)->pending_ops--;
|
|
||||||
if (PRIV(op)->pending_ops == 0)
|
|
||||||
{
|
|
||||||
PRIV(op)->op_state++;
|
|
||||||
ringloop->wakeup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -44,10 +44,8 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op, bool queue_has_in_prog
|
|||||||
if (journal.sector_info[journal.cur_sector].dirty)
|
if (journal.sector_info[journal.cur_sector].dirty)
|
||||||
{
|
{
|
||||||
// Write out the last journal sector if it happens to be dirty
|
// Write out the last journal sector if it happens to be dirty
|
||||||
BS_SUBMIT_GET_ONLY_SQE(sqe);
|
BS_SUBMIT_CHECK_SQES(1);
|
||||||
prepare_journal_sector_write(journal, journal.cur_sector, sqe, [this, op](ring_data_t *data) { handle_sync_event(data, op); });
|
prepare_journal_sector_write(journal.cur_sector, op);
|
||||||
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 1 + journal.cur_sector;
|
|
||||||
PRIV(op)->pending_ops = 1;
|
|
||||||
PRIV(op)->op_state = SYNC_JOURNAL_WRITE_SENT;
|
PRIV(op)->op_state = SYNC_JOURNAL_WRITE_SENT;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -64,7 +62,7 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op, bool queue_has_in_prog
|
|||||||
BS_SUBMIT_GET_SQE(sqe, data);
|
BS_SUBMIT_GET_SQE(sqe, data);
|
||||||
my_uring_prep_fsync(sqe, data_fd, IORING_FSYNC_DATASYNC);
|
my_uring_prep_fsync(sqe, data_fd, IORING_FSYNC_DATASYNC);
|
||||||
data->iov = { 0 };
|
data->iov = { 0 };
|
||||||
data->callback = [this, op](ring_data_t *data) { handle_sync_event(data, op); };
|
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
||||||
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 0;
|
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 0;
|
||||||
PRIV(op)->pending_ops = 1;
|
PRIV(op)->pending_ops = 1;
|
||||||
PRIV(op)->op_state = SYNC_DATA_SYNC_SENT;
|
PRIV(op)->op_state = SYNC_DATA_SYNC_SENT;
|
||||||
@@ -85,24 +83,18 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op, bool queue_has_in_prog
|
|||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// Get SQEs. Don't bother about merging, submit each journal sector as a separate request
|
// Check SQEs. Don't bother about merging, submit each journal sector as a separate request
|
||||||
struct io_uring_sqe *sqe[space_check.sectors_to_write];
|
BS_SUBMIT_CHECK_SQES(space_check.sectors_to_write);
|
||||||
for (int i = 0; i < space_check.sectors_to_write; i++)
|
|
||||||
{
|
|
||||||
BS_SUBMIT_GET_SQE_DECL(sqe[i]);
|
|
||||||
}
|
|
||||||
// Prepare and submit journal entries
|
// Prepare and submit journal entries
|
||||||
auto it = PRIV(op)->sync_big_writes.begin();
|
auto it = PRIV(op)->sync_big_writes.begin();
|
||||||
int s = 0, cur_sector = -1;
|
int s = 0;
|
||||||
while (it != PRIV(op)->sync_big_writes.end())
|
while (it != PRIV(op)->sync_big_writes.end())
|
||||||
{
|
{
|
||||||
if (!journal.entry_fits(sizeof(journal_entry_big_write) + clean_entry_bitmap_size) &&
|
if (!journal.entry_fits(sizeof(journal_entry_big_write) + clean_entry_bitmap_size) &&
|
||||||
journal.sector_info[journal.cur_sector].dirty)
|
journal.sector_info[journal.cur_sector].dirty)
|
||||||
{
|
{
|
||||||
if (cur_sector == -1)
|
prepare_journal_sector_write(journal.cur_sector, op);
|
||||||
PRIV(op)->min_flushed_journal_sector = 1 + journal.cur_sector;
|
s++;
|
||||||
prepare_journal_sector_write(journal, journal.cur_sector, sqe[s++], [this, op](ring_data_t *data) { handle_sync_event(data, op); });
|
|
||||||
cur_sector = journal.cur_sector;
|
|
||||||
}
|
}
|
||||||
auto & dirty_entry = dirty_db.at(*it);
|
auto & dirty_entry = dirty_db.at(*it);
|
||||||
journal_entry_big_write *je = (journal_entry_big_write*)prefill_single_journal_entry(
|
journal_entry_big_write *je = (journal_entry_big_write*)prefill_single_journal_entry(
|
||||||
@@ -129,12 +121,9 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op, bool queue_has_in_prog
|
|||||||
journal.crc32_last = je->crc32;
|
journal.crc32_last = je->crc32;
|
||||||
it++;
|
it++;
|
||||||
}
|
}
|
||||||
prepare_journal_sector_write(journal, journal.cur_sector, sqe[s++], [this, op](ring_data_t *data) { handle_sync_event(data, op); });
|
prepare_journal_sector_write(journal.cur_sector, op);
|
||||||
|
s++;
|
||||||
assert(s == space_check.sectors_to_write);
|
assert(s == space_check.sectors_to_write);
|
||||||
if (cur_sector == -1)
|
|
||||||
PRIV(op)->min_flushed_journal_sector = 1 + journal.cur_sector;
|
|
||||||
PRIV(op)->max_flushed_journal_sector = 1 + journal.cur_sector;
|
|
||||||
PRIV(op)->pending_ops = s;
|
|
||||||
PRIV(op)->op_state = SYNC_JOURNAL_WRITE_SENT;
|
PRIV(op)->op_state = SYNC_JOURNAL_WRITE_SENT;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -145,7 +134,7 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op, bool queue_has_in_prog
|
|||||||
BS_SUBMIT_GET_SQE(sqe, data);
|
BS_SUBMIT_GET_SQE(sqe, data);
|
||||||
my_uring_prep_fsync(sqe, journal.fd, IORING_FSYNC_DATASYNC);
|
my_uring_prep_fsync(sqe, journal.fd, IORING_FSYNC_DATASYNC);
|
||||||
data->iov = { 0 };
|
data->iov = { 0 };
|
||||||
data->callback = [this, op](ring_data_t *data) { handle_sync_event(data, op); };
|
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
||||||
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 0;
|
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 0;
|
||||||
PRIV(op)->pending_ops = 1;
|
PRIV(op)->pending_ops = 1;
|
||||||
PRIV(op)->op_state = SYNC_JOURNAL_SYNC_SENT;
|
PRIV(op)->op_state = SYNC_JOURNAL_SYNC_SENT;
|
||||||
@@ -164,42 +153,6 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op, bool queue_has_in_prog
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void blockstore_impl_t::handle_sync_event(ring_data_t *data, blockstore_op_t *op)
|
|
||||||
{
|
|
||||||
live = true;
|
|
||||||
if (data->res != data->iov.iov_len)
|
|
||||||
{
|
|
||||||
throw std::runtime_error(
|
|
||||||
"write operation failed ("+std::to_string(data->res)+" != "+std::to_string(data->iov.iov_len)+
|
|
||||||
"). in-memory state is corrupted. AAAAAAAaaaaaaaaa!!!111"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
PRIV(op)->pending_ops--;
|
|
||||||
if (PRIV(op)->pending_ops == 0)
|
|
||||||
{
|
|
||||||
// Release used journal sectors
|
|
||||||
release_journal_sectors(op);
|
|
||||||
// Handle states
|
|
||||||
if (PRIV(op)->op_state == SYNC_DATA_SYNC_SENT)
|
|
||||||
{
|
|
||||||
PRIV(op)->op_state = SYNC_DATA_SYNC_DONE;
|
|
||||||
}
|
|
||||||
else if (PRIV(op)->op_state == SYNC_JOURNAL_WRITE_SENT)
|
|
||||||
{
|
|
||||||
PRIV(op)->op_state = SYNC_JOURNAL_WRITE_DONE;
|
|
||||||
}
|
|
||||||
else if (PRIV(op)->op_state == SYNC_JOURNAL_SYNC_SENT)
|
|
||||||
{
|
|
||||||
PRIV(op)->op_state = SYNC_DONE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw std::runtime_error("BUG: unexpected sync op state");
|
|
||||||
}
|
|
||||||
ringloop->wakeup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void blockstore_impl_t::ack_sync(blockstore_op_t *op)
|
void blockstore_impl_t::ack_sync(blockstore_op_t *op)
|
||||||
{
|
{
|
||||||
// Handle states
|
// Handle states
|
||||||
|
@@ -268,8 +268,8 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
|||||||
cancel_all_writes(op, dirty_it, -ENOSPC);
|
cancel_all_writes(op, dirty_it, -ENOSPC);
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
write_iodepth++;
|
|
||||||
BS_SUBMIT_GET_SQE(sqe, data);
|
BS_SUBMIT_GET_SQE(sqe, data);
|
||||||
|
write_iodepth++;
|
||||||
dirty_it->second.location = loc << block_order;
|
dirty_it->second.location = loc << block_order;
|
||||||
dirty_it->second.state = (dirty_it->second.state & ~BS_ST_WORKFLOW_MASK) | BS_ST_SUBMITTED;
|
dirty_it->second.state = (dirty_it->second.state & ~BS_ST_WORKFLOW_MASK) | BS_ST_SUBMITTED;
|
||||||
#ifdef BLOCKSTORE_DEBUG
|
#ifdef BLOCKSTORE_DEBUG
|
||||||
@@ -324,29 +324,21 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
|||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
write_iodepth++;
|
// There is sufficient space. Check SQE(s)
|
||||||
// There is sufficient space. Get SQE(s)
|
BS_SUBMIT_CHECK_SQES(
|
||||||
struct io_uring_sqe *sqe1 = NULL;
|
|
||||||
if (immediate_commit != IMMEDIATE_NONE ||
|
|
||||||
!journal.entry_fits(sizeof(journal_entry_small_write) + clean_entry_bitmap_size))
|
|
||||||
{
|
|
||||||
// Write current journal sector only if it's dirty and full, or in the immediate_commit mode
|
// Write current journal sector only if it's dirty and full, or in the immediate_commit mode
|
||||||
BS_SUBMIT_GET_SQE_DECL(sqe1);
|
(immediate_commit != IMMEDIATE_NONE ||
|
||||||
}
|
!journal.entry_fits(sizeof(journal_entry_small_write) + clean_entry_bitmap_size) ? 1 : 0) +
|
||||||
struct io_uring_sqe *sqe2 = NULL;
|
(op->len > 0 ? 1 : 0)
|
||||||
if (op->len > 0)
|
);
|
||||||
{
|
write_iodepth++;
|
||||||
BS_SUBMIT_GET_SQE_DECL(sqe2);
|
|
||||||
}
|
|
||||||
// Got SQEs. Prepare previous journal sector write if required
|
// Got SQEs. Prepare previous journal sector write if required
|
||||||
auto cb = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
auto cb = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
||||||
if (immediate_commit == IMMEDIATE_NONE)
|
if (immediate_commit == IMMEDIATE_NONE)
|
||||||
{
|
{
|
||||||
if (sqe1)
|
if (!journal.entry_fits(sizeof(journal_entry_small_write) + clean_entry_bitmap_size))
|
||||||
{
|
{
|
||||||
prepare_journal_sector_write(journal, journal.cur_sector, sqe1, cb);
|
prepare_journal_sector_write(journal.cur_sector, op);
|
||||||
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 1 + journal.cur_sector;
|
|
||||||
PRIV(op)->pending_ops++;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -380,9 +372,7 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
|||||||
journal.crc32_last = je->crc32;
|
journal.crc32_last = je->crc32;
|
||||||
if (immediate_commit != IMMEDIATE_NONE)
|
if (immediate_commit != IMMEDIATE_NONE)
|
||||||
{
|
{
|
||||||
prepare_journal_sector_write(journal, journal.cur_sector, sqe1, cb);
|
prepare_journal_sector_write(journal.cur_sector, op);
|
||||||
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 1 + journal.cur_sector;
|
|
||||||
PRIV(op)->pending_ops++;
|
|
||||||
}
|
}
|
||||||
if (op->len > 0)
|
if (op->len > 0)
|
||||||
{
|
{
|
||||||
@@ -392,7 +382,7 @@ int blockstore_impl_t::dequeue_write(blockstore_op_t *op)
|
|||||||
// Copy data
|
// Copy data
|
||||||
memcpy(journal.buffer + journal.next_free, op->buf, op->len);
|
memcpy(journal.buffer + journal.next_free, op->buf, op->len);
|
||||||
}
|
}
|
||||||
ring_data_t *data2 = ((ring_data_t*)sqe2->user_data);
|
BS_SUBMIT_GET_SQE(sqe2, data2);
|
||||||
data2->iov = (struct iovec){ op->buf, op->len };
|
data2->iov = (struct iovec){ op->buf, op->len };
|
||||||
data2->callback = cb;
|
data2->callback = cb;
|
||||||
my_uring_prep_writev(
|
my_uring_prep_writev(
|
||||||
@@ -441,13 +431,12 @@ int blockstore_impl_t::continue_write(blockstore_op_t *op)
|
|||||||
resume_2:
|
resume_2:
|
||||||
// Only for the immediate_commit mode: prepare and submit big_write journal entry
|
// Only for the immediate_commit mode: prepare and submit big_write journal entry
|
||||||
{
|
{
|
||||||
|
BS_SUBMIT_CHECK_SQES(1);
|
||||||
auto dirty_it = dirty_db.find((obj_ver_id){
|
auto dirty_it = dirty_db.find((obj_ver_id){
|
||||||
.oid = op->oid,
|
.oid = op->oid,
|
||||||
.version = op->version,
|
.version = op->version,
|
||||||
});
|
});
|
||||||
assert(dirty_it != dirty_db.end());
|
assert(dirty_it != dirty_db.end());
|
||||||
io_uring_sqe *sqe = NULL;
|
|
||||||
BS_SUBMIT_GET_SQE_DECL(sqe);
|
|
||||||
journal_entry_big_write *je = (journal_entry_big_write*)prefill_single_journal_entry(
|
journal_entry_big_write *je = (journal_entry_big_write*)prefill_single_journal_entry(
|
||||||
journal, op->opcode == BS_OP_WRITE_STABLE ? JE_BIG_WRITE_INSTANT : JE_BIG_WRITE,
|
journal, op->opcode == BS_OP_WRITE_STABLE ? JE_BIG_WRITE_INSTANT : JE_BIG_WRITE,
|
||||||
sizeof(journal_entry_big_write) + clean_entry_bitmap_size
|
sizeof(journal_entry_big_write) + clean_entry_bitmap_size
|
||||||
@@ -469,10 +458,7 @@ resume_2:
|
|||||||
memcpy((void*)(je+1), (clean_entry_bitmap_size > sizeof(void*) ? dirty_it->second.bitmap : &dirty_it->second.bitmap), clean_entry_bitmap_size);
|
memcpy((void*)(je+1), (clean_entry_bitmap_size > sizeof(void*) ? dirty_it->second.bitmap : &dirty_it->second.bitmap), clean_entry_bitmap_size);
|
||||||
je->crc32 = je_crc32((journal_entry*)je);
|
je->crc32 = je_crc32((journal_entry*)je);
|
||||||
journal.crc32_last = je->crc32;
|
journal.crc32_last = je->crc32;
|
||||||
prepare_journal_sector_write(journal, journal.cur_sector, sqe,
|
prepare_journal_sector_write(journal.cur_sector, op);
|
||||||
[this, op](ring_data_t *data) { handle_write_event(data, op); });
|
|
||||||
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 1 + journal.cur_sector;
|
|
||||||
PRIV(op)->pending_ops = 1;
|
|
||||||
PRIV(op)->op_state = 3;
|
PRIV(op)->op_state = 3;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -587,6 +573,7 @@ void blockstore_impl_t::handle_write_event(ring_data_t *data, blockstore_op_t *o
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
PRIV(op)->pending_ops--;
|
PRIV(op)->pending_ops--;
|
||||||
|
assert(PRIV(op)->pending_ops >= 0);
|
||||||
if (PRIV(op)->pending_ops == 0)
|
if (PRIV(op)->pending_ops == 0)
|
||||||
{
|
{
|
||||||
release_journal_sectors(op);
|
release_journal_sectors(op);
|
||||||
@@ -604,7 +591,6 @@ void blockstore_impl_t::release_journal_sectors(blockstore_op_t *op)
|
|||||||
uint64_t s = PRIV(op)->min_flushed_journal_sector;
|
uint64_t s = PRIV(op)->min_flushed_journal_sector;
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
journal.sector_info[s-1].flush_count--;
|
|
||||||
if (s != (1+journal.cur_sector) && journal.sector_info[s-1].flush_count == 0)
|
if (s != (1+journal.cur_sector) && journal.sector_info[s-1].flush_count == 0)
|
||||||
{
|
{
|
||||||
// We know for sure that we won't write into this sector anymore
|
// We know for sure that we won't write into this sector anymore
|
||||||
@@ -644,23 +630,19 @@ int blockstore_impl_t::dequeue_del(blockstore_op_t *op)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
write_iodepth++;
|
write_iodepth++;
|
||||||
io_uring_sqe *sqe = NULL;
|
// Write current journal sector only if it's dirty and full, or in the immediate_commit mode
|
||||||
if (immediate_commit != IMMEDIATE_NONE ||
|
BS_SUBMIT_CHECK_SQES(
|
||||||
(journal_block_size - journal.in_sector_pos) < sizeof(journal_entry_del) &&
|
(immediate_commit != IMMEDIATE_NONE ||
|
||||||
journal.sector_info[journal.cur_sector].dirty)
|
(journal_block_size - journal.in_sector_pos) < sizeof(journal_entry_del) &&
|
||||||
{
|
journal.sector_info[journal.cur_sector].dirty) ? 1 : 0
|
||||||
// Write current journal sector only if it's dirty and full, or in the immediate_commit mode
|
);
|
||||||
BS_SUBMIT_GET_SQE_DECL(sqe);
|
|
||||||
}
|
|
||||||
auto cb = [this, op](ring_data_t *data) { handle_write_event(data, op); };
|
|
||||||
// Prepare journal sector write
|
// Prepare journal sector write
|
||||||
if (immediate_commit == IMMEDIATE_NONE)
|
if (immediate_commit == IMMEDIATE_NONE)
|
||||||
{
|
{
|
||||||
if (sqe)
|
if ((journal_block_size - journal.in_sector_pos) < sizeof(journal_entry_del) &&
|
||||||
|
journal.sector_info[journal.cur_sector].dirty)
|
||||||
{
|
{
|
||||||
prepare_journal_sector_write(journal, journal.cur_sector, sqe, cb);
|
prepare_journal_sector_write(journal.cur_sector, op);
|
||||||
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 1 + journal.cur_sector;
|
|
||||||
PRIV(op)->pending_ops++;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -687,9 +669,7 @@ int blockstore_impl_t::dequeue_del(blockstore_op_t *op)
|
|||||||
dirty_it->second.state = BS_ST_DELETE | BS_ST_SUBMITTED;
|
dirty_it->second.state = BS_ST_DELETE | BS_ST_SUBMITTED;
|
||||||
if (immediate_commit != IMMEDIATE_NONE)
|
if (immediate_commit != IMMEDIATE_NONE)
|
||||||
{
|
{
|
||||||
prepare_journal_sector_write(journal, journal.cur_sector, sqe, cb);
|
prepare_journal_sector_write(journal.cur_sector, op);
|
||||||
PRIV(op)->min_flushed_journal_sector = PRIV(op)->max_flushed_journal_sector = 1 + journal.cur_sector;
|
|
||||||
PRIV(op)->pending_ops++;
|
|
||||||
}
|
}
|
||||||
if (!PRIV(op)->pending_ops)
|
if (!PRIV(op)->pending_ops)
|
||||||
{
|
{
|
||||||
|
24
src/cli.cpp
24
src/cli.cpp
@@ -57,6 +57,7 @@ json11::Json::object cli_tool_t::parse_args(int narg, const char *args[])
|
|||||||
const char *opt = args[i]+2;
|
const char *opt = args[i]+2;
|
||||||
cfg[opt] = i == narg-1 || !strcmp(opt, "json") || !strcmp(opt, "wait-list") ||
|
cfg[opt] = i == narg-1 || !strcmp(opt, "json") || !strcmp(opt, "wait-list") ||
|
||||||
!strcmp(opt, "long") || !strcmp(opt, "del") || !strcmp(opt, "no-color") ||
|
!strcmp(opt, "long") || !strcmp(opt, "del") || !strcmp(opt, "no-color") ||
|
||||||
|
!strcmp(opt, "readonly") || !strcmp(opt, "readwrite") ||
|
||||||
!strcmp(opt, "force") || !strcmp(opt, "reverse") ||
|
!strcmp(opt, "force") || !strcmp(opt, "reverse") ||
|
||||||
!strcmp(opt, "writers-stopped") && strcmp("1", args[i+1]) != 0
|
!strcmp(opt, "writers-stopped") && strcmp("1", args[i+1]) != 0
|
||||||
? "1" : args[++i];
|
? "1" : args[++i];
|
||||||
@@ -69,7 +70,7 @@ json11::Json::object cli_tool_t::parse_args(int narg, const char *args[])
|
|||||||
if (!cmd.size())
|
if (!cmd.size())
|
||||||
{
|
{
|
||||||
std::string exe(exe_name);
|
std::string exe(exe_name);
|
||||||
if (exe.substr(exe.size()-11) == "vitastor-rm")
|
if (exe.size() >= 11 && exe.substr(exe.size()-11) == "vitastor-rm")
|
||||||
{
|
{
|
||||||
cmd.push_back("rm-data");
|
cmd.push_back("rm-data");
|
||||||
}
|
}
|
||||||
@@ -85,8 +86,11 @@ void cli_tool_t::help()
|
|||||||
"(c) Vitaliy Filippov, 2019+ (VNPL-1.1)\n"
|
"(c) Vitaliy Filippov, 2019+ (VNPL-1.1)\n"
|
||||||
"\n"
|
"\n"
|
||||||
"USAGE:\n"
|
"USAGE:\n"
|
||||||
"%s ls [-l] [-p POOL] [--sort FIELD] [-r] [-n N] [<name> ...]\n"
|
"%s df\n"
|
||||||
" List images (only specified if <name> passed).\n"
|
" Show pool space statistics\n"
|
||||||
|
"\n"
|
||||||
|
"%s ls [-l] [-p POOL] [--sort FIELD] [-r] [-n N] [<glob> ...]\n"
|
||||||
|
" List images (only matching <glob> patterns if passed).\n"
|
||||||
" -p|--pool POOL Filter images by pool ID or name\n"
|
" -p|--pool POOL Filter images by pool ID or name\n"
|
||||||
" -l|--long Also report allocated size and I/O statistics\n"
|
" -l|--long Also report allocated size and I/O statistics\n"
|
||||||
" --del Also include delete operation statistics\n"
|
" --del Also include delete operation statistics\n"
|
||||||
@@ -103,7 +107,7 @@ void cli_tool_t::help()
|
|||||||
"%s snap-create [-p|--pool <id|name>] <image>@<snapshot>\n"
|
"%s snap-create [-p|--pool <id|name>] <image>@<snapshot>\n"
|
||||||
" Create a snapshot of image <name>. May be used live if only a single writer is active.\n"
|
" Create a snapshot of image <name>. May be used live if only a single writer is active.\n"
|
||||||
"\n"
|
"\n"
|
||||||
"%s modify <name> [--rename <new-name>] [-s|--size <size>] [--readonly | --readwrite] [-f|--force]\n"
|
"%s modify <name> [--rename <new-name>] [--resize <size>] [--readonly | --readwrite] [-f|--force]\n"
|
||||||
" Rename, resize image or change its readonly status. Images with children can't be made read-write.\n"
|
" Rename, resize image or change its readonly status. Images with children can't be made read-write.\n"
|
||||||
" If the new size is smaller than the old size, extra data will be purged.\n"
|
" If the new size is smaller than the old size, extra data will be purged.\n"
|
||||||
" You should resize file system in the image, if present, before shrinking it.\n"
|
" You should resize file system in the image, if present, before shrinking it.\n"
|
||||||
@@ -151,7 +155,8 @@ void cli_tool_t::help()
|
|||||||
" --no-color Disable colored output\n"
|
" --no-color Disable colored output\n"
|
||||||
" --json JSON output\n"
|
" --json JSON output\n"
|
||||||
,
|
,
|
||||||
exe_name, exe_name, exe_name, exe_name, exe_name, exe_name, exe_name, exe_name, exe_name, exe_name, exe_name
|
exe_name, exe_name, exe_name, exe_name, exe_name, exe_name,
|
||||||
|
exe_name, exe_name, exe_name, exe_name, exe_name, exe_name
|
||||||
);
|
);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
@@ -245,6 +250,11 @@ void cli_tool_t::run(json11::Json cfg)
|
|||||||
fprintf(stderr, "command is missing\n");
|
fprintf(stderr, "command is missing\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
else if (cmd[0] == "df")
|
||||||
|
{
|
||||||
|
// Show pool space stats
|
||||||
|
action_cb = start_df(cfg);
|
||||||
|
}
|
||||||
else if (cmd[0] == "ls")
|
else if (cmd[0] == "ls")
|
||||||
{
|
{
|
||||||
// List images
|
// List images
|
||||||
@@ -295,6 +305,10 @@ void cli_tool_t::run(json11::Json cfg)
|
|||||||
fprintf(stderr, "unknown command: %s\n", cmd[0].string_value().c_str());
|
fprintf(stderr, "unknown command: %s\n", cmd[0].string_value().c_str());
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
if (action_cb == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
color = !cfg["no-color"].bool_value();
|
color = !cfg["no-color"].bool_value();
|
||||||
json_output = cfg["json"].bool_value();
|
json_output = cfg["json"].bool_value();
|
||||||
iodepth = cfg["iodepth"].uint64_value();
|
iodepth = cfg["iodepth"].uint64_value();
|
||||||
|
12
src/cli.h
12
src/cli.h
@@ -50,6 +50,7 @@ public:
|
|||||||
friend struct snap_flattener_t;
|
friend struct snap_flattener_t;
|
||||||
friend struct snap_remover_t;
|
friend struct snap_remover_t;
|
||||||
|
|
||||||
|
std::function<bool(void)> start_df(json11::Json);
|
||||||
std::function<bool(void)> start_ls(json11::Json);
|
std::function<bool(void)> start_ls(json11::Json);
|
||||||
std::function<bool(void)> start_create(json11::Json);
|
std::function<bool(void)> start_create(json11::Json);
|
||||||
std::function<bool(void)> start_modify(json11::Json);
|
std::function<bool(void)> start_modify(json11::Json);
|
||||||
@@ -61,5 +62,14 @@ public:
|
|||||||
std::function<bool(void)> simple_offsets(json11::Json cfg);
|
std::function<bool(void)> simple_offsets(json11::Json cfg);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string format_size(uint64_t size);
|
|
||||||
uint64_t parse_size(std::string size_str);
|
uint64_t parse_size(std::string size_str);
|
||||||
|
|
||||||
|
std::string print_table(json11::Json items, json11::Json header, bool use_esc);
|
||||||
|
|
||||||
|
std::string format_size(uint64_t size);
|
||||||
|
|
||||||
|
std::string format_lat(uint64_t lat);
|
||||||
|
|
||||||
|
std::string format_q(double depth);
|
||||||
|
|
||||||
|
bool stupid_glob(const std::string str, const std::string glob);
|
||||||
|
@@ -88,6 +88,31 @@ struct image_creator_t
|
|||||||
goto resume_2;
|
goto resume_2;
|
||||||
else if (state == 3)
|
else if (state == 3)
|
||||||
goto resume_3;
|
goto resume_3;
|
||||||
|
for (auto & ic: parent->cli->st_cli.inode_config)
|
||||||
|
{
|
||||||
|
if (ic.second.name == image_name)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Image %s already exists\n", image_name.c_str());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (ic.second.name == new_parent)
|
||||||
|
{
|
||||||
|
new_parent_id = ic.second.num;
|
||||||
|
if (!new_pool_id)
|
||||||
|
{
|
||||||
|
new_pool_id = INODE_POOL(ic.second.num);
|
||||||
|
}
|
||||||
|
if (!size)
|
||||||
|
{
|
||||||
|
size = ic.second.size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (new_parent != "" && !new_parent_id)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Parent image not found\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
if (!new_pool_id)
|
if (!new_pool_id)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Pool name or ID is missing\n");
|
fprintf(stderr, "Pool name or ID is missing\n");
|
||||||
@@ -98,14 +123,6 @@ struct image_creator_t
|
|||||||
fprintf(stderr, "Image size is missing\n");
|
fprintf(stderr, "Image size is missing\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
for (auto & ic: parent->cli->st_cli.inode_config)
|
|
||||||
{
|
|
||||||
if (ic.second.name == image_name)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Image %s already exists\n", image_name.c_str());
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
etcd_txn(json11::Json::object {
|
etcd_txn(json11::Json::object {
|
||||||
@@ -151,6 +168,11 @@ resume_3:
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (new_parent != "")
|
||||||
|
{
|
||||||
|
fprintf(stderr, "--parent can't be used with snapshots\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
// In addition to next_id, get: size, old_id, old_pool_id, new_parent, cfg_mod_rev, idx_mod_rev
|
// In addition to next_id, get: size, old_id, old_pool_id, new_parent, cfg_mod_rev, idx_mod_rev
|
||||||
@@ -443,6 +465,10 @@ resume_3:
|
|||||||
|
|
||||||
uint64_t parse_size(std::string size_str)
|
uint64_t parse_size(std::string size_str)
|
||||||
{
|
{
|
||||||
|
if (!size_str.length())
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
uint64_t mul = 1;
|
uint64_t mul = 1;
|
||||||
char type_char = tolower(size_str[size_str.length()-1]);
|
char type_char = tolower(size_str[size_str.length()-1]);
|
||||||
if (type_char == 'k' || type_char == 'm' || type_char == 'g' || type_char == 't')
|
if (type_char == 'k' || type_char == 'm' || type_char == 'g' || type_char == 't')
|
||||||
|
229
src/cli_df.cpp
Normal file
229
src/cli_df.cpp
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
// Copyright (c) Vitaliy Filippov, 2019+
|
||||||
|
// License: VNPL-1.1 (see README.md for details)
|
||||||
|
|
||||||
|
#include "cli.h"
|
||||||
|
#include "cluster_client.h"
|
||||||
|
#include "base64.h"
|
||||||
|
|
||||||
|
// List pools with space statistics
|
||||||
|
struct pool_lister_t
|
||||||
|
{
|
||||||
|
cli_tool_t *parent;
|
||||||
|
|
||||||
|
int state = 0;
|
||||||
|
json11::Json space_info;
|
||||||
|
std::map<pool_id_t, json11::Json::object> pool_stats;
|
||||||
|
|
||||||
|
bool is_done()
|
||||||
|
{
|
||||||
|
return state == 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_stats()
|
||||||
|
{
|
||||||
|
if (state == 1)
|
||||||
|
goto resume_1;
|
||||||
|
// Space statistics - pool/stats/<pool>
|
||||||
|
parent->waiting++;
|
||||||
|
parent->cli->st_cli.etcd_txn(json11::Json::object {
|
||||||
|
{ "success", json11::Json::array {
|
||||||
|
json11::Json::object {
|
||||||
|
{ "request_range", json11::Json::object {
|
||||||
|
{ "key", base64_encode(
|
||||||
|
parent->cli->st_cli.etcd_prefix+"/pool/stats/"
|
||||||
|
) },
|
||||||
|
{ "range_end", base64_encode(
|
||||||
|
parent->cli->st_cli.etcd_prefix+"/pool/stats0"
|
||||||
|
) },
|
||||||
|
} },
|
||||||
|
},
|
||||||
|
json11::Json::object {
|
||||||
|
{ "request_range", json11::Json::object {
|
||||||
|
{ "key", base64_encode(
|
||||||
|
parent->cli->st_cli.etcd_prefix+"/osd/stats/"
|
||||||
|
) },
|
||||||
|
{ "range_end", base64_encode(
|
||||||
|
parent->cli->st_cli.etcd_prefix+"/osd/stats0"
|
||||||
|
) },
|
||||||
|
} },
|
||||||
|
},
|
||||||
|
} },
|
||||||
|
}, ETCD_SLOW_TIMEOUT, [this](std::string err, json11::Json res)
|
||||||
|
{
|
||||||
|
parent->waiting--;
|
||||||
|
if (err != "")
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error reading from etcd: %s\n", err.c_str());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
space_info = res;
|
||||||
|
parent->ringloop->wakeup();
|
||||||
|
});
|
||||||
|
state = 1;
|
||||||
|
resume_1:
|
||||||
|
if (parent->waiting > 0)
|
||||||
|
return;
|
||||||
|
std::map<pool_id_t, uint64_t> osd_free;
|
||||||
|
for (auto & kv_item: space_info["responses"][0]["response_range"]["kvs"].array_items())
|
||||||
|
{
|
||||||
|
auto kv = parent->cli->st_cli.parse_etcd_kv(kv_item);
|
||||||
|
// pool ID
|
||||||
|
pool_id_t pool_id;
|
||||||
|
char null_byte = 0;
|
||||||
|
sscanf(kv.key.substr(parent->cli->st_cli.etcd_prefix.length()).c_str(), "/pool/stats/%u%c", &pool_id, &null_byte);
|
||||||
|
if (!pool_id || pool_id >= POOL_ID_MAX || null_byte != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Invalid key in etcd: %s\n", kv.key.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// pool/stats/<N>
|
||||||
|
pool_stats[pool_id] = kv.value.object_items();
|
||||||
|
}
|
||||||
|
for (auto & kv_item: space_info["responses"][1]["response_range"]["kvs"].array_items())
|
||||||
|
{
|
||||||
|
auto kv = parent->cli->st_cli.parse_etcd_kv(kv_item);
|
||||||
|
// osd ID
|
||||||
|
osd_num_t osd_num;
|
||||||
|
char null_byte = 0;
|
||||||
|
sscanf(kv.key.substr(parent->cli->st_cli.etcd_prefix.length()).c_str(), "/osd/stats/%lu%c", &osd_num, &null_byte);
|
||||||
|
if (!osd_num || osd_num >= POOL_ID_MAX || null_byte != 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Invalid key in etcd: %s\n", kv.key.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// osd/stats/<N>::free
|
||||||
|
osd_free[osd_num] = kv.value["free"].uint64_value();
|
||||||
|
}
|
||||||
|
// Calculate max_avail for each pool
|
||||||
|
for (auto & pp: parent->cli->st_cli.pool_config)
|
||||||
|
{
|
||||||
|
auto & pool_cfg = pp.second;
|
||||||
|
uint64_t pool_avail = UINT64_MAX;
|
||||||
|
std::map<osd_num_t, uint64_t> pg_per_osd;
|
||||||
|
for (auto & pgp: pool_cfg.pg_config)
|
||||||
|
{
|
||||||
|
for (auto pg_osd: pgp.second.target_set)
|
||||||
|
{
|
||||||
|
if (pg_osd != 0)
|
||||||
|
{
|
||||||
|
pg_per_osd[pg_osd]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto pg_per_pair: pg_per_osd)
|
||||||
|
{
|
||||||
|
uint64_t pg_free = osd_free[pg_per_pair.first] * pool_cfg.pg_count / pg_per_pair.second;
|
||||||
|
if (pool_avail > pg_free)
|
||||||
|
{
|
||||||
|
pool_avail = pg_free;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pool_cfg.scheme != POOL_SCHEME_REPLICATED)
|
||||||
|
{
|
||||||
|
pool_avail = pool_avail * (pool_cfg.pg_size - pool_cfg.parity_chunks) / pool_stats[pool_cfg.id]["pg_real_size"].uint64_value();
|
||||||
|
}
|
||||||
|
pool_stats[pool_cfg.id] = json11::Json::object {
|
||||||
|
{ "name", pool_cfg.name },
|
||||||
|
{ "pg_count", pool_cfg.pg_count },
|
||||||
|
{ "scheme", pool_cfg.scheme == POOL_SCHEME_REPLICATED ? "replicated" : "jerasure" },
|
||||||
|
{ "scheme_name", pool_cfg.scheme == POOL_SCHEME_REPLICATED
|
||||||
|
? std::to_string(pool_cfg.pg_size)+"/"+std::to_string(pool_cfg.pg_minsize)
|
||||||
|
: "EC "+std::to_string(pool_cfg.pg_size-pool_cfg.parity_chunks)+"+"+std::to_string(pool_cfg.parity_chunks) },
|
||||||
|
{ "used_raw", (uint64_t)(pool_stats[pool_cfg.id]["used_raw_tb"].number_value() * (1l<<40)) },
|
||||||
|
{ "total_raw", (uint64_t)(pool_stats[pool_cfg.id]["total_raw_tb"].number_value() * (1l<<40)) },
|
||||||
|
{ "max_available", pool_avail },
|
||||||
|
{ "raw_to_usable", pool_stats[pool_cfg.id]["raw_to_usable"].number_value() },
|
||||||
|
{ "space_efficiency", pool_stats[pool_cfg.id]["space_efficiency"].number_value() },
|
||||||
|
{ "pg_real_size", pool_stats[pool_cfg.id]["pg_real_size"].uint64_value() },
|
||||||
|
{ "failure_domain", pool_cfg.failure_domain },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
json11::Json::array to_list()
|
||||||
|
{
|
||||||
|
json11::Json::array list;
|
||||||
|
for (auto & kv: pool_stats)
|
||||||
|
{
|
||||||
|
list.push_back(kv.second);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
get_stats();
|
||||||
|
if (parent->waiting > 0)
|
||||||
|
return;
|
||||||
|
if (parent->json_output)
|
||||||
|
{
|
||||||
|
// JSON output
|
||||||
|
printf("%s\n", json11::Json(to_list()).dump().c_str());
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Table output: name, scheme_name, pg_count, total, used, max_avail, used%, efficiency
|
||||||
|
json11::Json::array cols;
|
||||||
|
cols.push_back(json11::Json::object{
|
||||||
|
{ "key", "name" },
|
||||||
|
{ "title", "NAME" },
|
||||||
|
});
|
||||||
|
cols.push_back(json11::Json::object{
|
||||||
|
{ "key", "scheme_name" },
|
||||||
|
{ "title", "SCHEME" },
|
||||||
|
});
|
||||||
|
cols.push_back(json11::Json::object{
|
||||||
|
{ "key", "pg_count" },
|
||||||
|
{ "title", "PGS" },
|
||||||
|
});
|
||||||
|
cols.push_back(json11::Json::object{
|
||||||
|
{ "key", "total_fmt" },
|
||||||
|
{ "title", "TOTAL" },
|
||||||
|
});
|
||||||
|
cols.push_back(json11::Json::object{
|
||||||
|
{ "key", "used_fmt" },
|
||||||
|
{ "title", "USED" },
|
||||||
|
});
|
||||||
|
cols.push_back(json11::Json::object{
|
||||||
|
{ "key", "max_avail_fmt" },
|
||||||
|
{ "title", "AVAILABLE" },
|
||||||
|
});
|
||||||
|
cols.push_back(json11::Json::object{
|
||||||
|
{ "key", "used_pct" },
|
||||||
|
{ "title", "USED%" },
|
||||||
|
});
|
||||||
|
cols.push_back(json11::Json::object{
|
||||||
|
{ "key", "eff_fmt" },
|
||||||
|
{ "title", "EFFICIENCY" },
|
||||||
|
});
|
||||||
|
json11::Json::array list;
|
||||||
|
for (auto & kv: pool_stats)
|
||||||
|
{
|
||||||
|
kv.second["total_fmt"] = format_size(kv.second["total_raw"].uint64_value() / kv.second["raw_to_usable"].number_value());
|
||||||
|
kv.second["used_fmt"] = format_size(kv.second["used_raw"].uint64_value() / kv.second["raw_to_usable"].number_value());
|
||||||
|
kv.second["max_avail_fmt"] = format_size(kv.second["max_available"].uint64_value());
|
||||||
|
kv.second["used_pct"] = format_q(100 - 100*kv.second["max_available"].uint64_value() *
|
||||||
|
kv.second["raw_to_usable"].number_value() / kv.second["total_raw"].uint64_value())+"%";
|
||||||
|
kv.second["eff_fmt"] = format_q(kv.second["space_efficiency"].number_value()*100)+"%";
|
||||||
|
}
|
||||||
|
printf("%s", print_table(to_list(), cols, parent->color).c_str());
|
||||||
|
state = 100;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::function<bool(void)> cli_tool_t::start_df(json11::Json cfg)
|
||||||
|
{
|
||||||
|
json11::Json::array cmd = cfg["command"].array_items();
|
||||||
|
auto lister = new pool_lister_t();
|
||||||
|
lister->parent = this;
|
||||||
|
return [lister]()
|
||||||
|
{
|
||||||
|
lister->loop();
|
||||||
|
if (lister->is_done())
|
||||||
|
{
|
||||||
|
delete lister;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
}
|
@@ -6,16 +6,6 @@
|
|||||||
#include "cluster_client.h"
|
#include "cluster_client.h"
|
||||||
#include "base64.h"
|
#include "base64.h"
|
||||||
|
|
||||||
#define MIN(a, b) ((a) < (b) ? (b) : (a))
|
|
||||||
|
|
||||||
std::string print_table(json11::Json items, json11::Json header, bool use_esc);
|
|
||||||
|
|
||||||
std::string format_size(uint64_t size);
|
|
||||||
|
|
||||||
std::string format_lat(uint64_t lat);
|
|
||||||
|
|
||||||
std::string format_q(double depth);
|
|
||||||
|
|
||||||
// List existing images
|
// List existing images
|
||||||
//
|
//
|
||||||
// Again, you can just look into etcd, but this console tool incapsulates it
|
// Again, you can just look into etcd, but this console tool incapsulates it
|
||||||
@@ -213,10 +203,21 @@ resume_1:
|
|||||||
json11::Json::array list;
|
json11::Json::array list;
|
||||||
for (auto & kv: stats)
|
for (auto & kv: stats)
|
||||||
{
|
{
|
||||||
if (!only_names.size() || only_names.find(kv.second["name"].string_value()) != only_names.end())
|
if (!only_names.size())
|
||||||
{
|
{
|
||||||
list.push_back(kv.second);
|
list.push_back(kv.second);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (auto glob: only_names)
|
||||||
|
{
|
||||||
|
if (stupid_glob(kv.second["name"].string_value(), glob))
|
||||||
|
{
|
||||||
|
list.push_back(kv.second);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (sort_field == "name" || sort_field == "pool_name")
|
if (sort_field == "name" || sort_field == "pool_name")
|
||||||
{
|
{
|
||||||
@@ -355,6 +356,9 @@ resume_1:
|
|||||||
kv.second["read_bw"] = format_size(kv.second["read_bps"].uint64_value())+"/s";
|
kv.second["read_bw"] = format_size(kv.second["read_bps"].uint64_value())+"/s";
|
||||||
kv.second["write_bw"] = format_size(kv.second["write_bps"].uint64_value())+"/s";
|
kv.second["write_bw"] = format_size(kv.second["write_bps"].uint64_value())+"/s";
|
||||||
kv.second["delete_bw"] = format_size(kv.second["delete_bps"].uint64_value())+"/s";
|
kv.second["delete_bw"] = format_size(kv.second["delete_bps"].uint64_value())+"/s";
|
||||||
|
kv.second["read_iops"] = format_q(kv.second["read_iops"].number_value());
|
||||||
|
kv.second["write_iops"] = format_q(kv.second["write_iops"].number_value());
|
||||||
|
kv.second["delete_iops"] = format_q(kv.second["delete_iops"].number_value());
|
||||||
kv.second["read_lat_f"] = format_lat(kv.second["read_lat"].uint64_value());
|
kv.second["read_lat_f"] = format_lat(kv.second["read_lat"].uint64_value());
|
||||||
kv.second["write_lat_f"] = format_lat(kv.second["write_lat"].uint64_value());
|
kv.second["write_lat_f"] = format_lat(kv.second["write_lat"].uint64_value());
|
||||||
kv.second["delete_lat_f"] = format_lat(kv.second["delete_lat"].uint64_value());
|
kv.second["delete_lat_f"] = format_lat(kv.second["delete_lat"].uint64_value());
|
||||||
@@ -493,6 +497,62 @@ std::string format_q(double depth)
|
|||||||
return std::string(buf);
|
return std::string(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct glob_stack_t
|
||||||
|
{
|
||||||
|
int glob_pos;
|
||||||
|
int str_pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Yes I know I could do it by translating the pattern to std::regex O:-)
|
||||||
|
bool stupid_glob(const std::string str, const std::string glob)
|
||||||
|
{
|
||||||
|
std::vector<glob_stack_t> wildcards;
|
||||||
|
int pos = 0, gp = 0;
|
||||||
|
bool m;
|
||||||
|
back:
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (gp >= glob.length())
|
||||||
|
{
|
||||||
|
if (pos >= str.length())
|
||||||
|
return true;
|
||||||
|
m = false;
|
||||||
|
}
|
||||||
|
else if (glob[gp] == '*')
|
||||||
|
{
|
||||||
|
wildcards.push_back((glob_stack_t){ .glob_pos = ++gp, .str_pos = pos });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (glob[gp] == '?')
|
||||||
|
m = pos < str.size();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (glob[gp] == '\\' && gp < glob.length()-1)
|
||||||
|
gp++;
|
||||||
|
m = pos < str.size() && str[pos] == glob[gp];
|
||||||
|
}
|
||||||
|
if (!m)
|
||||||
|
{
|
||||||
|
while (wildcards.size() > 0)
|
||||||
|
{
|
||||||
|
// Backtrack
|
||||||
|
pos = (++wildcards[wildcards.size()-1].str_pos);
|
||||||
|
if (pos > str.size())
|
||||||
|
wildcards.pop_back();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gp = wildcards[wildcards.size()-1].glob_pos;
|
||||||
|
goto back;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
gp++;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
std::function<bool(void)> cli_tool_t::start_ls(json11::Json cfg)
|
std::function<bool(void)> cli_tool_t::start_ls(json11::Json cfg)
|
||||||
{
|
{
|
||||||
json11::Json::array cmd = cfg["command"].array_items();
|
json11::Json::array cmd = cfg["command"].array_items();
|
||||||
|
@@ -63,6 +63,15 @@ struct image_changer_t
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ((!set_readwrite || !cfg.readonly) &&
|
||||||
|
(!set_readonly || cfg.readonly) &&
|
||||||
|
(!new_size || cfg.size == new_size) &&
|
||||||
|
(new_name == "" || new_name == image_name))
|
||||||
|
{
|
||||||
|
printf("No change\n");
|
||||||
|
state = 100;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (new_size != 0)
|
if (new_size != 0)
|
||||||
{
|
{
|
||||||
if (cfg.size >= new_size)
|
if (cfg.size >= new_size)
|
||||||
@@ -201,11 +210,7 @@ std::function<bool(void)> cli_tool_t::start_modify(json11::Json cfg)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
changer->new_name = cfg["rename"].string_value();
|
changer->new_name = cfg["rename"].string_value();
|
||||||
if (changer->new_name == changer->image_name)
|
changer->new_size = parse_size(cfg["resize"].string_value());
|
||||||
{
|
|
||||||
changer->new_name = "";
|
|
||||||
}
|
|
||||||
changer->new_size = cfg["size"].uint64_value();
|
|
||||||
if (changer->new_size != 0 && (changer->new_size % 4096))
|
if (changer->new_size != 0 && (changer->new_size % 4096))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Image size should be a multiple of 4096\n");
|
fprintf(stderr, "Image size should be a multiple of 4096\n");
|
||||||
|
@@ -28,16 +28,23 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "addr_util.h"
|
||||||
#include "rw_blocking.h"
|
#include "rw_blocking.h"
|
||||||
#include "osd_ops.h"
|
#include "osd_ops.h"
|
||||||
#include "fio_headers.h"
|
#include "fio_headers.h"
|
||||||
|
|
||||||
|
struct op_buf_t
|
||||||
|
{
|
||||||
|
osd_any_op_t buf;
|
||||||
|
io_u* fio_op;
|
||||||
|
};
|
||||||
|
|
||||||
struct sec_data
|
struct sec_data
|
||||||
{
|
{
|
||||||
int connect_fd;
|
int connect_fd;
|
||||||
/* block_size = 1 << block_order (128KB by default) */
|
/* block_size = 1 << block_order (128KB by default) */
|
||||||
uint64_t block_order = 17, block_size = 1 << 17;
|
uint64_t block_order = 17, block_size = 1 << 17;
|
||||||
std::unordered_map<uint64_t, io_u*> queue;
|
std::unordered_map<uint64_t, op_buf_t*> queue;
|
||||||
bool last_sync = false;
|
bool last_sync = false;
|
||||||
/* The list of completed io_u structs. */
|
/* The list of completed io_u structs. */
|
||||||
std::vector<io_u*> completed;
|
std::vector<io_u*> completed;
|
||||||
@@ -52,6 +59,7 @@ struct sec_options
|
|||||||
int single_primary = 0;
|
int single_primary = 0;
|
||||||
int trace = 0;
|
int trace = 0;
|
||||||
int block_order = 17;
|
int block_order = 17;
|
||||||
|
int zerocopy_send = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct fio_option options[] = {
|
static struct fio_option options[] = {
|
||||||
@@ -102,6 +110,16 @@ static struct fio_option options[] = {
|
|||||||
.category = FIO_OPT_C_ENGINE,
|
.category = FIO_OPT_C_ENGINE,
|
||||||
.group = FIO_OPT_G_FILENAME,
|
.group = FIO_OPT_G_FILENAME,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.name = "zerocopy_send",
|
||||||
|
.lname = "Use zero-copy send",
|
||||||
|
.type = FIO_OPT_BOOL,
|
||||||
|
.off1 = offsetof(struct sec_options, zerocopy_send),
|
||||||
|
.help = "Use zero-copy send (MSG_ZEROCOPY)",
|
||||||
|
.def = "0",
|
||||||
|
.category = FIO_OPT_C_ENGINE,
|
||||||
|
.group = FIO_OPT_G_FILENAME,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.name = NULL,
|
.name = NULL,
|
||||||
},
|
},
|
||||||
@@ -152,17 +170,14 @@ static int sec_init(struct thread_data *td)
|
|||||||
bsd->block_order = o->block_order == 0 ? 17 : o->block_order;
|
bsd->block_order = o->block_order == 0 ? 17 : o->block_order;
|
||||||
bsd->block_size = 1 << o->block_order;
|
bsd->block_size = 1 << o->block_order;
|
||||||
|
|
||||||
struct sockaddr_in addr;
|
sockaddr addr;
|
||||||
int r;
|
if (!string_to_addr(std::string(o->host ? o->host : "127.0.0.1"), false, o->port > 0 ? o->port : 11203, &addr))
|
||||||
if ((r = inet_pton(AF_INET, o->host ? o->host : "127.0.0.1", &addr.sin_addr)) != 1)
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "server address: %s%s\n", o->host ? o->host : "127.0.0.1", r == 0 ? " is not valid" : ": no ipv4 support");
|
fprintf(stderr, "server address: %s is not valid\n", o->host ? o->host : "127.0.0.1");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
addr.sin_port = htons(o->port ? o->port : 11203);
|
|
||||||
|
|
||||||
bsd->connect_fd = socket(AF_INET, SOCK_STREAM, 0);
|
bsd->connect_fd = socket(addr.sa_family, SOCK_STREAM, 0);
|
||||||
if (bsd->connect_fd < 0)
|
if (bsd->connect_fd < 0)
|
||||||
{
|
{
|
||||||
perror("socket");
|
perror("socket");
|
||||||
@@ -175,6 +190,14 @@ static int sec_init(struct thread_data *td)
|
|||||||
}
|
}
|
||||||
int one = 1;
|
int one = 1;
|
||||||
setsockopt(bsd->connect_fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
|
setsockopt(bsd->connect_fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
|
||||||
|
if (o->zerocopy_send)
|
||||||
|
{
|
||||||
|
if (setsockopt(bsd->connect_fd, SOL_SOCKET, SO_ZEROCOPY, &one, sizeof(one)) < 0)
|
||||||
|
{
|
||||||
|
perror("setsockopt zerocopy");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: read config (block size) from OSD
|
// FIXME: read config (block size) from OSD
|
||||||
|
|
||||||
@@ -195,7 +218,9 @@ static enum fio_q_status sec_queue(struct thread_data *td, struct io_u *io)
|
|||||||
}
|
}
|
||||||
|
|
||||||
io->engine_data = bsd;
|
io->engine_data = bsd;
|
||||||
osd_any_op_t op = { 0 };
|
op_buf_t *op_buf = new op_buf_t;
|
||||||
|
op_buf->fio_op = io;
|
||||||
|
osd_any_op_t &op = op_buf->buf;
|
||||||
|
|
||||||
op.hdr.magic = SECONDARY_OSD_OP_MAGIC;
|
op.hdr.magic = SECONDARY_OSD_OP_MAGIC;
|
||||||
op.hdr.id = n;
|
op.hdr.id = n;
|
||||||
@@ -271,19 +296,18 @@ static enum fio_q_status sec_queue(struct thread_data *td, struct io_u *io)
|
|||||||
io->error = 0;
|
io->error = 0;
|
||||||
bsd->inflight++;
|
bsd->inflight++;
|
||||||
bsd->op_n++;
|
bsd->op_n++;
|
||||||
bsd->queue[n] = io;
|
bsd->queue[n] = op_buf;
|
||||||
|
|
||||||
iovec iov[2] = { { .iov_base = op.buf, .iov_len = OSD_PACKET_SIZE } };
|
iovec iov[2] = { { .iov_base = op.buf, .iov_len = OSD_PACKET_SIZE } };
|
||||||
int iovcnt = 1, wtotal = OSD_PACKET_SIZE;
|
int iovcnt = 1, wtotal = OSD_PACKET_SIZE;
|
||||||
if (io->ddir == DDIR_WRITE)
|
if (io->ddir == DDIR_WRITE)
|
||||||
{
|
{
|
||||||
iov[1] = { .iov_base = io->xfer_buf, .iov_len = io->xfer_buflen };
|
iov[iovcnt++] = { .iov_base = io->xfer_buf, .iov_len = io->xfer_buflen };
|
||||||
wtotal += io->xfer_buflen;
|
wtotal += io->xfer_buflen;
|
||||||
iovcnt++;
|
|
||||||
}
|
}
|
||||||
if (writev_blocking(bsd->connect_fd, iov, iovcnt) != wtotal)
|
if (sendv_blocking(bsd->connect_fd, iov, iovcnt, opt->zerocopy_send ? MSG_ZEROCOPY : 0) != wtotal)
|
||||||
{
|
{
|
||||||
perror("writev");
|
perror("sendmsg");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,7 +336,8 @@ static int sec_getevents(struct thread_data *td, unsigned int min, unsigned int
|
|||||||
fprintf(stderr, "bad reply: op id %lx missing in local queue\n", reply.hdr.id);
|
fprintf(stderr, "bad reply: op id %lx missing in local queue\n", reply.hdr.id);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
io_u* io = it->second;
|
io_u* io = it->second->fio_op;
|
||||||
|
delete it->second;
|
||||||
bsd->queue.erase(it);
|
bsd->queue.erase(it);
|
||||||
if (io->ddir == DDIR_READ)
|
if (io->ddir == DDIR_READ)
|
||||||
{
|
{
|
||||||
@@ -321,7 +346,23 @@ static int sec_getevents(struct thread_data *td, unsigned int min, unsigned int
|
|||||||
fprintf(stderr, "Short read: retval = %ld instead of %llu\n", reply.hdr.retval, io->xfer_buflen);
|
fprintf(stderr, "Short read: retval = %ld instead of %llu\n", reply.hdr.retval, io->xfer_buflen);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
read_blocking(bsd->connect_fd, io->xfer_buf, io->xfer_buflen);
|
// Support bitmap
|
||||||
|
uint64_t bitmap = 0;
|
||||||
|
int iovcnt = 0;
|
||||||
|
iovec iov[2];
|
||||||
|
if (reply.sec_rw.attr_len > 0)
|
||||||
|
{
|
||||||
|
if (reply.sec_rw.attr_len <= 8)
|
||||||
|
iov[iovcnt++] = { .iov_base = &bitmap, .iov_len = reply.sec_rw.attr_len };
|
||||||
|
else
|
||||||
|
iov[iovcnt++] = { .iov_base = (void*)(bitmap = (uint64_t)malloc(reply.sec_rw.attr_len)), .iov_len = reply.sec_rw.attr_len };
|
||||||
|
}
|
||||||
|
iov[iovcnt++] = { .iov_base = io->xfer_buf, .iov_len = io->xfer_buflen };
|
||||||
|
readv_blocking(bsd->connect_fd, iov, iovcnt);
|
||||||
|
if (reply.sec_rw.attr_len > 8)
|
||||||
|
{
|
||||||
|
free((void*)bitmap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (io->ddir == DDIR_WRITE)
|
else if (io->ddir == DDIR_WRITE)
|
||||||
{
|
{
|
||||||
|
@@ -15,13 +15,13 @@
|
|||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "addr_util.h"
|
||||||
#include "json11/json11.hpp"
|
#include "json11/json11.hpp"
|
||||||
#include "http_client.h"
|
#include "http_client.h"
|
||||||
#include "timerfd_manager.h"
|
#include "timerfd_manager.h"
|
||||||
|
|
||||||
#define READ_BUFFER_SIZE 9000
|
#define READ_BUFFER_SIZE 9000
|
||||||
|
|
||||||
static int extract_port(std::string & host);
|
|
||||||
static std::string trim(const std::string & in);
|
static std::string trim(const std::string & in);
|
||||||
static std::string ws_format_frame(int type, uint64_t size);
|
static std::string ws_format_frame(int type, uint64_t size);
|
||||||
static bool ws_parse_frame(std::string & buf, int & type, std::string & res);
|
static bool ws_parse_frame(std::string & buf, int & type, std::string & res);
|
||||||
@@ -185,19 +185,15 @@ http_co_t::~http_co_t()
|
|||||||
void http_co_t::start_connection()
|
void http_co_t::start_connection()
|
||||||
{
|
{
|
||||||
stackin();
|
stackin();
|
||||||
int port = extract_port(host);
|
struct sockaddr addr;
|
||||||
struct sockaddr_in addr;
|
if (!string_to_addr(host.c_str(), 1, 80, &addr))
|
||||||
int r;
|
|
||||||
if ((r = inet_pton(AF_INET, host.c_str(), &addr.sin_addr)) != 1)
|
|
||||||
{
|
{
|
||||||
parsed.error_code = ENXIO;
|
parsed.error_code = ENXIO;
|
||||||
stackout();
|
stackout();
|
||||||
end();
|
end();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
addr.sin_family = AF_INET;
|
peer_fd = socket(addr.sa_family, SOCK_STREAM, 0);
|
||||||
addr.sin_port = htons(port ? port : 80);
|
|
||||||
peer_fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (peer_fd < 0)
|
if (peer_fd < 0)
|
||||||
{
|
{
|
||||||
parsed.error_code = errno;
|
parsed.error_code = errno;
|
||||||
@@ -219,7 +215,7 @@ void http_co_t::start_connection()
|
|||||||
}
|
}
|
||||||
epoll_events = 0;
|
epoll_events = 0;
|
||||||
// Finally call connect
|
// Finally call connect
|
||||||
r = ::connect(peer_fd, (sockaddr*)&addr, sizeof(addr));
|
int r = ::connect(peer_fd, (sockaddr*)&addr, sizeof(addr));
|
||||||
if (r < 0 && errno != EINPROGRESS)
|
if (r < 0 && errno != EINPROGRESS)
|
||||||
{
|
{
|
||||||
parsed.error_code = errno;
|
parsed.error_code = errno;
|
||||||
@@ -759,22 +755,6 @@ std::vector<std::string> getifaddr_list(json11::Json mask_cfg, bool include_v6)
|
|||||||
return addresses;
|
return addresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int extract_port(std::string & host)
|
|
||||||
{
|
|
||||||
int port = 0;
|
|
||||||
int pos = 0;
|
|
||||||
if ((pos = host.find(':')) >= 0)
|
|
||||||
{
|
|
||||||
port = strtoull(host.c_str() + pos + 1, NULL, 10);
|
|
||||||
if (port >= 0x10000)
|
|
||||||
{
|
|
||||||
port = 0;
|
|
||||||
}
|
|
||||||
host = host.substr(0, pos);
|
|
||||||
}
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string strtolower(const std::string & in)
|
std::string strtolower(const std::string & in)
|
||||||
{
|
{
|
||||||
std::string s = in;
|
std::string s = in;
|
||||||
|
@@ -45,7 +45,7 @@ struct websocket_t
|
|||||||
|
|
||||||
void parse_http_headers(std::string & res, http_response_t *parsed);
|
void parse_http_headers(std::string & res, http_response_t *parsed);
|
||||||
|
|
||||||
std::vector<std::string> getifaddr_list(json11::Json mask_cfg = json11::Json(), bool include_v6 = false);
|
std::vector<std::string> getifaddr_list(json11::Json mask_cfg = json11::Json(), bool include_v6 = true);
|
||||||
|
|
||||||
uint64_t stoull_full(const std::string & str, int base = 10);
|
uint64_t stoull_full(const std::string & str, int base = 10);
|
||||||
|
|
||||||
|
@@ -4,10 +4,13 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <sys/random.h>
|
||||||
#include <sys/epoll.h>
|
#include <sys/epoll.h>
|
||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "base64.h"
|
||||||
|
#include "addr_util.h"
|
||||||
#include "messenger.h"
|
#include "messenger.h"
|
||||||
|
|
||||||
void osd_messenger_t::init()
|
void osd_messenger_t::init()
|
||||||
@@ -193,7 +196,7 @@ void osd_messenger_t::connect_peer(uint64_t peer_osd, json11::Json peer_state)
|
|||||||
try_connect_peer(peer_osd);
|
try_connect_peer(peer_osd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void osd_messenger_t::try_connect_peer(uint64_t peer_osd)
|
void osd_messenger_t::try_connect_peer(osd_num_t peer_osd)
|
||||||
{
|
{
|
||||||
auto wp_it = wanted_peers.find(peer_osd);
|
auto wp_it = wanted_peers.find(peer_osd);
|
||||||
if (wp_it == wanted_peers.end() || wp_it->second.connecting ||
|
if (wp_it == wanted_peers.end() || wp_it->second.connecting ||
|
||||||
@@ -214,43 +217,75 @@ void osd_messenger_t::try_connect_peer(uint64_t peer_osd)
|
|||||||
wp.cur_addr = wp.address_list[wp.address_index].string_value();
|
wp.cur_addr = wp.address_list[wp.address_index].string_value();
|
||||||
wp.cur_port = wp.port;
|
wp.cur_port = wp.port;
|
||||||
wp.connecting = true;
|
wp.connecting = true;
|
||||||
try_connect_peer_addr(peer_osd, wp.cur_addr.c_str(), wp.cur_port);
|
try_connect_peer_addr(peer_osd, wp.cur_addr.c_str(), wp.cur_port, NULL, [this](osd_num_t peer_osd, int peer_fd)
|
||||||
|
{
|
||||||
|
if (peer_fd >= 0)
|
||||||
|
osd_peer_fds[peer_osd] = peer_fd;
|
||||||
|
on_connect_peer(peer_osd, peer_fd);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void osd_messenger_t::try_connect_peer_addr(osd_num_t peer_osd, const char *peer_host, int peer_port)
|
static std::string urandom_str(int bytes)
|
||||||
|
{
|
||||||
|
std::string str;
|
||||||
|
str.resize(bytes);
|
||||||
|
char *buf = (char*)str.data();
|
||||||
|
while (bytes > 0)
|
||||||
|
{
|
||||||
|
int r = getrandom(buf, bytes, 0);
|
||||||
|
if (r < 0)
|
||||||
|
throw std::runtime_error(std::string("getrandom: ") + strerror(errno));
|
||||||
|
buf += r;
|
||||||
|
bytes -= r;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
void osd_messenger_t::try_connect_peer_addr(osd_num_t peer_osd, const char *peer_host, int peer_port,
|
||||||
|
osd_client_t *meta_cl, std::function<void(osd_num_t, int)> connect_callback)
|
||||||
{
|
{
|
||||||
assert(peer_osd != this->osd_num);
|
assert(peer_osd != this->osd_num);
|
||||||
struct sockaddr_in addr;
|
struct sockaddr addr;
|
||||||
int r;
|
if (!meta_cl)
|
||||||
if ((r = inet_pton(AF_INET, peer_host, &addr.sin_addr)) != 1)
|
|
||||||
{
|
{
|
||||||
on_connect_peer(peer_osd, -EINVAL);
|
if (!string_to_addr(peer_host, 0, peer_port, &addr))
|
||||||
return;
|
{
|
||||||
|
connect_callback(peer_osd, -EINVAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addr = meta_cl->peer_addr;
|
||||||
|
}
|
||||||
|
int peer_fd = socket(addr.sa_family, SOCK_STREAM, 0);
|
||||||
|
if (peer_fd >= 0)
|
||||||
|
{
|
||||||
|
fcntl(peer_fd, F_SETFL, fcntl(peer_fd, F_GETFL, 0) | O_NONBLOCK);
|
||||||
|
int r = connect(peer_fd, (sockaddr*)&addr, sizeof(addr));
|
||||||
|
if (r < 0 && errno != EINPROGRESS)
|
||||||
|
{
|
||||||
|
close(peer_fd);
|
||||||
|
peer_fd = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
addr.sin_port = htons(peer_port ? peer_port : 11203);
|
|
||||||
int peer_fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (peer_fd < 0)
|
if (peer_fd < 0)
|
||||||
{
|
{
|
||||||
on_connect_peer(peer_osd, -errno);
|
connect_callback(peer_osd, -errno);
|
||||||
return;
|
|
||||||
}
|
|
||||||
fcntl(peer_fd, F_SETFL, fcntl(peer_fd, F_GETFL, 0) | O_NONBLOCK);
|
|
||||||
r = connect(peer_fd, (sockaddr*)&addr, sizeof(addr));
|
|
||||||
if (r < 0 && errno != EINPROGRESS)
|
|
||||||
{
|
|
||||||
close(peer_fd);
|
|
||||||
on_connect_peer(peer_osd, -errno);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
clients[peer_fd] = new osd_client_t();
|
clients[peer_fd] = new osd_client_t();
|
||||||
clients[peer_fd]->peer_addr = addr;
|
clients[peer_fd]->peer_addr = addr;
|
||||||
clients[peer_fd]->peer_port = peer_port;
|
clients[peer_fd]->peer_port = ((struct sockaddr_in*)&addr)->sin_port;
|
||||||
clients[peer_fd]->peer_fd = peer_fd;
|
clients[peer_fd]->peer_fd = peer_fd;
|
||||||
clients[peer_fd]->peer_state = PEER_CONNECTING;
|
clients[peer_fd]->peer_state = PEER_CONNECTING;
|
||||||
clients[peer_fd]->connect_timeout_id = -1;
|
clients[peer_fd]->connect_timeout_id = -1;
|
||||||
|
clients[peer_fd]->connect_callback = connect_callback;
|
||||||
clients[peer_fd]->osd_num = peer_osd;
|
clients[peer_fd]->osd_num = peer_osd;
|
||||||
clients[peer_fd]->in_buf = malloc_or_die(receive_buffer_size);
|
clients[peer_fd]->in_buf = malloc_or_die(receive_buffer_size);
|
||||||
|
clients[peer_fd]->data_for = meta_cl ? addr_to_string(meta_cl->peer_addr) : "";
|
||||||
|
clients[peer_fd]->data_connection_cookie = meta_cl
|
||||||
|
? meta_cl->data_connection_cookie : base64_encode(urandom_str(12));
|
||||||
tfd->set_fd_handler(peer_fd, true, [this](int peer_fd, int epoll_events)
|
tfd->set_fd_handler(peer_fd, true, [this](int peer_fd, int epoll_events)
|
||||||
{
|
{
|
||||||
// Either OUT (connected) or HUP
|
// Either OUT (connected) or HUP
|
||||||
@@ -260,10 +295,12 @@ void osd_messenger_t::try_connect_peer_addr(osd_num_t peer_osd, const char *peer
|
|||||||
{
|
{
|
||||||
clients[peer_fd]->connect_timeout_id = tfd->set_timer(1000*peer_connect_timeout, false, [this, peer_fd](int timer_id)
|
clients[peer_fd]->connect_timeout_id = tfd->set_timer(1000*peer_connect_timeout, false, [this, peer_fd](int timer_id)
|
||||||
{
|
{
|
||||||
osd_num_t peer_osd = clients.at(peer_fd)->osd_num;
|
auto cl = clients.at(peer_fd);
|
||||||
|
auto connect_callback = cl->connect_callback;
|
||||||
|
cl->connect_callback = NULL;
|
||||||
|
osd_num_t peer_osd = cl->osd_num;
|
||||||
stop_client(peer_fd, true);
|
stop_client(peer_fd, true);
|
||||||
on_connect_peer(peer_osd, -EPIPE);
|
connect_callback(peer_osd, -EPIPE);
|
||||||
return;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -285,8 +322,10 @@ void osd_messenger_t::handle_connect_epoll(int peer_fd)
|
|||||||
}
|
}
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
{
|
{
|
||||||
|
auto connect_callback = cl->connect_callback;
|
||||||
|
cl->connect_callback = NULL;
|
||||||
stop_client(peer_fd, true);
|
stop_client(peer_fd, true);
|
||||||
on_connect_peer(peer_osd, -result);
|
connect_callback(peer_osd, -result);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int one = 1;
|
int one = 1;
|
||||||
@@ -366,6 +405,11 @@ void osd_messenger_t::on_connect_peer(osd_num_t peer_osd, int peer_fd)
|
|||||||
|
|
||||||
void osd_messenger_t::check_peer_config(osd_client_t *cl)
|
void osd_messenger_t::check_peer_config(osd_client_t *cl)
|
||||||
{
|
{
|
||||||
|
json11::Json::object payload;
|
||||||
|
if (cl->data_connection_cookie != "")
|
||||||
|
{
|
||||||
|
payload["data_cookie"] = cl->data_connection_cookie;
|
||||||
|
}
|
||||||
osd_op_t *op = new osd_op_t();
|
osd_op_t *op = new osd_op_t();
|
||||||
op->op_type = OSD_OP_OUT;
|
op->op_type = OSD_OP_OUT;
|
||||||
op->peer_fd = cl->peer_fd;
|
op->peer_fd = cl->peer_fd;
|
||||||
@@ -378,24 +422,33 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
#ifdef WITH_RDMA
|
if (cl->data_for == "")
|
||||||
if (rdma_context)
|
|
||||||
{
|
{
|
||||||
cl->rdma_conn = msgr_rdma_connection_t::create(rdma_context, rdma_max_send, rdma_max_recv, rdma_max_sge, rdma_max_msg);
|
#ifdef WITH_RDMA
|
||||||
if (cl->rdma_conn)
|
if (rdma_context)
|
||||||
{
|
{
|
||||||
json11::Json payload = json11::Json::object {
|
cl->rdma_conn = msgr_rdma_connection_t::create(rdma_context, rdma_max_send, rdma_max_recv, rdma_max_sge, rdma_max_msg);
|
||||||
{ "connect_rdma", cl->rdma_conn->addr.to_string() },
|
if (cl->rdma_conn)
|
||||||
{ "rdma_max_msg", cl->rdma_conn->max_msg },
|
{
|
||||||
};
|
payload["connect_rdma"] = cl->rdma_conn->addr.to_string();
|
||||||
std::string payload_str = payload.dump();
|
payload["rdma_max_msg"] = cl->rdma_conn->max_msg;
|
||||||
op->req.show_conf.json_len = payload_str.size();
|
}
|
||||||
op->buf = malloc_or_die(payload_str.size());
|
|
||||||
op->iov.push_back(op->buf, payload_str.size());
|
|
||||||
memcpy(op->buf, payload_str.c_str(), payload_str.size());
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Mark it as a data connection
|
||||||
|
payload["data_for"] = cl->data_for;
|
||||||
|
}
|
||||||
|
if (payload.size())
|
||||||
|
{
|
||||||
|
std::string payload_str = json11::Json(payload).dump();
|
||||||
|
op->req.show_conf.json_len = payload_str.size();
|
||||||
|
op->buf = malloc_or_die(payload_str.size());
|
||||||
|
op->iov.push_back(op->buf, payload_str.size());
|
||||||
|
memcpy(op->buf, payload_str.c_str(), payload_str.size());
|
||||||
|
}
|
||||||
op->callback = [this, cl](osd_op_t *op)
|
op->callback = [this, cl](osd_op_t *op)
|
||||||
{
|
{
|
||||||
std::string json_err;
|
std::string json_err;
|
||||||
@@ -428,18 +481,30 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
|
|||||||
cl->osd_num, config["protocol_version"].uint64_value(), OSD_PROTOCOL_VERSION
|
cl->osd_num, config["protocol_version"].uint64_value(), OSD_PROTOCOL_VERSION
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
else if (cl->data_for != "" && config["data_for"] != cl->data_for)
|
||||||
|
{
|
||||||
|
err = true;
|
||||||
|
fprintf(
|
||||||
|
stderr, "OSD %lu does not support separate data connections."
|
||||||
|
" Proceeding with a single connection\n", cl->osd_num
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
osd_num_t peer_osd = cl->osd_num;
|
osd_num_t peer_osd = cl->osd_num;
|
||||||
|
auto connect_callback = cl->connect_callback;
|
||||||
|
cl->connect_callback = NULL;
|
||||||
stop_client(op->peer_fd);
|
stop_client(op->peer_fd);
|
||||||
on_connect_peer(peer_osd, -1);
|
connect_callback(peer_osd, -EINVAL);
|
||||||
delete op;
|
delete op;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#ifdef WITH_RDMA
|
#ifdef WITH_RDMA
|
||||||
if (config["rdma_address"].is_string())
|
if (rdma_context && cl->rdma_conn && config["rdma_address"].is_string())
|
||||||
{
|
{
|
||||||
|
// Prevent creating data connection - we are trying RDMA
|
||||||
|
cl->data_connection_cookie = "";
|
||||||
msgr_rdma_address_t addr;
|
msgr_rdma_address_t addr;
|
||||||
if (!msgr_rdma_address_t::from_string(config["rdma_address"].string_value().c_str(), &addr) ||
|
if (!msgr_rdma_address_t::from_string(config["rdma_address"].string_value().c_str(), &addr) ||
|
||||||
cl->rdma_conn->connect(&addr) != 0)
|
cl->rdma_conn->connect(&addr) != 0)
|
||||||
@@ -452,8 +517,10 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
|
|||||||
cl->rdma_conn = NULL;
|
cl->rdma_conn = NULL;
|
||||||
// FIXME: Keep TCP connection in this case
|
// FIXME: Keep TCP connection in this case
|
||||||
osd_num_t peer_osd = cl->osd_num;
|
osd_num_t peer_osd = cl->osd_num;
|
||||||
|
auto connect_callback = cl->connect_callback;
|
||||||
|
cl->connect_callback = NULL;
|
||||||
stop_client(cl->peer_fd);
|
stop_client(cl->peer_fd);
|
||||||
on_connect_peer(peer_osd, -1);
|
connect_callback(peer_osd, -EPIPE);
|
||||||
delete op;
|
delete op;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -475,8 +542,37 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
osd_peer_fds[cl->osd_num] = cl->peer_fd;
|
if (cl->data_connection_cookie != "")
|
||||||
on_connect_peer(cl->osd_num, cl->peer_fd);
|
{
|
||||||
|
// Try to open second connection to the same address
|
||||||
|
try_connect_peer_addr(cl->osd_num, NULL, 0, cl, [this, peer_fd = cl->peer_fd](osd_num_t data_peer, int data_peer_fd)
|
||||||
|
{
|
||||||
|
auto cl_it = clients.find(peer_fd);
|
||||||
|
if (cl_it != clients.end())
|
||||||
|
{
|
||||||
|
// Proceed with or without the data connection
|
||||||
|
auto cl = cl_it->second;
|
||||||
|
if (data_peer_fd >= 0)
|
||||||
|
{
|
||||||
|
cl->data_connection_fd = data_peer_fd;
|
||||||
|
auto data_cl = clients.at(data_peer_fd);
|
||||||
|
data_cl->meta_connection_fd = cl->peer_fd;
|
||||||
|
}
|
||||||
|
osd_peer_fds[cl->osd_num] = cl->peer_fd;
|
||||||
|
on_connect_peer(cl->osd_num, cl->peer_fd);
|
||||||
|
}
|
||||||
|
else if (data_peer_fd >= 0)
|
||||||
|
{
|
||||||
|
stop_client(data_peer_fd);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto connect_callback = cl->connect_callback;
|
||||||
|
cl->connect_callback = NULL;
|
||||||
|
connect_callback(cl->osd_num, cl->peer_fd);
|
||||||
|
}
|
||||||
delete op;
|
delete op;
|
||||||
};
|
};
|
||||||
outbox_push(op);
|
outbox_push(op);
|
||||||
@@ -485,24 +581,24 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
|
|||||||
void osd_messenger_t::accept_connections(int listen_fd)
|
void osd_messenger_t::accept_connections(int listen_fd)
|
||||||
{
|
{
|
||||||
// Accept new connections
|
// Accept new connections
|
||||||
sockaddr_in addr;
|
sockaddr addr;
|
||||||
socklen_t peer_addr_size = sizeof(addr);
|
socklen_t peer_addr_size = sizeof(addr);
|
||||||
int peer_fd;
|
int peer_fd;
|
||||||
while ((peer_fd = accept(listen_fd, (sockaddr*)&addr, &peer_addr_size)) >= 0)
|
while ((peer_fd = accept(listen_fd, &addr, &peer_addr_size)) >= 0)
|
||||||
{
|
{
|
||||||
assert(peer_fd != 0);
|
assert(peer_fd != 0);
|
||||||
char peer_str[256];
|
fprintf(stderr, "[OSD %lu] new client %d: connection from %s\n", this->osd_num, peer_fd,
|
||||||
fprintf(stderr, "[OSD %lu] new client %d: connection from %s port %d\n", this->osd_num, peer_fd,
|
addr_to_string(addr).c_str());
|
||||||
inet_ntop(AF_INET, &addr.sin_addr, peer_str, 256), ntohs(addr.sin_port));
|
|
||||||
fcntl(peer_fd, F_SETFL, fcntl(peer_fd, F_GETFL, 0) | O_NONBLOCK);
|
fcntl(peer_fd, F_SETFL, fcntl(peer_fd, F_GETFL, 0) | O_NONBLOCK);
|
||||||
int one = 1;
|
int one = 1;
|
||||||
setsockopt(peer_fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
|
setsockopt(peer_fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
|
||||||
clients[peer_fd] = new osd_client_t();
|
clients[peer_fd] = new osd_client_t();
|
||||||
clients[peer_fd]->peer_addr = addr;
|
clients[peer_fd]->peer_addr = addr;
|
||||||
clients[peer_fd]->peer_port = ntohs(addr.sin_port);
|
clients[peer_fd]->peer_port = ntohs(((sockaddr_in*)&addr)->sin_port);
|
||||||
clients[peer_fd]->peer_fd = peer_fd;
|
clients[peer_fd]->peer_fd = peer_fd;
|
||||||
clients[peer_fd]->peer_state = PEER_CONNECTED;
|
clients[peer_fd]->peer_state = PEER_CONNECTED;
|
||||||
clients[peer_fd]->in_buf = malloc_or_die(receive_buffer_size);
|
clients[peer_fd]->in_buf = malloc_or_die(receive_buffer_size);
|
||||||
|
clients_by_addr[addr_to_string(addr)] = peer_fd;
|
||||||
// Add FD to epoll
|
// Add FD to epoll
|
||||||
tfd->set_fd_handler(peer_fd, false, [this](int peer_fd, int epoll_events)
|
tfd->set_fd_handler(peer_fd, false, [this](int peer_fd, int epoll_events)
|
||||||
{
|
{
|
||||||
|
@@ -49,7 +49,7 @@ struct osd_client_t
|
|||||||
{
|
{
|
||||||
int refs = 0;
|
int refs = 0;
|
||||||
|
|
||||||
sockaddr_in peer_addr;
|
sockaddr peer_addr;
|
||||||
int peer_port;
|
int peer_port;
|
||||||
int peer_fd;
|
int peer_fd;
|
||||||
int peer_state;
|
int peer_state;
|
||||||
@@ -57,6 +57,10 @@ struct osd_client_t
|
|||||||
int ping_time_remaining = 0;
|
int ping_time_remaining = 0;
|
||||||
int idle_time_remaining = 0;
|
int idle_time_remaining = 0;
|
||||||
osd_num_t osd_num = 0;
|
osd_num_t osd_num = 0;
|
||||||
|
std::function<void(osd_num_t, int)> connect_callback;
|
||||||
|
|
||||||
|
int data_connection_fd = -1, meta_connection_fd = -1;
|
||||||
|
std::string data_connection_cookie, data_for;
|
||||||
|
|
||||||
void *in_buf = NULL;
|
void *in_buf = NULL;
|
||||||
|
|
||||||
@@ -148,6 +152,7 @@ public:
|
|||||||
osd_num_t osd_num;
|
osd_num_t osd_num;
|
||||||
uint64_t next_subop_id = 1;
|
uint64_t next_subop_id = 1;
|
||||||
std::map<int, osd_client_t*> clients;
|
std::map<int, osd_client_t*> clients;
|
||||||
|
std::map<std::string, int> clients_by_addr;
|
||||||
std::map<osd_num_t, osd_wanted_peer_t> wanted_peers;
|
std::map<osd_num_t, osd_wanted_peer_t> wanted_peers;
|
||||||
std::map<uint64_t, int> osd_peer_fds;
|
std::map<uint64_t, int> osd_peer_fds;
|
||||||
// op statistics
|
// op statistics
|
||||||
@@ -157,6 +162,7 @@ public:
|
|||||||
void parse_config(const json11::Json & config);
|
void parse_config(const json11::Json & config);
|
||||||
void connect_peer(uint64_t osd_num, json11::Json peer_state);
|
void connect_peer(uint64_t osd_num, json11::Json peer_state);
|
||||||
void stop_client(int peer_fd, bool force = false, bool force_delete = false);
|
void stop_client(int peer_fd, bool force = false, bool force_delete = false);
|
||||||
|
void break_data_client_pair(osd_client_t *cl);
|
||||||
void outbox_push(osd_op_t *cur_op);
|
void outbox_push(osd_op_t *cur_op);
|
||||||
std::function<void(osd_op_t*)> exec_op;
|
std::function<void(osd_op_t*)> exec_op;
|
||||||
std::function<void(osd_num_t)> repeer_pgs;
|
std::function<void(osd_num_t)> repeer_pgs;
|
||||||
@@ -174,7 +180,8 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void try_connect_peer(uint64_t osd_num);
|
void try_connect_peer(uint64_t osd_num);
|
||||||
void try_connect_peer_addr(osd_num_t peer_osd, const char *peer_host, int peer_port);
|
void try_connect_peer_addr(osd_num_t peer_osd, const char *peer_host, int peer_port,
|
||||||
|
osd_client_t *meta_cl, std::function<void(osd_num_t, int)> connect_callback);
|
||||||
void handle_peer_epoll(int peer_fd, int epoll_events);
|
void handle_peer_epoll(int peer_fd, int epoll_events);
|
||||||
void handle_connect_epoll(int peer_fd);
|
void handle_connect_epoll(int peer_fd);
|
||||||
void on_connect_peer(osd_num_t peer_osd, int peer_fd);
|
void on_connect_peer(osd_num_t peer_osd, int peer_fd);
|
||||||
|
@@ -62,7 +62,13 @@ msgr_rdma_context_t *msgr_rdma_context_t::create(const char *ib_devname, uint8_t
|
|||||||
dev_list = ibv_get_device_list(NULL);
|
dev_list = ibv_get_device_list(NULL);
|
||||||
if (!dev_list)
|
if (!dev_list)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Failed to get RDMA device list: %s\n", strerror(errno));
|
if (errno == -ENOSYS || errno == ENOSYS)
|
||||||
|
{
|
||||||
|
if (log_level > 0)
|
||||||
|
fprintf(stderr, "No RDMA devices found (RDMA device list returned ENOSYS)\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fprintf(stderr, "Failed to get RDMA device list: %s\n", strerror(errno));
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
if (!ib_devname)
|
if (!ib_devname)
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "addr_util.h"
|
||||||
#include "messenger.h"
|
#include "messenger.h"
|
||||||
|
|
||||||
void osd_messenger_t::cancel_osd_ops(osd_client_t *cl)
|
void osd_messenger_t::cancel_osd_ops(osd_client_t *cl)
|
||||||
@@ -58,7 +59,8 @@ void osd_messenger_t::stop_client(int peer_fd, bool force, bool force_delete)
|
|||||||
{
|
{
|
||||||
if (cl->osd_num)
|
if (cl->osd_num)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "[OSD %lu] Stopping client %d (OSD peer %lu)\n", osd_num, peer_fd, cl->osd_num);
|
fprintf(stderr, "[OSD %lu] Stopping client %d (OSD %speer %lu)\n",
|
||||||
|
osd_num, peer_fd, cl->meta_connection_fd >= 0 ? " data" : "", cl->osd_num);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -68,7 +70,7 @@ void osd_messenger_t::stop_client(int peer_fd, bool force, bool force_delete)
|
|||||||
// First set state to STOPPED so another stop_client() call doesn't try to free it again
|
// First set state to STOPPED so another stop_client() call doesn't try to free it again
|
||||||
cl->refs++;
|
cl->refs++;
|
||||||
cl->peer_state = PEER_STOPPED;
|
cl->peer_state = PEER_STOPPED;
|
||||||
if (cl->osd_num)
|
if (cl->osd_num && cl->meta_connection_fd < 0)
|
||||||
{
|
{
|
||||||
// ...and forget OSD peer
|
// ...and forget OSD peer
|
||||||
osd_peer_fds.erase(cl->osd_num);
|
osd_peer_fds.erase(cl->osd_num);
|
||||||
@@ -100,9 +102,17 @@ void osd_messenger_t::stop_client(int peer_fd, bool force, bool force_delete)
|
|||||||
#endif
|
#endif
|
||||||
if (cl->osd_num)
|
if (cl->osd_num)
|
||||||
{
|
{
|
||||||
// Then repeer PGs because cancel_op() callbacks can try to perform
|
if (cl->meta_connection_fd < 0)
|
||||||
// some actions and we need correct PG states to not do something silly
|
{
|
||||||
repeer_pgs(cl->osd_num);
|
// Then repeer PGs because cancel_op() callbacks can try to perform
|
||||||
|
// some actions and we need correct PG states to not do something silly
|
||||||
|
repeer_pgs(cl->osd_num);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// FIXME Try to re-establish data connection
|
||||||
|
// Only when the connection is outbound, but here it's always outbound
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Then cancel all operations
|
// Then cancel all operations
|
||||||
if (cl->read_op)
|
if (cl->read_op)
|
||||||
@@ -128,6 +138,7 @@ void osd_messenger_t::stop_client(int peer_fd, bool force, bool force_delete)
|
|||||||
delete cl->rdma_conn;
|
delete cl->rdma_conn;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
clients_by_addr.erase(addr_to_string(cl->peer_addr));
|
||||||
#endif
|
#endif
|
||||||
// Find the item again because it can be invalidated at this point
|
// Find the item again because it can be invalidated at this point
|
||||||
it = clients.find(peer_fd);
|
it = clients.find(peer_fd);
|
||||||
@@ -135,9 +146,40 @@ void osd_messenger_t::stop_client(int peer_fd, bool force, bool force_delete)
|
|||||||
{
|
{
|
||||||
clients.erase(it);
|
clients.erase(it);
|
||||||
}
|
}
|
||||||
|
// Break metadata/data connection pair
|
||||||
|
if (cl->data_connection_fd >= 0)
|
||||||
|
{
|
||||||
|
// No sense to keep data connection when metadata connection is stopped
|
||||||
|
auto dc_it = clients.find(cl->data_connection_fd);
|
||||||
|
cl->data_connection_fd = -1;
|
||||||
|
if (dc_it != clients.end() && dc_it->second->meta_connection_fd == cl->peer_fd)
|
||||||
|
{
|
||||||
|
stop_client(dc_it->second->peer_fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break_data_client_pair(cl);
|
||||||
|
// Refcount and delete
|
||||||
cl->refs--;
|
cl->refs--;
|
||||||
if (cl->refs <= 0 || force_delete)
|
if (cl->refs <= 0 || force_delete)
|
||||||
{
|
{
|
||||||
delete cl;
|
delete cl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void osd_messenger_t::break_data_client_pair(osd_client_t *cl)
|
||||||
|
{
|
||||||
|
if (cl->meta_connection_fd >= 0)
|
||||||
|
{
|
||||||
|
auto dc_it = clients.find(cl->meta_connection_fd);
|
||||||
|
if (dc_it != clients.end() && dc_it->second->data_connection_fd == cl->peer_fd)
|
||||||
|
dc_it->second->data_connection_fd = -1;
|
||||||
|
cl->meta_connection_fd = -1;
|
||||||
|
}
|
||||||
|
if (cl->data_connection_fd >= 0)
|
||||||
|
{
|
||||||
|
auto dc_it = clients.find(cl->data_connection_fd);
|
||||||
|
if (dc_it != clients.end() && dc_it->second->meta_connection_fd == cl->peer_fd)
|
||||||
|
dc_it->second->meta_connection_fd = -1;
|
||||||
|
cl->data_connection_fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
41
src/osd.cpp
41
src/osd.cpp
@@ -7,6 +7,7 @@
|
|||||||
#include <netinet/tcp.h>
|
#include <netinet/tcp.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include "addr_util.h"
|
||||||
#include "blockstore_impl.h"
|
#include "blockstore_impl.h"
|
||||||
#include "osd_primary.h"
|
#include "osd_primary.h"
|
||||||
#include "osd.h"
|
#include "osd.h"
|
||||||
@@ -156,14 +157,6 @@ void osd_t::parse_config(const json11::Json & config)
|
|||||||
|
|
||||||
void osd_t::bind_socket()
|
void osd_t::bind_socket()
|
||||||
{
|
{
|
||||||
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (listen_fd < 0)
|
|
||||||
{
|
|
||||||
throw std::runtime_error(std::string("socket: ") + strerror(errno));
|
|
||||||
}
|
|
||||||
int enable = 1;
|
|
||||||
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
|
|
||||||
|
|
||||||
if (config["osd_network"].is_string() ||
|
if (config["osd_network"].is_string() ||
|
||||||
config["osd_network"].is_array())
|
config["osd_network"].is_array())
|
||||||
{
|
{
|
||||||
@@ -173,28 +166,40 @@ void osd_t::bind_socket()
|
|||||||
else
|
else
|
||||||
for (auto v: config["osd_network"].array_items())
|
for (auto v: config["osd_network"].array_items())
|
||||||
mask.push_back(v.string_value());
|
mask.push_back(v.string_value());
|
||||||
auto matched_addrs = getifaddr_list(mask, false);
|
auto matched_addrs = getifaddr_list(mask);
|
||||||
if (matched_addrs.size() > 1)
|
if (matched_addrs.size() > 1)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "More than 1 address matches requested network(s): %s\n", json11::Json(matched_addrs).dump().c_str());
|
fprintf(stderr, "More than 1 address matches requested network(s): %s\n", json11::Json(matched_addrs).dump().c_str());
|
||||||
force_stop(1);
|
force_stop(1);
|
||||||
}
|
}
|
||||||
|
if (!matched_addrs.size())
|
||||||
|
{
|
||||||
|
std::string nets;
|
||||||
|
for (auto v: mask)
|
||||||
|
nets += (nets == "" ? v : ","+v);
|
||||||
|
fprintf(stderr, "Addresses matching osd_network(s) %s not found\n", nets.c_str());
|
||||||
|
force_stop(1);
|
||||||
|
}
|
||||||
bind_address = matched_addrs[0];
|
bind_address = matched_addrs[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME Support multiple listening sockets
|
// FIXME Support multiple listening sockets
|
||||||
|
|
||||||
sockaddr_in addr;
|
sockaddr addr;
|
||||||
int r;
|
if (!string_to_addr(bind_address, 0, bind_port, &addr))
|
||||||
if ((r = inet_pton(AF_INET, bind_address.c_str(), &addr.sin_addr)) != 1)
|
|
||||||
{
|
{
|
||||||
close(listen_fd);
|
throw std::runtime_error("bind address "+bind_address+" is not valid");
|
||||||
throw std::runtime_error("bind address "+bind_address+(r == 0 ? " is not valid" : ": no ipv4 support"));
|
|
||||||
}
|
}
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
|
|
||||||
addr.sin_port = htons(bind_port);
|
listen_fd = socket(addr.sa_family, SOCK_STREAM, 0);
|
||||||
if (bind(listen_fd, (sockaddr*)&addr, sizeof(addr)) < 0)
|
if (listen_fd < 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string("socket: ") + strerror(errno));
|
||||||
|
}
|
||||||
|
int enable = 1;
|
||||||
|
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
|
||||||
|
|
||||||
|
if (bind(listen_fd, &addr, sizeof(addr)) < 0)
|
||||||
{
|
{
|
||||||
close(listen_fd);
|
close(listen_fd);
|
||||||
throw std::runtime_error(std::string("bind: ") + strerror(errno));
|
throw std::runtime_error(std::string("bind: ") + strerror(errno));
|
||||||
@@ -207,7 +212,7 @@ void osd_t::bind_socket()
|
|||||||
close(listen_fd);
|
close(listen_fd);
|
||||||
throw std::runtime_error(std::string("getsockname: ") + strerror(errno));
|
throw std::runtime_error(std::string("getsockname: ") + strerror(errno));
|
||||||
}
|
}
|
||||||
listening_port = ntohs(addr.sin_port);
|
listening_port = ntohs(((sockaddr_in*)&addr)->sin_port);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@@ -102,7 +102,7 @@ class osd_t
|
|||||||
bool no_rebalance = false;
|
bool no_rebalance = false;
|
||||||
bool no_recovery = false;
|
bool no_recovery = false;
|
||||||
std::string bind_address;
|
std::string bind_address;
|
||||||
int bind_port, listen_backlog;
|
int bind_port, listen_backlog = 128;
|
||||||
// FIXME: Implement client queue depth limit
|
// FIXME: Implement client queue depth limit
|
||||||
int client_queue_depth = 128;
|
int client_queue_depth = 128;
|
||||||
bool allow_test_ops = false;
|
bool allow_test_ops = false;
|
||||||
|
@@ -194,6 +194,24 @@ void osd_t::start_pg_peering(pg_t & pg)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (pg.pg_cursize < pg.pg_minsize)
|
||||||
|
{
|
||||||
|
pg.state = PG_INCOMPLETE;
|
||||||
|
report_pg_state(pg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::set<osd_num_t> cur_peers;
|
||||||
|
for (auto pg_osd: pg.all_peers)
|
||||||
|
{
|
||||||
|
if (pg_osd == this->osd_num || msgr.osd_peer_fds.find(pg_osd) != msgr.osd_peer_fds.end())
|
||||||
|
{
|
||||||
|
cur_peers.insert(pg_osd);
|
||||||
|
}
|
||||||
|
else if (msgr.wanted_peers.find(pg_osd) == msgr.wanted_peers.end())
|
||||||
|
{
|
||||||
|
msgr.connect_peer(pg_osd, st_cli.peer_states[pg_osd]);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (pg.target_history.size())
|
if (pg.target_history.size())
|
||||||
{
|
{
|
||||||
// Refuse to start PG if no peers are available from any of the historical OSD sets
|
// Refuse to start PG if no peers are available from any of the historical OSD sets
|
||||||
@@ -222,24 +240,6 @@ void osd_t::start_pg_peering(pg_t & pg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (pg.pg_cursize < pg.pg_minsize)
|
|
||||||
{
|
|
||||||
pg.state = PG_INCOMPLETE;
|
|
||||||
report_pg_state(pg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::set<osd_num_t> cur_peers;
|
|
||||||
for (auto pg_osd: pg.all_peers)
|
|
||||||
{
|
|
||||||
if (pg_osd == this->osd_num || msgr.osd_peer_fds.find(pg_osd) != msgr.osd_peer_fds.end())
|
|
||||||
{
|
|
||||||
cur_peers.insert(pg_osd);
|
|
||||||
}
|
|
||||||
else if (msgr.wanted_peers.find(pg_osd) == msgr.wanted_peers.end())
|
|
||||||
{
|
|
||||||
msgr.connect_peer(pg_osd, st_cli.peer_states[pg_osd]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pg.cur_peers.insert(pg.cur_peers.begin(), cur_peers.begin(), cur_peers.end());
|
pg.cur_peers.insert(pg.cur_peers.begin(), cur_peers.begin(), cur_peers.end());
|
||||||
if (pg.peering_state)
|
if (pg.peering_state)
|
||||||
{
|
{
|
||||||
|
@@ -178,6 +178,37 @@ void osd_t::exec_show_config(osd_op_t *cur_op)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
if (req_json["data_for"].is_string())
|
||||||
|
{
|
||||||
|
auto cli = msgr.clients.at(cur_op->peer_fd);
|
||||||
|
auto md_it = msgr.clients_by_addr.find(req_json["data_for"].string_value());
|
||||||
|
if (md_it != msgr.clients_by_addr.end())
|
||||||
|
{
|
||||||
|
int md_peer_fd = md_it->second;
|
||||||
|
auto md_it = msgr.clients.find(md_peer_fd);
|
||||||
|
if (md_it != msgr.clients.end() && md_it->second->data_connection_cookie != "" &&
|
||||||
|
req_json["data_cookie"].string_value() == md_it->second->data_connection_cookie)
|
||||||
|
{
|
||||||
|
// Break previous metadata/data connections for both FDs, if present
|
||||||
|
msgr.break_data_client_pair(cli);
|
||||||
|
msgr.break_data_client_pair(md_it->second);
|
||||||
|
// And setup the new pair
|
||||||
|
cli->meta_connection_fd = md_it->second->peer_fd;
|
||||||
|
md_it->second->data_connection_fd = cli->peer_fd;
|
||||||
|
wire_config["data_for"] = req_json["data_for"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (req_json["data_cookie"].is_string())
|
||||||
|
{
|
||||||
|
auto cli = msgr.clients.at(cur_op->peer_fd);
|
||||||
|
cli->data_connection_cookie = req_json["data_cookie"].string_value();
|
||||||
|
}
|
||||||
|
#ifdef WITH_RDMA
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (cur_op->buf)
|
if (cur_op->buf)
|
||||||
free(cur_op->buf);
|
free(cur_op->buf);
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "addr_util.h"
|
||||||
#include "osd_ops.h"
|
#include "osd_ops.h"
|
||||||
#include "rw_blocking.h"
|
#include "rw_blocking.h"
|
||||||
#include "test_pattern.h"
|
#include "test_pattern.h"
|
||||||
@@ -133,17 +134,14 @@ int main(int narg, char *args[])
|
|||||||
|
|
||||||
int connect_osd(const char *osd_address, int osd_port)
|
int connect_osd(const char *osd_address, int osd_port)
|
||||||
{
|
{
|
||||||
struct sockaddr_in addr;
|
struct sockaddr addr;
|
||||||
int r;
|
if (!string_to_addr(osd_address, 0, osd_port, &addr))
|
||||||
if ((r = inet_pton(AF_INET, osd_address, &addr.sin_addr)) != 1)
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "server address: %s%s\n", osd_address, r == 0 ? " is not valid" : ": no ipv4 support");
|
fprintf(stderr, "server address: %s is not valid\n", osd_address);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
addr.sin_port = htons(osd_port);
|
|
||||||
|
|
||||||
int connect_fd = socket(AF_INET, SOCK_STREAM, 0);
|
int connect_fd = socket(addr.sa_family, SOCK_STREAM, 0);
|
||||||
if (connect_fd < 0)
|
if (connect_fd < 0)
|
||||||
{
|
{
|
||||||
perror("socket");
|
perror("socket");
|
||||||
|
@@ -308,7 +308,7 @@ static void vitastor_close(BlockDriverState *bs)
|
|||||||
static int vitastor_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz)
|
static int vitastor_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz)
|
||||||
{
|
{
|
||||||
bsz->phys = 4096;
|
bsz->phys = 4096;
|
||||||
bsz->log = 4096;
|
bsz->log = 512;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -4,6 +4,8 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
#include "rw_blocking.h"
|
#include "rw_blocking.h"
|
||||||
|
|
||||||
@@ -60,7 +62,7 @@ int readv_blocking(int fd, iovec *iov, int iovcnt)
|
|||||||
int done = 0;
|
int done = 0;
|
||||||
while (v < iovcnt)
|
while (v < iovcnt)
|
||||||
{
|
{
|
||||||
ssize_t r = readv(fd, iov, iovcnt);
|
ssize_t r = readv(fd, iov+v, iovcnt-v);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
{
|
{
|
||||||
if (errno != EAGAIN && errno != EPIPE)
|
if (errno != EAGAIN && errno != EPIPE)
|
||||||
@@ -70,6 +72,7 @@ int readv_blocking(int fd, iovec *iov, int iovcnt)
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
done += r;
|
||||||
while (v < iovcnt)
|
while (v < iovcnt)
|
||||||
{
|
{
|
||||||
if (iov[v].iov_len > r)
|
if (iov[v].iov_len > r)
|
||||||
@@ -80,10 +83,10 @@ int readv_blocking(int fd, iovec *iov, int iovcnt)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
r -= iov[v].iov_len;
|
||||||
v++;
|
v++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
done += r;
|
|
||||||
}
|
}
|
||||||
return done;
|
return done;
|
||||||
}
|
}
|
||||||
@@ -94,7 +97,7 @@ int writev_blocking(int fd, iovec *iov, int iovcnt)
|
|||||||
int done = 0;
|
int done = 0;
|
||||||
while (v < iovcnt)
|
while (v < iovcnt)
|
||||||
{
|
{
|
||||||
ssize_t r = writev(fd, iov, iovcnt);
|
ssize_t r = writev(fd, iov+v, iovcnt-v);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
{
|
{
|
||||||
if (errno != EAGAIN && errno != EPIPE)
|
if (errno != EAGAIN && errno != EPIPE)
|
||||||
@@ -104,6 +107,7 @@ int writev_blocking(int fd, iovec *iov, int iovcnt)
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
done += r;
|
||||||
while (v < iovcnt)
|
while (v < iovcnt)
|
||||||
{
|
{
|
||||||
if (iov[v].iov_len > r)
|
if (iov[v].iov_len > r)
|
||||||
@@ -114,10 +118,48 @@ int writev_blocking(int fd, iovec *iov, int iovcnt)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
r -= iov[v].iov_len;
|
||||||
|
v++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sendv_blocking(int fd, iovec *iov, int iovcnt, int flags)
|
||||||
|
{
|
||||||
|
struct msghdr msg = { 0 };
|
||||||
|
int v = 0;
|
||||||
|
int done = 0;
|
||||||
|
while (v < iovcnt)
|
||||||
|
{
|
||||||
|
msg.msg_iov = iov+v;
|
||||||
|
msg.msg_iovlen = iovcnt-v;
|
||||||
|
ssize_t r = sendmsg(fd, &msg, flags);
|
||||||
|
if (r < 0)
|
||||||
|
{
|
||||||
|
if (errno != EAGAIN && errno != EPIPE)
|
||||||
|
{
|
||||||
|
perror("sendmsg");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
done += r;
|
||||||
|
while (v < iovcnt)
|
||||||
|
{
|
||||||
|
if (iov[v].iov_len > r)
|
||||||
|
{
|
||||||
|
iov[v].iov_len -= r;
|
||||||
|
iov[v].iov_base += r;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
r -= iov[v].iov_len;
|
||||||
v++;
|
v++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
done += r;
|
|
||||||
}
|
}
|
||||||
return done;
|
return done;
|
||||||
}
|
}
|
||||||
|
@@ -10,3 +10,4 @@ int read_blocking(int fd, void *read_buf, size_t remaining);
|
|||||||
int write_blocking(int fd, void *write_buf, size_t remaining);
|
int write_blocking(int fd, void *write_buf, size_t remaining);
|
||||||
int readv_blocking(int fd, iovec *iov, int iovcnt);
|
int readv_blocking(int fd, iovec *iov, int iovcnt);
|
||||||
int writev_blocking(int fd, iovec *iov, int iovcnt);
|
int writev_blocking(int fd, iovec *iov, int iovcnt);
|
||||||
|
int sendv_blocking(int fd, iovec *iov, int iovcnt, int flags);
|
||||||
|
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "addr_util.h"
|
||||||
#include "rw_blocking.h"
|
#include "rw_blocking.h"
|
||||||
#include "osd_ops.h"
|
#include "osd_ops.h"
|
||||||
|
|
||||||
@@ -66,16 +67,14 @@ int main(int narg, char *args[])
|
|||||||
|
|
||||||
int connect_stub(const char *server_address, int server_port)
|
int connect_stub(const char *server_address, int server_port)
|
||||||
{
|
{
|
||||||
struct sockaddr_in addr;
|
struct sockaddr addr;
|
||||||
int r;
|
if (!string_to_addr(server_address, 0, server_port, &addr))
|
||||||
if ((r = inet_pton(AF_INET, server_address, &addr.sin_addr)) != 1)
|
|
||||||
{
|
{
|
||||||
fprintf(stderr, "server address: %s%s\n", server_address, r == 0 ? " is not valid" : ": no ipv4 support");
|
fprintf(stderr, "server address: %s is not valid\n", server_address);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
addr.sin_port = htons(server_port);
|
int connect_fd = socket(addr.sa_family, SOCK_STREAM, 0);
|
||||||
int connect_fd = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (connect_fd < 0)
|
if (connect_fd < 0)
|
||||||
{
|
{
|
||||||
perror("socket");
|
perror("socket");
|
||||||
|
@@ -37,10 +37,11 @@
|
|||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "addr_util.h"
|
||||||
#include "rw_blocking.h"
|
#include "rw_blocking.h"
|
||||||
#include "osd_ops.h"
|
#include "osd_ops.h"
|
||||||
|
|
||||||
int bind_stub(const char *bind_address, int bind_port);
|
int bind_stub(std::string bind_address, int bind_port);
|
||||||
|
|
||||||
void run_stub(int peer_fd);
|
void run_stub(int peer_fd);
|
||||||
|
|
||||||
@@ -48,13 +49,13 @@ int main(int narg, char *args[])
|
|||||||
{
|
{
|
||||||
int listen_fd = bind_stub("0.0.0.0", 11203);
|
int listen_fd = bind_stub("0.0.0.0", 11203);
|
||||||
// Accept new connections
|
// Accept new connections
|
||||||
sockaddr_in addr;
|
sockaddr addr;
|
||||||
socklen_t peer_addr_size = sizeof(addr);
|
socklen_t peer_addr_size = sizeof(addr);
|
||||||
int peer_fd;
|
int peer_fd;
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
printf("stub_osd: waiting for 1 client\n");
|
printf("stub_osd: waiting for 1 client\n");
|
||||||
peer_fd = accept(listen_fd, (sockaddr*)&addr, &peer_addr_size);
|
peer_fd = accept(listen_fd, &addr, &peer_addr_size);
|
||||||
if (peer_fd == -1)
|
if (peer_fd == -1)
|
||||||
{
|
{
|
||||||
if (errno == EAGAIN)
|
if (errno == EAGAIN)
|
||||||
@@ -62,9 +63,8 @@ int main(int narg, char *args[])
|
|||||||
else
|
else
|
||||||
throw std::runtime_error(std::string("accept: ") + strerror(errno));
|
throw std::runtime_error(std::string("accept: ") + strerror(errno));
|
||||||
}
|
}
|
||||||
char peer_str[256];
|
printf("stub_osd: new client %d: connection from %s\n", peer_fd,
|
||||||
printf("stub_osd: new client %d: connection from %s port %d\n", peer_fd,
|
addr_to_string(addr).c_str());
|
||||||
inet_ntop(AF_INET, &addr.sin_addr, peer_str, 256), ntohs(addr.sin_port));
|
|
||||||
int one = 1;
|
int one = 1;
|
||||||
setsockopt(peer_fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
|
setsockopt(peer_fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
|
||||||
run_stub(peer_fd);
|
run_stub(peer_fd);
|
||||||
@@ -76,11 +76,17 @@ int main(int narg, char *args[])
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int bind_stub(const char *bind_address, int bind_port)
|
int bind_stub(std::string bind_address, int bind_port)
|
||||||
{
|
{
|
||||||
int listen_backlog = 128;
|
int listen_backlog = 128;
|
||||||
|
|
||||||
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
|
sockaddr addr;
|
||||||
|
if (!string_to_addr(bind_address, 0, bind_port, &addr))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("bind address "+bind_address+" is not valid");
|
||||||
|
}
|
||||||
|
|
||||||
|
int listen_fd = socket(addr.sa_family, SOCK_STREAM, 0);
|
||||||
if (listen_fd < 0)
|
if (listen_fd < 0)
|
||||||
{
|
{
|
||||||
throw std::runtime_error(std::string("socket: ") + strerror(errno));
|
throw std::runtime_error(std::string("socket: ") + strerror(errno));
|
||||||
@@ -88,17 +94,7 @@ int bind_stub(const char *bind_address, int bind_port)
|
|||||||
int enable = 1;
|
int enable = 1;
|
||||||
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
|
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
|
||||||
|
|
||||||
sockaddr_in addr;
|
if (bind(listen_fd, &addr, sizeof(addr)) < 0)
|
||||||
int r;
|
|
||||||
if ((r = inet_pton(AF_INET, bind_address, &addr.sin_addr)) != 1)
|
|
||||||
{
|
|
||||||
close(listen_fd);
|
|
||||||
throw std::runtime_error("bind address "+std::string(bind_address)+(r == 0 ? " is not valid" : ": no ipv4 support"));
|
|
||||||
}
|
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
addr.sin_port = htons(bind_port);
|
|
||||||
|
|
||||||
if (bind(listen_fd, (sockaddr*)&addr, sizeof(addr)) < 0)
|
|
||||||
{
|
{
|
||||||
close(listen_fd);
|
close(listen_fd);
|
||||||
throw std::runtime_error(std::string("bind: ") + strerror(errno));
|
throw std::runtime_error(std::string("bind: ") + strerror(errno));
|
||||||
@@ -116,7 +112,7 @@ int bind_stub(const char *bind_address, int bind_port)
|
|||||||
void run_stub(int peer_fd)
|
void run_stub(int peer_fd)
|
||||||
{
|
{
|
||||||
osd_any_op_t op;
|
osd_any_op_t op;
|
||||||
osd_any_reply_t reply;
|
osd_any_reply_t reply = { 0 };
|
||||||
void *buf = NULL;
|
void *buf = NULL;
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
@@ -139,7 +135,7 @@ void run_stub(int peer_fd)
|
|||||||
buf = malloc(op.sec_rw.len);
|
buf = malloc(op.sec_rw.len);
|
||||||
r = write_blocking(peer_fd, reply.buf, OSD_PACKET_SIZE);
|
r = write_blocking(peer_fd, reply.buf, OSD_PACKET_SIZE);
|
||||||
if (r == OSD_PACKET_SIZE)
|
if (r == OSD_PACKET_SIZE)
|
||||||
r = write_blocking(peer_fd, &buf, op.sec_rw.len);
|
r = write_blocking(peer_fd, buf, op.sec_rw.len);
|
||||||
free(buf);
|
free(buf);
|
||||||
if (r < op.sec_rw.len)
|
if (r < op.sec_rw.len)
|
||||||
break;
|
break;
|
||||||
|
@@ -20,11 +20,12 @@
|
|||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "addr_util.h"
|
||||||
#include "ringloop.h"
|
#include "ringloop.h"
|
||||||
#include "epoll_manager.h"
|
#include "epoll_manager.h"
|
||||||
#include "messenger.h"
|
#include "messenger.h"
|
||||||
|
|
||||||
int bind_stub(const char *bind_address, int bind_port);
|
int bind_stub(std::string bind_address, int bind_port);
|
||||||
|
|
||||||
void stub_exec_op(osd_messenger_t *msgr, osd_op_t *op);
|
void stub_exec_op(osd_messenger_t *msgr, osd_op_t *op);
|
||||||
|
|
||||||
@@ -39,6 +40,8 @@ int main(int narg, char *args[])
|
|||||||
msgr->ringloop = ringloop;
|
msgr->ringloop = ringloop;
|
||||||
msgr->repeer_pgs = [](osd_num_t) {};
|
msgr->repeer_pgs = [](osd_num_t) {};
|
||||||
msgr->exec_op = [msgr](osd_op_t *op) { stub_exec_op(msgr, op); };
|
msgr->exec_op = [msgr](osd_op_t *op) { stub_exec_op(msgr, op); };
|
||||||
|
json11::Json config = json11::Json::object { { "log_level", 1 } };
|
||||||
|
msgr->parse_config(config);
|
||||||
// Accept new connections
|
// Accept new connections
|
||||||
int listen_fd = bind_stub("0.0.0.0", 11203);
|
int listen_fd = bind_stub("0.0.0.0", 11203);
|
||||||
epmgr->set_fd_handler(listen_fd, false, [listen_fd, msgr](int fd, int events)
|
epmgr->set_fd_handler(listen_fd, false, [listen_fd, msgr](int fd, int events)
|
||||||
@@ -64,11 +67,17 @@ int main(int narg, char *args[])
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int bind_stub(const char *bind_address, int bind_port)
|
int bind_stub(std::string bind_address, int bind_port)
|
||||||
{
|
{
|
||||||
int listen_backlog = 128;
|
int listen_backlog = 128;
|
||||||
|
|
||||||
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
|
sockaddr addr;
|
||||||
|
if (!string_to_addr(bind_address, 0, bind_port, &addr))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("bind address "+bind_address+" is not valid");
|
||||||
|
}
|
||||||
|
|
||||||
|
int listen_fd = socket(addr.sa_family, SOCK_STREAM, 0);
|
||||||
if (listen_fd < 0)
|
if (listen_fd < 0)
|
||||||
{
|
{
|
||||||
throw std::runtime_error(std::string("socket: ") + strerror(errno));
|
throw std::runtime_error(std::string("socket: ") + strerror(errno));
|
||||||
@@ -76,17 +85,7 @@ int bind_stub(const char *bind_address, int bind_port)
|
|||||||
int enable = 1;
|
int enable = 1;
|
||||||
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
|
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
|
||||||
|
|
||||||
sockaddr_in addr;
|
if (bind(listen_fd, &addr, sizeof(addr)) < 0)
|
||||||
int r;
|
|
||||||
if ((r = inet_pton(AF_INET, bind_address, &addr.sin_addr)) != 1)
|
|
||||||
{
|
|
||||||
close(listen_fd);
|
|
||||||
throw std::runtime_error("bind address "+std::string(bind_address)+(r == 0 ? " is not valid" : ": no ipv4 support"));
|
|
||||||
}
|
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
addr.sin_port = htons(bind_port);
|
|
||||||
|
|
||||||
if (bind(listen_fd, (sockaddr*)&addr, sizeof(addr)) < 0)
|
|
||||||
{
|
{
|
||||||
close(listen_fd);
|
close(listen_fd);
|
||||||
throw std::runtime_error(std::string("bind: ") + strerror(errno));
|
throw std::runtime_error(std::string("bind: ") + strerror(errno));
|
||||||
@@ -111,8 +110,10 @@ void stub_exec_op(osd_messenger_t *msgr, osd_op_t *op)
|
|||||||
if (op->req.hdr.opcode == OSD_OP_SEC_READ)
|
if (op->req.hdr.opcode == OSD_OP_SEC_READ)
|
||||||
{
|
{
|
||||||
op->reply.hdr.retval = op->req.sec_rw.len;
|
op->reply.hdr.retval = op->req.sec_rw.len;
|
||||||
op->buf = malloc(op->req.sec_rw.len);
|
op->buf = memalign_or_die(MEM_ALIGNMENT, op->req.sec_rw.len);
|
||||||
op->iov.push_back(op->buf, op->req.sec_rw.len);
|
op->iov.push_back(op->buf, op->req.sec_rw.len);
|
||||||
|
op->reply.sec_rw.attr_len = 4;
|
||||||
|
op->bitmap = op->buf;
|
||||||
}
|
}
|
||||||
else if (op->req.hdr.opcode == OSD_OP_SEC_WRITE || op->req.hdr.opcode == OSD_OP_SEC_WRITE_STABLE)
|
else if (op->req.hdr.opcode == OSD_OP_SEC_WRITE || op->req.hdr.opcode == OSD_OP_SEC_WRITE_STABLE)
|
||||||
{
|
{
|
||||||
|
@@ -6,7 +6,7 @@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
|||||||
|
|
||||||
Name: Vitastor
|
Name: Vitastor
|
||||||
Description: Vitastor client library
|
Description: Vitastor client library
|
||||||
Version: 0.6.9
|
Version: 0.6.11
|
||||||
Libs: -L${libdir} -lvitastor_client
|
Libs: -L${libdir} -lvitastor_client
|
||||||
Cflags: -I${includedir}
|
Cflags: -I${includedir}
|
||||||
|
|
||||||
|
@@ -21,6 +21,7 @@ cd `dirname $0`/..
|
|||||||
trap 'kill -9 $(jobs -p)' EXIT
|
trap 'kill -9 $(jobs -p)' EXIT
|
||||||
|
|
||||||
ETCD=${ETCD:-etcd}
|
ETCD=${ETCD:-etcd}
|
||||||
|
ETCD_IP=${ETCD_IP:-127.0.0.1}
|
||||||
ETCD_PORT=${ETCD_PORT:-12379}
|
ETCD_PORT=${ETCD_PORT:-12379}
|
||||||
|
|
||||||
if [ "$KEEP_DATA" = "" ]; then
|
if [ "$KEEP_DATA" = "" ]; then
|
||||||
@@ -29,11 +30,11 @@ if [ "$KEEP_DATA" = "" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
$ETCD -name etcd_test --data-dir ./testdata/etcd \
|
$ETCD -name etcd_test --data-dir ./testdata/etcd \
|
||||||
--advertise-client-urls http://127.0.0.1:$ETCD_PORT --listen-client-urls http://127.0.0.1:$ETCD_PORT \
|
--advertise-client-urls http://$ETCD_IP:$ETCD_PORT --listen-client-urls http://$ETCD_IP:$ETCD_PORT \
|
||||||
--initial-advertise-peer-urls http://127.0.0.1:$((ETCD_PORT+1)) --listen-peer-urls http://127.0.0.1:$((ETCD_PORT+1)) \
|
--initial-advertise-peer-urls http://$ETCD_IP:$((ETCD_PORT+1)) --listen-peer-urls http://$ETCD_IP:$((ETCD_PORT+1)) \
|
||||||
--max-txn-ops=100000 --auto-compaction-retention=10 --auto-compaction-mode=revision &>./testdata/etcd.log &
|
--max-txn-ops=100000 --auto-compaction-retention=10 --auto-compaction-mode=revision &>./testdata/etcd.log &
|
||||||
ETCD_PID=$!
|
ETCD_PID=$!
|
||||||
ETCD_URL=127.0.0.1:$ETCD_PORT/v3
|
ETCD_URL=$ETCD_IP:$ETCD_PORT/v3
|
||||||
ETCDCTL="${ETCD}ctl --endpoints=http://$ETCD_URL"
|
ETCDCTL="${ETCD}ctl --endpoints=http://$ETCD_URL"
|
||||||
|
|
||||||
echo leak:fio >> testdata/lsan-suppress.txt
|
echo leak:fio >> testdata/lsan-suppress.txt
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
OSD_SIZE=${OSD_SIZE:-1024}
|
OSD_SIZE=${OSD_SIZE:-1024}
|
||||||
PG_COUNT=${PG_COUNT:-1}
|
PG_COUNT=${PG_COUNT:-1}
|
||||||
PG_SIZE=${PG_SIZE:-3}
|
PG_SIZE=${PG_SIZE:-3}
|
||||||
|
PG_MINSIZE=${PG_MINSIZE:-2}
|
||||||
OSD_COUNT=${OSD_COUNT:-3}
|
OSD_COUNT=${OSD_COUNT:-3}
|
||||||
SCHEME=${SCHEME:-ec}
|
SCHEME=${SCHEME:-ec}
|
||||||
|
|
||||||
@@ -25,9 +26,9 @@ if [ -n "$GLOBAL_CONF" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$SCHEME" = "replicated" ]; then
|
if [ "$SCHEME" = "replicated" ]; then
|
||||||
$ETCDCTL put /vitastor/config/pools '{"1":{"name":"testpool","scheme":"replicated","pg_size":'$PG_SIZE',"pg_minsize":'$((PG_SIZE-1))',"pg_count":'$PG_COUNT',"failure_domain":"osd"}}'
|
$ETCDCTL put /vitastor/config/pools '{"1":{"name":"testpool","scheme":"replicated","pg_size":'$PG_SIZE',"pg_minsize":'$PG_MINSIZE',"pg_count":'$PG_COUNT',"failure_domain":"osd"}}'
|
||||||
else
|
else
|
||||||
$ETCDCTL put /vitastor/config/pools '{"1":{"name":"testpool","scheme":"xor","pg_size":'$PG_SIZE',"pg_minsize":'$((PG_SIZE-1))',"parity_chunks":1,"pg_count":'$PG_COUNT',"failure_domain":"osd"}}'
|
$ETCDCTL put /vitastor/config/pools '{"1":{"name":"testpool","scheme":"xor","pg_size":'$PG_SIZE',"pg_minsize":'$PG_MINSIZE',"parity_chunks":1,"pg_count":'$PG_COUNT',"failure_domain":"osd"}}'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
sleep 2
|
sleep 2
|
||||||
|
17
tests/test_minsize_1.sh
Executable file
17
tests/test_minsize_1.sh
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash -ex
|
||||||
|
|
||||||
|
PG_MINSIZE=1
|
||||||
|
SCHEME=replicated
|
||||||
|
|
||||||
|
. `dirname $0`/run_3osds.sh
|
||||||
|
|
||||||
|
kill -INT $OSD1_PID
|
||||||
|
kill -INT $OSD2_PID
|
||||||
|
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
if ! ($ETCDCTL get /vitastor/pg/state/1/ --prefix --print-value-only | jq -s -e '[ .[] | select(.state == ["active", "degraded"]) ] | length == '$PG_COUNT); then
|
||||||
|
format_error "FAILED: $PG_COUNT PG(s) NOT ACTIVE+DEGRADED"
|
||||||
|
fi
|
||||||
|
|
||||||
|
format_green OK
|
Reference in New Issue
Block a user