Compare commits

..

28 Commits

Author SHA1 Message Date
8124ee9eec WIP vitastor-cli create-user 2026-03-20 21:00:32 +03:00
ccbde3ff91 Duplicate all data in /index/image/ and support reading it from there 2026-03-20 21:00:32 +03:00
3a0455877a Add security parameter documentation 2026-03-20 21:00:32 +03:00
6c2c7f9d0f Support inline (string PEM) certificates and pkeys 2026-03-20 21:00:32 +03:00
11618da189 Show encryption keys (only IDs) in the listing 2026-03-20 21:00:32 +03:00
9d2a859760 Support storing image encryption keys in Vault 2026-03-20 21:00:32 +03:00
76042d9ea8 Prefer local etcd addresses and correctly cycle over them even when they need resolving
Seems slightly overcomplicated...
2026-03-20 21:00:32 +03:00
96473f15f9 Support DNS resolving via libc-ares 2026-03-20 21:00:32 +03:00
7207d0fa75 Batch handle_immediate_ops more 2026-03-20 21:00:32 +03:00
0fb58e3c43 Add vitastor-cli create & modify --enc-key parameter 2026-03-20 21:00:32 +03:00
de7edfd31d Support reading from snapshots encrypted with different keys 2026-03-20 21:00:32 +03:00
e6c3f4c6f4 Support decryption with multiple keys 2026-03-20 21:00:32 +03:00
b4d172be35 Allow to return chain_info in response to reads 2026-03-20 21:00:32 +03:00
a2debfca68 Add basic AES-XTS client-side encryption support 2026-03-20 21:00:32 +03:00
4c3c43f8ac Rework msgr send/receive to allow encryption support 2026-03-20 21:00:32 +03:00
6638889126 Move fromhexstr() to str_util 2026-03-20 20:58:58 +03:00
7fb5426ae9 Add openapi description 2026-03-20 20:58:58 +03:00
b4af7409af Slightly fix API return and input types 2026-03-20 20:58:58 +03:00
645b1f3081 Implement vitastor-cli serve command to serve simple HTTP API 2026-03-20 20:58:58 +03:00
9a21a0ca65 Implement HTTP server support O_o 2026-03-20 20:58:58 +03:00
28e4610d25 Rename http_response_t to http_message_t 2026-03-20 20:58:58 +03:00
2ddefa539f Extract common HTTP context 2026-03-20 20:58:57 +03:00
3aa307f034 Support xxhash 32-bit checksums (data_csum_type=xxh3_32) 2026-03-20 20:58:57 +03:00
3ed7a0bc5e Detect block checksums using csum_block_size, not data_csum_type 2026-03-20 20:58:57 +03:00
ab77f487e3 Add client certificate support 2026-03-20 20:58:57 +03:00
ba9be46acd Do not re-initialize TLS context every connection 2026-03-20 20:58:57 +03:00
04c7d61ee9 Add https support to antietcd 2026-03-20 20:58:57 +03:00
d0ae63eb50 Implement etcd SSL support via OpenSSL
Maybe I should remove all of this and use libwebsockets :)
2026-03-20 20:58:57 +03:00
295 changed files with 6244 additions and 23680 deletions

View File

@@ -23,7 +23,7 @@ RUN apt-get update
RUN apt-get -y install etcd qemu-system-x86 qemu-block-extra qemu-utils fio libasan8 \
libgoogle-perftools-dev devscripts libjerasure-dev cmake libibverbs-dev libisal-dev
RUN apt-get -y build-dep fio qemu=`dpkg -s qemu-system-x86|grep ^Version:|awk '{print $2}'`
RUN apt-get update && apt-get -y install jq lp-solve sudo nfs-common fdisk parted libc-ares-dev udev iproute2
RUN apt-get update && apt-get -y install jq lp-solve sudo nfs-common fdisk parted libc-ares-dev udev
RUN apt-get --download-only source fio qemu=`dpkg -s qemu-system-x86|grep ^Version:|awk '{print $2}'`
RUN set -ex; \

View File

@@ -63,7 +63,7 @@ jobs:
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
# leak sanitizer sometimes crashes
- run: cd /root/vitastor/build && ASAN_OPTIONS=detect_leaks=0 make -j16 build_tests test
- run: cd /root/vitastor/build && ASAN_OPTIONS=detect_leaks=0 make -j16 test
npm_lint:
runs-on: ubuntu-latest
@@ -162,24 +162,6 @@ jobs:
echo ""
done
test_change_pg_minsize:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: /root/vitastor/tests/test_change_pg_minsize.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_change_pg_size:
runs-on: ubuntu-latest
needs: build
@@ -306,24 +288,6 @@ jobs:
echo ""
done
test_img_online_resize:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: /root/vitastor/tests/test_img_online_resize.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_interrupted_rebalance:
runs-on: ubuntu-latest
needs: build
@@ -396,78 +360,6 @@ jobs:
echo ""
done
test_dump_load:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: /root/vitastor/tests/test_dump_load.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_dump_load_32k:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: TEST_NAME=32k OSD_ARGS="--data_csum_type crc32c --csum_block_size 32k" OFFSET_ARGS="$OSD_ARGS" /root/vitastor/tests/test_dump_load.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_dump_load:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 /root/vitastor/tests/test_dump_load.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_dump_load_old_32k:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: TEST_NAME=old_32k OLD=1 OSD_ARGS="--data_csum_type crc32c --csum_block_size 32k" OFFSET_ARGS="$OSD_ARGS" /root/vitastor/tests/test_dump_load.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_interrupted_rebalance:
runs-on: ubuntu-latest
needs: build
@@ -828,24 +720,6 @@ jobs:
echo ""
done
test_snapshot_chain_enc_gcm:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: TEST_NAME=enc_gcm ENCRYPTED=1 VITASTOR_CFG=',"proto_checksums":"gcm"' /root/vitastor/tests/test_snapshot_chain.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_snapshot_chain:
runs-on: ubuntu-latest
needs: build
@@ -1404,24 +1278,6 @@ jobs:
echo ""
done
test_heal_ec_rdma:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 10
run: TEST_NAME=ec_rdma RDMA=1 SCHEME=ec /root/vitastor/tests/test_heal.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_checksum:
runs-on: ubuntu-latest
needs: build
@@ -1692,24 +1548,6 @@ jobs:
echo ""
done
test_resize_last:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: /root/vitastor/tests/test_resize_last.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_resize_auto:
runs-on: ubuntu-latest
needs: build
@@ -1746,24 +1584,6 @@ jobs:
echo ""
done
test_old_resize_last:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: OLD=1 /root/vitastor/tests/test_resize_last.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_resize_auto:
runs-on: ubuntu-latest
needs: build
@@ -2322,24 +2142,6 @@ jobs:
echo ""
done
test_nfs_long_readdir:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: /root/vitastor/tests/test_nfs_long_readdir.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_write_encrypted:
runs-on: ubuntu-latest
needs: build

View File

@@ -1,20 +1,20 @@
cmake_minimum_required(VERSION 2.8...3.30)
cmake_minimum_required(VERSION 2.8.12)
project(vitastor)
set(VITASTOR_VERSION "3.1.0")
set(VITASTOR_VERSION "3.0.5")
include(CTest)
add_custom_target(build_tests)
set_property(TEST PROPERTY ENVIRONMENT LSAN_OPTIONS=suppressions=${CMAKE_CURRENT_BINARY_DIR}/lsan-suppress.txt)
add_test(gen_lsan_suppress
${CMAKE_COMMAND} -E echo leak:tcmalloc > "${CMAKE_CURRENT_BINARY_DIR}/lsan-suppress.txt"
add_custom_target(test
COMMAND
echo leak:tcmalloc > ${CMAKE_CURRENT_BINARY_DIR}/lsan-suppress.txt &&
env LSAN_OPTIONS=suppressions=${CMAKE_CURRENT_BINARY_DIR}/lsan-suppress.txt ${CMAKE_CTEST_COMMAND}
)
set_tests_properties(gen_lsan_suppress PROPERTIES FIXTURES_SETUP f_lsan_suppress)
set_property(TEST PROPERTY FIXTURES_REQUIRED f_lsan_suppress)
# make -j16 -C ../../build test_heap && ../../build/src/test/test_heap
# make -j16 -C ../../build test_heap && rm -f $(find ../../build -name '*.gcda') && ctest -V -T test -T coverage -R heap --test-dir ../../build && (cd ../../build; gcovr -f ../src --html --html-nested -o coverage/index.html; cd ../src/test)
# make -j16 -C ../../build test_blockstore && rm -f $(find ../../build -name '*.gcda') && ctest -V -T test -T coverage -R blockstore --test-dir ../../build && (cd ../../build; gcovr -f ../src --html --html-nested -o coverage/index.html; cd ../src/test)
# kcov --include-path=../../../src ../../kcov ./test_blockstore
add_dependencies(test build_tests)
add_subdirectory(src)

View File

@@ -73,7 +73,6 @@ Vitastor поддерживает QEMU-драйвер, протоколы UBLK,
- [NBD](docs/usage/nbd.ru.md) - старый интерфейс для монтирования ядром
- [QEMU, qemu-img и VDUSE](docs/usage/qemu.ru.md)
- [NFS](docs/usage/nfs.ru.md) кластерная файловая система и псевдо-ФС прокси
- [Безопасность](docs/usage/security.ru.md)
- [Администрирование](docs/usage/admin.ru.md)
- Производительность
- [Понимание сути производительности](docs/performance/understanding.ru.md)

View File

@@ -73,7 +73,6 @@ Read more details in the documentation. You can start from here: [Quick Start](d
- [NBD](docs/usage/nbd.en.md) - old interface for kernel mounts
- [QEMU, qemu-img and VDUSE](docs/usage/qemu.en.md)
- [NFS](docs/usage/nfs.en.md) clustered file system and pseudo-FS proxy
- [Security](docs/usage/security.en.md)
- [Administration](docs/usage/admin.en.md)
- Performance
- [Understanding storage performance](docs/performance/understanding.en.md)

View File

@@ -1,4 +1,4 @@
VITASTOR_VERSION ?= v3.1.0
VITASTOR_VERSION ?= v3.0.5
all: build push

View File

@@ -49,7 +49,7 @@ spec:
capabilities:
add: ["SYS_ADMIN"]
allowPrivilegeEscalation: true
image: vitalif/vitastor-csi:v3.1.0
image: vitalif/vitastor-csi:v3.0.5
args:
- "--node=$(NODE_ID)"
- "--endpoint=$(CSI_ENDPOINT)"

View File

@@ -121,7 +121,7 @@ spec:
privileged: true
capabilities:
add: ["SYS_ADMIN"]
image: vitalif/vitastor-csi:v3.1.0
image: vitalif/vitastor-csi:v3.0.5
args:
- "--node=$(NODE_ID)"
- "--endpoint=$(CSI_ENDPOINT)"

View File

@@ -5,7 +5,7 @@ package vitastor
const (
vitastorCSIDriverName = "csi.vitastor.io"
vitastorCSIDriverVersion = "3.1.0"
vitastorCSIDriverVersion = "3.0.5"
)
// Config struct fills the parameters of request or user input

View File

@@ -27,6 +27,7 @@ const (
MB int64 = 1024 * KB
GB int64 = 1024 * MB
TB int64 = 1024 * GB
ETCD_TIMEOUT time.Duration = 15*time.Second
)
type InodeIndex struct
@@ -686,7 +687,7 @@ func (cs *ControllerServer) ControllerExpandVolume(ctx context.Context, req *csi
return &csi.ControllerExpandVolumeResponse{
CapacityBytes: int64(inodeCfg[0].Size),
NodeExpansionRequired: true,
NodeExpansionRequired: false,
}, nil
}

View File

@@ -754,7 +754,7 @@ func (ns *NodeServer) mountNFS(ctxVars map[string]string) (string, error)
// Mount NFS
_, _, err = system(
"mount", "-t", "nfs", "127.0.0.1:/", state.Path,
"-o", fmt.Sprintf("port=%d,mountport=%d,nfsvers=3,soft,sync,nolock,tcp", port, port),
"-o", fmt.Sprintf("port=%d,mountport=%d,nfsvers=3,soft,nolock,tcp", port, port),
)
if (err != nil)
{
@@ -1030,131 +1030,10 @@ func (ns *NodeServer) NodeGetVolumeStats(ctx context.Context, req *csi.NodeGetVo
return nil, status.Error(codes.Unimplemented, "")
}
// NodeExpandVolume expands the block device and, for filesystem volumes, the filesystem on the node
// NodeExpandVolume expanding the file system on the node
func (ns *NodeServer) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandVolumeRequest) (*csi.NodeExpandVolumeResponse, error)
{
klog.Infof("received node expand volume request %+v", protosanitizer.StripSecrets(req))
if (req == nil)
{
return nil, status.Error(codes.InvalidArgument, "request cannot be empty")
}
if (req.VolumeId == "")
{
return nil, status.Error(codes.InvalidArgument, "VolumeId is required")
}
ctxVars := make(map[string]string)
err := json.Unmarshal([]byte(req.VolumeId), &ctxVars)
if (err != nil)
{
return nil, status.Error(codes.Internal, "volume ID not in JSON format")
}
volName := ctxVars["name"]
if (ctxVars["vitastorfs"] != "")
{
// VitastorFS CSI volumes are NFS-backed and currently don't have per-volume quotas to resize here.
return &csi.NodeExpandVolumeResponse{CapacityBytes: req.GetCapacityRange().GetRequiredBytes()}, nil
}
ns.lockVolume(ctxVars["configPath"]+":block:"+volName)
defer ns.unlockVolume(ctxVars["configPath"]+":block:"+volName)
targetPath := req.GetStagingTargetPath()
if (targetPath == "")
{
targetPath = req.GetVolumePath()
}
if (targetPath == "")
{
return nil, status.Error(codes.InvalidArgument, "VolumePath or StagingTargetPath is required")
}
devicePath, err := GetDeviceNameFromMount(targetPath)
if (err != nil)
{
if (os.IsNotExist(err))
{
return nil, status.Error(codes.NotFound, "Target path not found")
}
return nil, err
}
if (devicePath == "")
{
return nil, status.Errorf(codes.NotFound, "%s is not a mountpoint", targetPath)
}
if (devicePath[0] != '/')
{
return nil, status.Errorf(codes.Internal, "failed to resolve device for %s: %s", targetPath, devicePath)
}
if (len(devicePath) < 5 || devicePath[0:5] != "/dev/")
{
// NodeExpandVolume may be called with the published target path. For block volumes it is
// usually a bind mount of the staging target, so resolve one more mount layer if needed.
var realDevicePath string
realDevicePath, err = GetDeviceNameFromMount(devicePath)
if (err == nil && realDevicePath != "")
{
devicePath = realDevicePath
}
}
// All newest Vitastor driver versions (nbd, ublk and vduse) support online resize.
// So all we need to do is just wait a bit until the device is resized by the driver.
requiredBytes := req.GetCapacityRange().GetRequiredBytes()
if (requiredBytes > 0)
{
timeout := 30
var actualSize uint64
var err error
for i := 0; i < timeout && actualSize < requiredBytes; i++
{
actualSize, err = GetBlockDeviceSize(devicePath)
if (err != nil)
{
return nil, err
}
time.Sleep(1 * time.Second)
}
if (actualSize < requiredBytes)
{
return nil, status.Error(codes.NotFound, "Failed to wait for online resize of %s to %v bytes: actual size is still %v after %v seconds",
devicePath, requiredBytes, actualSize, timeout)
}
}
isBlock := req.GetVolumeCapability().GetBlock() != nil
if (isBlock)
{
return &csi.NodeExpandVolumeResponse{CapacityBytes: requiredBytes}, nil
}
diskMounter := &mount.SafeFormatAndMount{Interface: ns.mounter, Exec: utilexec.New()}
fsType := req.GetVolumeCapability().GetMount().GetFsType()
if (fsType == "")
{
fsType, err = diskMounter.GetDiskFormat(devicePath)
if (err != nil)
{
return nil, err
}
}
switch (fsType)
{
case "ext4":
_, err = systemCombined("resize2fs", devicePath)
case "xfs":
_, err = systemCombined("xfs_growfs", targetPath)
default:
return nil, status.Errorf(codes.InvalidArgument, "unsupported filesystem for resize: %s", fsType)
}
if (err != nil)
{
return nil, err
}
return &csi.NodeExpandVolumeResponse{CapacityBytes: requiredBytes}, nil
return nil, status.Error(codes.Unimplemented, "")
}
// NodeGetCapabilities returns the supported capabilities of the node server
@@ -1169,13 +1048,6 @@ func (ns *NodeServer) NodeGetCapabilities(ctx context.Context, req *csi.NodeGetC
},
},
},
&csi.NodeServiceCapability{
Type: &csi.NodeServiceCapability_Rpc{
Rpc: &csi.NodeServiceCapability_RPC{
Type: csi.NodeServiceCapability_RPC_EXPAND_VOLUME,
},
},
},
},
}, nil
}

View File

@@ -20,7 +20,6 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"golang.org/x/sys/unix"
)
type MountMethod int
@@ -520,22 +519,3 @@ func GetDeviceNameFromMount(mountPath string) (string, error)
return device, nil
}
func GetBlockDeviceSize(devicePath string) (uint64, error)
{
file, err := os.Open(devicePath)
if (err != nil)
{
return 0, err
}
defer file.Close()
// BLKGETSIZE64 returns the size in bytes as a uint64/int64
size, err := unix.IoctlGetInt(int(file.Fd()), unix.BLKGETSIZE64)
if (err != nil)
{
return 0, fmt.Errorf("ioctl BLKGETSIZE64 failed: %w", err)
}
return uint64(size), nil
}

2
debian/changelog vendored
View File

@@ -1,4 +1,4 @@
vitastor (3.1.0-1) unstable; urgency=medium
vitastor (3.0.5-1) unstable; urgency=medium
* Bugfixes

1
debian/rules vendored
View File

@@ -11,7 +11,6 @@ override_dh_install:
cp -v node-binding/package.json node-binding/index.js node-binding/addon.cc node-binding/addon.h node-binding/client.cc node-binding/client.h debian/tmp/usr/lib/x86_64-linux-gnu/nodejs/vitastor
cp -v node-binding/build/Release/addon.node debian/tmp/usr/lib/x86_64-linux-gnu/nodejs/vitastor/build/Release
dh_install
cd debian/vitastor-mon/usr/lib/vitastor/mon && npm install --production
override_dh_installdeb:
cat debian/fio_version >> debian/vitastor-fio.substvars

View File

@@ -37,8 +37,14 @@ rm -rf a b
echo "dep:fio=$FIO" > debian/fio_version
cd /root/vitastor/packages/vitastor-$REL/vitastor-$VER
mkdir mon/node_modules
cd mon/node_modules
curl -s https://git.yourcmc.ru/vitalif/antietcd/archive/master.tar.gz | tar -zx
curl -s https://git.yourcmc.ru/vitalif/tinyraft/archive/master.tar.gz | tar -zx
cd /root/vitastor/packages/vitastor-$REL
if [[ ( "$REL" = "trixie" || "$REL" = "resolute" ) && -e ../vitastor-bookworm/vitastor_$VER.orig.tar.xz ]]; then
if [[ "$REL" = "trixie" && -e ../vitastor-bookworm/vitastor_$VER.orig.tar.xz ]]; then
# Fucking shit, archives differ between bookworm (xz 5.4.1) and trixie (xz 5.8.1)
cp ../vitastor-bookworm/vitastor_$VER.orig.tar.xz .
else

View File

@@ -14,18 +14,18 @@ WORKDIR /root
RUN set -e -x; \
if [ "$REL" = "buster" ]; then \
perl -i -pe 's/deb.debian.org/archive.debian.org/' /etc/apt/sources.list; \
apt-get update; \
apt-get -y install wget; \
wget https://vitastor.io/debian/pubkey.gpg -O /etc/apt/trusted.gpg.d/vitastor.gpg; \
echo "deb https://vitastor.io/debian $REL main" >> /etc/apt/sources.list; \
fi; \
apt-get update; \
apt-get -y install wget; \
wget https://vitastor.io/debian/pubkey.gpg -O /etc/apt/trusted.gpg.d/vitastor.gpg; \
echo "deb https://vitastor.io/debian $REL main" >> /etc/apt/sources.list; \
grep '^deb ' /etc/apt/sources.list | perl -pe 's/^deb/deb-src/' >> /etc/apt/sources.list; \
perl -i -pe 's/Types: deb$/Types: deb deb-src/' /etc/apt/sources.list.d/*.sources || true; \
echo 'APT::Install-Recommends false;' >> /etc/apt/apt.conf; \
echo 'APT::Install-Suggests false;' >> /etc/apt/apt.conf
RUN apt-get update && \
apt-get -y install fio libgoogle-perftools-dev devscripts libjerasure-dev cmake libc-ares-dev libisal-crypto-dev \
libibverbs-dev librdmacm-dev libisal-dev libnl-3-dev libnl-genl-3-dev libssl-dev curl nodejs npm node-nan node-bindings && \
apt-get -y install fio libgoogle-perftools-dev devscripts libjerasure-dev cmake libc-ares-dev \
libibverbs-dev librdmacm-dev libisal-dev libnl-3-dev libnl-genl-3-dev curl nodejs npm node-nan node-bindings && \
apt-get -y build-dep fio && \
apt-get --download-only source fio

View File

@@ -4,8 +4,6 @@ usr/bin/vitastor-rm
usr/bin/vitastor-nbd
usr/bin/vitastor-ublk
usr/bin/vitastor-nfs
usr/sbin/mount.vitastorfs
usr/sbin/mount.vitastorblk
usr/bin/vitastor-kv
usr/bin/vitastor-kv-stress
usr/lib/*/libvitastor*.so*

View File

@@ -1,4 +1,4 @@
VITASTOR_VERSION ?= v3.1.0
VITASTOR_VERSION ?= v3.0.5
all: build push

View File

@@ -12,7 +12,12 @@ EnvironmentFile=/etc/vitastor/etcd.conf
SyslogIdentifier=etcd
ExecStart=bash -c 'docker run --rm -i -v /var/lib/vitastor/etcd:/data \
--log-driver none --network host $CONTAINER_OPTIONS --name vitastor-etcd \
$ETCD_IMAGE /usr/local/bin/etcd --data-dir /data
$ETCD_IMAGE /usr/local/bin/etcd --name "$ETCD_NAME" --data-dir /data \
--snapshot-count 10000 --advertise-client-urls http://$ETCD_IP:2379 --listen-client-urls http://$ETCD_IP:2379 \
--initial-advertise-peer-urls http://$ETCD_IP:2380 --listen-peer-urls http://$ETCD_IP:2380 \
--initial-cluster-token vitastor-etcd-1 --initial-cluster "$ETCD_INITIAL_CLUSTER" \
--initial-cluster-state new --max-txn-ops=100000 --max-request-bytes=104857600 \
--auto-compaction-retention=10 --auto-compaction-mode=revision'
ExecStop=docker stop vitastor-etcd
Restart=always
StartLimitInterval=0

View File

@@ -4,7 +4,7 @@
#
# Desired Vitastor version
VITASTOR_VERSION=v3.1.0
VITASTOR_VERSION=v3.0.5
# Additional arguments for all containers
# For example, you may want to specify a custom logging driver here

View File

@@ -1 +1,4 @@
ETCD_IMAGE=quay.io/coreos/etcd:v3.5.18
ETCD_NAME=""
ETCD_IP=""
ETCD_INITIAL_CLUSTER=""

View File

@@ -50,9 +50,6 @@ or antietcd_data_dir options). All other antietcd parameters
cluster, cluster_key, persist_filter, stale_read can also be set in
Vitastor configuration with `antietcd_` prefix.
See also: [antietcd_cert](security.en.md#antietcd_cert),
[antietcd_key](security.en.md#antietcd_key) and [etcd_proxy](security.en.md#etcd_proxyurls).
You can dump/load data to or from antietcd using Antietcd `anticli` tool:
```

View File

@@ -50,9 +50,6 @@ antietcd_data_file или antietcd_data_dir). Все остальные пара
node_id, cluster, cluster_key, persist_filter, stale_read также можно задавать
в конфигурации Vitastor с префиксом `antietcd_`.
Смотрите также настройки [antietcd_cert](security.ru.md#antietcd_cert),
[antietcd_key](security.ru.md#antietcd_key) и [etcd_proxy](security.ru.md#etcd_proxyurls).
Вы можете выгружать/загружать данные в или из antietcd с помощью его инструмента
`anticli`:

View File

@@ -70,7 +70,6 @@ with an OSD restart or, for some of them, even without restarting by updating co
- [use_atomic_flag](#use_atomic_flag)
- [pg_reshard_chunk_size](#pg_reshard_chunk_size)
- [pg_reshard_chunk_pause_ms](#pg_reshard_chunk_pause_ms)
- [gc_on_start](#gc_on_start)
## bind_address
@@ -754,9 +753,3 @@ This option sets the maximum number of object is a chunk. Moving 100k objects us
- Default: 100
This option sets the interval between handling two PG count change chunks.
## gc_on_start
- Type: boolean
Forcibly clean all garbage entries in the new store on every OSD restart.

View File

@@ -71,7 +71,6 @@
- [use_atomic_flag](#use_atomic_flag)
- [pg_reshard_chunk_size](#pg_reshard_chunk_size)
- [pg_reshard_chunk_pause_ms](#pg_reshard_chunk_pause_ms)
- [gc_on_start](#gc_on_start)
## bind_address
@@ -794,9 +793,3 @@ pg_minsize OSD во время переключений, что может по
- Значение по умолчанию: 100
Данная опция задаёт интервал между обработкой двух порций изменения числа PG пулов.
## gc_on_start
- Тип: булево (да/нет)
Принудительно очищать все мусорные записи в новом хранилище при каждом запуске OSD.

View File

@@ -10,37 +10,13 @@ These parameters affect your Vitastor installation security and apply to OSDs, m
Most of them can be set in /etc/vitastor/vitastor.conf and in etcd, but don't support online modification.
All certificate and private key parameters mentioned may contain a path to a PEM file or just
a PEM string with certificate or a private key. In the latter case, the string must begin with
"-----BEGIN CERTIFICATE-----" or "-----BEGIN PRIVATE KEY-----".
- [use_perms](#use_perms)
- [cert](#cert)
- [pkey](#pkey)
- [etcd_ca](#etcd_ca)
- [client_ca](#client_ca)
- [admin_ca](#admin_ca)
- [osd_ca](#osd_ca)
- [mon_ca](#mon_ca)
- [antietcd_cert](#antietcd_cert)
- [antietcd_key](#antietcd_key)
- [etcd_proxy.urls](#etcd_proxyurls)
- [etcd_proxy.cert](#etcd_proxycert)
- [etcd_proxy.key](#etcd_proxykey)
- [etcd_proxy.ca](#etcd_proxyca)
- [osd_cert](#osd_cert)
- [osd_pkey](#osd_pkey)
- [api_cert](#api_cert)
- [api_pkey](#api_pkey)
- [etcd_client_cert](#etcd_client_cert)
- [etcd_client_key](#etcd_client_key)
- [etcd_ca](#etcd_ca)
- [osd_etcd_client_cert](#osd_etcd_client_cert)
- [osd_etcd_client_key](#osd_etcd_client_key)
- [mon_etcd_client_cert](#mon_etcd_client_cert)
- [mon_etcd_client_key](#mon_etcd_client_key)
- [proto_checksums](#proto_checksums)
- [force_proto_checksums](#force_proto_checksums)
- [max_cipher_pool_size](#max_cipher_pool_size)
- [vault_url](#vault_url)
- [vault_secret_api_path](#vault_secret_api_path)
- [vault_client_cert](#vault_client_cert)
@@ -49,201 +25,54 @@ a PEM string with certificate or a private key. In the latter case, the string m
- [vault_timeout_ms](#vault_timeout_ms)
- [vault_error_timeout_sec](#vault_error_timeout_sec)
- [vault_refresh_leeway_sec](#vault_refresh_leeway_sec)
## use_perms
- Type: boolean
- Default: false
Enable client permissions in a Vitastor cluster, including Antietcd built into the Monitor.
Requires configured encryption. Also note that separate Antietcd requires separate configuration
to use permissions (see [security documentation](../usage/security.en.md) for details).
## cert
- Type: string
Client certificate of the current Vitastor user. Required for Vitastor protocol encryption.
Must be signed with [client_ca](#client_ca). Also used as the client certificate for etcd/Antietcd
connections by default.
## pkey
- Type: string
Private key of the current Vitastor user.
## etcd_ca
- Type: string
Trusted TLS CA to verify etcd server certificate. Or just the etcd server's
certificate itself - it's fine to use it for etcd_ca.
## client_ca
- Type: string
Trusted TLS CA to verify Vitastor client certificates.
Mandatory for Vitastor protocol encryption.
## admin_ca
- Type: string
Trusted TLS CA to verify Vitastor administrator certificates.
## osd_ca
- Type: string
Trusted TLS CA to verify Vitastor OSD certificates. Also mandatory for Vitastor protocol
encryption. Must be different from client_ca. May be equal to osd_cert - different OSDs
don't require separate certificates at the moment because their permissions don't differ.
## mon_ca
- Type: string
Trusted TLS CA to verify Vitastor Monitor certificates. Used only for separate Antietcd,
not required when a monitor built-in Antietcd is used. May be equal to mon_client_etcd_cert.
## antietcd_cert
- Type: string
Server TLS certificate for Antietcd built into the Monitor.
## antietcd_key
- Type: string
Private key for antietcd_cert.
## etcd_proxy.urls
- Type: string or array of strings
etcd URLs for Antietcd etcd proxy mode.
See [Mon as Etcd proxy](../usage/security.en.md#mon-as-etcd-proxy) for details.
## etcd_proxy.cert
- Type: string
Client certificate for Antietcd connections to etcd in proxy mode.
## etcd_proxy.key
- Type: string
Private key for etcd_proxy.cert.
## etcd_proxy.ca
- Type: string
Trusted TLS CA to verify etcd server certificate when connecting to it from Antietcd.
## osd_cert
- Type: string
Vitastor OSD server certificate. Required for Vitastor protocol encryption. May be equal
to [osd_ca](#osd_ca) - all OSDs share the same permission set for now. Also used as the client
certificate for connections from OSD to etcd/Antietcd by default.
## osd_pkey
- Type: string
Private key for osd_cert.
## api_cert
- Type: string
Server TLS certificate for [vitastor-cli serve](../usage/cli.en.md#serve) API server.
## api_pkey
- Type: string
Private key for api_cert.
- [max_aes_xts_pool_size](#max_aes_xts_pool_size)
## etcd_client_cert
- Type: string
Client TLS certificate to use for connections from Vitastor clients to etcd/Antietcd if you don't want
to use the common client certificate [cert](#cert).
Client TLS certificate to use for Vitastor client (not OSD and not monitor)
etcd https connections. May be path to a file or just a PEM string with certificate.
In the latter case, string must begin with "-----BEGIN CERTIFICATE-----".
## etcd_client_key
- Type: string
Private key for etcd_client_cert.
Private key for etcd_client_cert (also a file or a PEM string).
## etcd_ca
- Type: string
Trusted TLS CA to verify etcd server certificate. May be path to a file,
directory or just a PEM string with certificate.
## osd_etcd_client_cert
- Type: string
Client TLS certificate to use for connections from Vitastor OSDs to etcd/Antietcd if you don't want
to use the common OSD certificate [osd_cert](#osd_cert).
Same as [etcd_client_cert](#etcd_client_cert), but only for OSDs.
OSDs, clients and monitors should have different permissions, so they should
use different certificates.
## osd_etcd_client_key
- Type: string
Private key for osd_etcd_client_cert.
Same as [etcd_client_key](#etcd_client_key), but only for OSDs.
## mon_etcd_client_cert
- Type: string
Client TLS certificate to use for connections from Vitastor Monitors to etcd/Antietcd - required
if you don't use the built-in Antietcd. In case you use it Monitor has direct access to Antietcd data
and doesn't require any connection.
Same as [etcd_client_cert](#etcd_client_cert), but only for Vitastor monitors.
## mon_etcd_client_key
- Type: string
Private key for mon_etcd_client_cert.
## proto_checksums
- Type: string
- Default: payload
One of "full", "payload", "gcm", "none":
- "full" means calculate and verify transport level checksums from the full message data
including the header - recommended for unencrypted setups.
- "payload" enables checksums only for the actual read/write data, but skips them for message
headers - recommended for encrypted setups because headers are already protected by AES-GCM.
- "gcm" disables checksums and enables AES-GCM encryption of the whole messages including headers
and data - AES-GCM already includes MAC which is actually a stronger checksum. This option is
slower and is only recommended for untrusted networks.
- "none" disables transport level checksums at all.
## force_proto_checksums
- Type: string
To allow older clients to connect to a Vitastor cluster with enabled checksums, Vitastor OSDs
allow clients to downgrade their proto_checksums by default. force_proto_checksums sets the
minimum security level allowed for connecting clients. When encryption is disabled, default
force_proto_checksums is none and clients without checksums are allowed. With enabled
encryption, force_proto_checksums becomes "payload" by default to block unauthenticated data
on the transport level.
## max_cipher_pool_size
- Type: integer
- Default: 256
Maximum number of OpenSSL cipher contexts cached in OSD memory, counted separately
for each cipher and for encryption/decryption. Probably doesn't require modification.
Same as [etcd_client_key](#etcd_client_key), but only for Vitastor monitors.
## vault_url
@@ -274,14 +103,14 @@ Vault v1 secret API mount path to use.
- Type: string
Client TLS certificate to use for Vault connections if you don't want to use the common Vitastor
client certificate [cert](#cert) which is also used for Vault connections by default.
Client TLS certificate to use for Vault connections. Just like [etcd_client_cert](#etcd_client_cert),
may be path to a file or just a certificate in PEM string.
## vault_client_key
- Type: string
Private key for the vault_client_cert certificate.
Private key for vault_client_cert (also a file or a PEM string).
## vault_ca
@@ -311,3 +140,11 @@ Time (in seconds) to wait before retrying after receiving an error from Vault.
Extra time (in seconds) before real Vault token lease_timeout to refresh it, just
in case of system clock drift.
## max_aes_xts_pool_size
- Type: integer
- Default: 256
Maximum number of OpenSSL encryption contexts cached in OSD memory. Probably
doesn't require modification.

View File

@@ -12,37 +12,13 @@ OSD, мониторами и клиентами.
Большая их часть может задаваться в /etc/vitastor/vitastor.conf и в etcd, но не
поддерживает онлайн-изменение.
Все параметры сертификатов и закрытых ключей могут быть путём к файлу или просто
строкой с сертификатом в формате PEM. В последнем случае строка должна начинаться с
"-----BEGIN CERTIFICATE-----" или "-----BEGIN PRIVATE KEY-----".
- [use_perms](#use_perms)
- [cert](#cert)
- [pkey](#pkey)
- [etcd_ca](#etcd_ca)
- [client_ca](#client_ca)
- [admin_ca](#admin_ca)
- [osd_ca](#osd_ca)
- [mon_ca](#mon_ca)
- [antietcd_cert](#antietcd_cert)
- [antietcd_key](#antietcd_key)
- [etcd_proxy.urls](#etcd_proxyurls)
- [etcd_proxy.cert](#etcd_proxycert)
- [etcd_proxy.key](#etcd_proxykey)
- [etcd_proxy.ca](#etcd_proxyca)
- [osd_cert](#osd_cert)
- [osd_pkey](#osd_pkey)
- [api_cert](#api_cert)
- [api_pkey](#api_pkey)
- [etcd_client_cert](#etcd_client_cert)
- [etcd_client_key](#etcd_client_key)
- [etcd_ca](#etcd_ca)
- [osd_etcd_client_cert](#osd_etcd_client_cert)
- [osd_etcd_client_key](#osd_etcd_client_key)
- [mon_etcd_client_cert](#mon_etcd_client_cert)
- [mon_etcd_client_key](#mon_etcd_client_key)
- [proto_checksums](#proto_checksums)
- [force_proto_checksums](#force_proto_checksums)
- [max_cipher_pool_size](#max_cipher_pool_size)
- [vault_url](#vault_url)
- [vault_secret_api_path](#vault_secret_api_path)
- [vault_client_cert](#vault_client_cert)
@@ -51,205 +27,56 @@ OSD, мониторами и клиентами.
- [vault_timeout_ms](#vault_timeout_ms)
- [vault_error_timeout_sec](#vault_error_timeout_sec)
- [vault_refresh_leeway_sec](#vault_refresh_leeway_sec)
- [max_aes_xts_pool_size](#max_aes_xts_pool_size)
## use_perms
- Тип: булево (да/нет)
- Значение по умолчанию: false
Включает клиентские привилегии в кластере Vitastor, в том числе во встроенном в мониторе Antietcd.
Требует настроенного шифрования протокола. Также обратите внимание, что отдельно установленный Antietcd
требует отдельной настройки привилегий (подробности смотрите в [документации безопасности](../usage/security.ru.md)).
## cert
## etcd_client_cert
- Тип: строка
Клиентский сертификат текущего пользователя Vitastor. Требуется для шифрования протокола Vitastor.
Должен быть подписан [client_ca](#client_ca). Также по умолчанию используется как клиентский
сертификат для подключения к etcd/Antietcd и Vault.
Клиентский TLS сертификат для https-подключений к etcd для клиентов Vitastor
(не OSD и не мониторов). Может быть путём к файлу или просто строкой с
сертификатом в формате PEM. В последнем случае строка должна начинаться с
"-----BEGIN CERTIFICATE-----".
## pkey
## etcd_client_key
- Тип: строка
Закрытый ключ текущего пользователя Vitastor.
Закрытый ключ для сертификата etcd_client_cert (также путь к файлу или PEM строка).
## etcd_ca
- Тип: строка
Доверенный корневой TLS-сертификат для проверки сертификата сервера etcd.
Либо же просто сам сертификат сервера etcd - его можно использовать как etcd_ca.
## client_ca
- Тип: строка
Доверенный TLS-сертификат для проверки сертификатов клиентов Vitastor.
Требуется для шифрования протокола Vitastor.
## admin_ca
- Тип: строка
Доверенный TLS-сертификат для проверки сертификатов администраторов Vitastor.
## osd_ca
- Тип: строка
Доверенный TLS-сертификат для проверки сертификатов OSD Vitastor. Также обязателен
для шифрования протокола Vitastor. Должен отличаться от client_ca. Может быть равен
osd_cert - разные OSD не требуют разных сертификатов, потому что на данный момент
привилегии разных OSD никак не отличаются.
## mon_ca
- Тип: строка
Доверенный TLS-сертификат для проверки сертификатов мониторов Vitastor. Используется
только отдельно установленным Antietcd, не требуется при использовании встроенного в монитор
Antietcd. Может быть равен mon_client_etcd_cert.
## antietcd_cert
- Тип: строка
Серверный TLS-сертификат для Antietcd, встроенного в монитор.
## antietcd_key
- Тип: строка
Закрытый ключ для сертификата antietcd_cert.
## etcd_proxy.urls
- Тип: строка или массив строк
Адреса etcd для режима Antietcd etcd-прокси.
Смотрите подробности в разделе [Mon в роли Etcd proxy](../usage/security.ru.md#mon-в-роли-etcd-proxy).
## etcd_proxy.cert
- Тип: строка
Клиентский сертификат для подключений от Antietcd к etcd в режиме прокси.
## etcd_proxy.key
- Тип: строка
Закрытый ключ для сертификата etcd_proxy.cert.
## etcd_proxy.ca
- Тип: строка
Доверенный TLS-сертификат для проверки сертификата сервера etcd при подключениях от Antietcd.
## osd_cert
- Тип: строка
Сертификат сервера Vitastor OSD. Требуется для шифрования протокола Vitastor. Может быть равен
[osd_ca](#osd_ca) - все OSD на данный момент имеют одинаковые привилегии. Также по умолчанию
используется как клиентский сертификат для подключения от OSD к etcd/Antietcd.
## osd_pkey
- Тип: строка
Закрытый ключ для сертификата osd_cert.
## api_cert
- Тип: строка
Серверный TLS-сертификат для API-сервера [vitastor-cli serve](../usage/cli.ru.md#serve).
## api_pkey
- Тип: строка
Закрытый ключ для сертификата api_cert.
## etcd_client_cert
- Тип: строка
Клиентский TLS сертификат для подключений от клиентов Vitastor к etcd/Antietcd, если вы не хотите
использовать общий клиентский сертификат [cert](#cert).
## etcd_client_key
- Тип: строка
Закрытый ключ для сертификата etcd_client_cert.
Может быть путём к файлу, директории или просто строкой с сертификатом в
формате PEM.
## osd_etcd_client_cert
- Тип: строка
Клиентский TLS сертификат для подключений от Vitastor OSD к etcd/Antietcd, если вы не хотите
использовать общий сертификат OSD [osd_cert](#osd_cert).
Аналогично [etcd_client_cert](#etcd_client_cert), но только для OSD.
OSD, клиенты и мониторы должны иметь разные привилегии, поэтому они должны
использовать разные сертификаты.
## osd_etcd_client_key
- Тип: строка
Закрытый ключ для сертификата osd_etcd_client_cert.
Аналогично [etcd_client_key](#etcd_client_key), но только для OSD.
## mon_etcd_client_cert
- Тип: строка
Клиентский TLS сертификат для подключений от мониторов Vitastor к etcd/Antietcd - требуется, если
вы не используете встроенный в монитор Antietcd. Если вы используете его, то монитор и так имеет
прямой доступ к данным Antietcd и не требует никаких соединений.
Аналогично [etcd_client_cert](#etcd_client_cert), но только для мониторов Vitastor.
## mon_etcd_client_key
- Тип: строка
Закрытый ключ для сертификата mon_etcd_client_cert.
## proto_checksums
- Тип: строка
- Значение по умолчанию: payload
Одно из значений "full", "payload", "gcm" и "none":
- "full" означает расчёт и проверку контрольных сумм на транспортном уровне от полных сообщений,
включая их заголовки и данные - рекомендуется для кластеров без шифрования.
- "payload" включает контрольные суммы только для данных сообщений, но пропускает заголовки -
такая настройка рекомендуется для кластеров с включённым шифрованием, потому что в них заголовки
и так защищены шифрованием AES-GCM.
- "gcm" отключает контрольные суммы и включает шифрование полных сообщений включая заголовки и
данные - AES-GCM уже включает в себя MAC, который по сути является криптостойкой контрольной
суммой. Такая настройка медленнее и рекомендуется только для недоверенных сетей.
- "none" полностью отключает контрольные суммы на транспортном уровне.
## force_proto_checksums
- Тип: строка
Чтобы старые клиенты Vitastor могли подключаться к кластеру с включёнными контрольными
суммами, Vitastor OSD по умолчанию разрешают клиентам отключать контрольные суммы
данных (proto_checksums). Настройка force_proto_checksums задаёт минимальный уровень
безопасности, разрешённый для подключающихся клиентов. Когда шифрование отключено,
force_proto_checksums по умолчанию равно none и подключения клиентов без контрольных
сумм разрешаются. При включённом шифровании значение по умолчанию force_proto_checksums
становится "payload", чтобы блокировать подключения с неаутентифицированными данными.
## max_cipher_pool_size
- Тип: целое число
- Значение по умолчанию: 256
Максимальное количество кэшируемых в памяти OSD контекстов шифра OpenSSL, учитываемое
отдельно для каждого шифра и для шифрования и расшифровки. Вряд ли требует изменения.
Аналогично [etcd_client_key](#etcd_client_key), но только для мониторов Vitastor.
## vault_url
@@ -279,14 +106,14 @@ force_proto_checksums по умолчанию равно none и подключ
- Тип: строка
Клиентский TLS сертификат для подключений к Vault на тот случай, если вы не хотите использовать
общий сертификат клиента Vitastor [cert](#cert), используемый для подключений к Vault по умолчанию.
Клиентский TLS сертификат для подключений к Vault. Как и [etcd_client_cert](#etcd_client_cert),
может быть путём к файлу или просто PEM-строкой с сертификатом.
## vault_client_key
- Тип: строка
Закрытый ключ для сертификата vault_client_cert.
Закрытый ключ для сертификата vault_client_cert (также путь к файлу или PEM строка).
## vault_ca
@@ -317,3 +144,11 @@ force_proto_checksums по умолчанию равно none и подключ
Зазор времени (в секундах), чтобы обновлять токены Vault чуть раньше их реального
lease_timeout, на случай "ухода" системных часов.
## max_aes_xts_pool_size
- Тип: целое число
- Значение по умолчанию: 256
Максимальное количество кэшируемых в памяти OSD контекстов шифрования OpenSSL.
Вряд ли требует изменения.

View File

@@ -66,8 +66,6 @@
{{../../usage/nfs.en.md}}
{{../../usage/security.en.md}}
{{../../usage/admin.en.md}}
## Performance

View File

@@ -66,8 +66,6 @@
{{../../usage/nfs.ru.md}}
{{../../usage/security.ru.md}}
{{../../usage/admin.ru.md}}
## Производительность

View File

@@ -64,7 +64,7 @@ for (const file of params_files)
let out = '\n';
for (const c of cfg)
{
out += `\n- [${c.name}](#${c.name.replace(/\./g, '')})`;
out += `\n- [${c.name}](#${c.name})`;
}
for (const c of cfg)
{

View File

@@ -21,9 +21,6 @@
cluster, cluster_key, persist_filter, stale_read can also be set in
Vitastor configuration with `antietcd_` prefix.
See also: [antietcd_cert](security.en.md#antietcd_cert),
[antietcd_key](security.en.md#antietcd_key) and [etcd_proxy](security.en.md#etcd_proxyurls).
You can dump/load data to or from antietcd using Antietcd `anticli` tool:
```
@@ -50,9 +47,6 @@
node_id, cluster, cluster_key, persist_filter, stale_read также можно задавать
в конфигурации Vitastor с префиксом `antietcd_`.
Смотрите также настройки [antietcd_cert](security.ru.md#antietcd_cert),
[antietcd_key](security.ru.md#antietcd_key) и [etcd_proxy](security.ru.md#etcd_proxyurls).
Вы можете выгружать/загружать данные в или из antietcd с помощью его инструмента
`anticli`:

View File

@@ -938,7 +938,3 @@
This option sets the interval between handling two PG count change chunks.
info_ru: |
Данная опция задаёт интервал между обработкой двух порций изменения числа PG пулов.
- name: gc_on_start
type: bool
info: Forcibly clean all garbage entries in the new store on every OSD restart.
info_ru: Принудительно очищать все мусорные записи в новом хранилище при каждом запуске OSD.

View File

@@ -3,7 +3,3 @@
These parameters affect your Vitastor installation security and apply to OSDs, monitors and clients.
Most of them can be set in /etc/vitastor/vitastor.conf and in etcd, but don't support online modification.
All certificate and private key parameters mentioned may contain a path to a PEM file or just
a PEM string with certificate or a private key. In the latter case, the string must begin with
"-----BEGIN CERTIFICATE-----" or "-----BEGIN PRIVATE KEY-----".

View File

@@ -5,7 +5,3 @@ OSD, мониторами и клиентами.
Большая их часть может задаваться в /etc/vitastor/vitastor.conf и в etcd, но не
поддерживает онлайн-изменение.
Все параметры сертификатов и закрытых ключей могут быть путём к файлу или просто
строкой с сертификатом в формате PEM. В последнем случае строка должна начинаться с
"-----BEGIN CERTIFICATE-----" или "-----BEGIN PRIVATE KEY-----".

View File

@@ -1,209 +1,49 @@
- name: use_perms
type: bool
default: false
info: |
Enable client permissions in a Vitastor cluster, including Antietcd built into the Monitor.
Requires configured encryption. Also note that separate Antietcd requires separate configuration
to use permissions (see [security documentation](../usage/security.en.md) for details).
info_ru: |
Включает клиентские привилегии в кластере Vitastor, в том числе во встроенном в мониторе Antietcd.
Требует настроенного шифрования протокола. Также обратите внимание, что отдельно установленный Antietcd
требует отдельной настройки привилегий (подробности смотрите в [документации безопасности](../usage/security.ru.md)).
- name: cert
type: string
info: |
Client certificate of the current Vitastor user. Required for Vitastor protocol encryption.
Must be signed with [client_ca](#client_ca). Also used as the client certificate for etcd/Antietcd
connections by default.
info_ru: |
Клиентский сертификат текущего пользователя Vitastor. Требуется для шифрования протокола Vitastor.
Должен быть подписан [client_ca](#client_ca). Также по умолчанию используется как клиентский
сертификат для подключения к etcd/Antietcd и Vault.
- name: pkey
type: string
info: Private key of the current Vitastor user.
info_ru: Закрытый ключ текущего пользователя Vitastor.
- name: etcd_ca
type: string
info: |
Trusted TLS CA to verify etcd server certificate. Or just the etcd server's
certificate itself - it's fine to use it for etcd_ca.
info_ru: |
Доверенный корневой TLS-сертификат для проверки сертификата сервера etcd.
Либо же просто сам сертификат сервера etcd - его можно использовать как etcd_ca.
- name: client_ca
type: string
info: |
Trusted TLS CA to verify Vitastor client certificates.
Mandatory for Vitastor protocol encryption.
info_ru: |
Доверенный TLS-сертификат для проверки сертификатов клиентов Vitastor.
Требуется для шифрования протокола Vitastor.
- name: admin_ca
type: string
info: |
Trusted TLS CA to verify Vitastor administrator certificates.
info_ru: |
Доверенный TLS-сертификат для проверки сертификатов администраторов Vitastor.
- name: osd_ca
type: string
info: |
Trusted TLS CA to verify Vitastor OSD certificates. Also mandatory for Vitastor protocol
encryption. Must be different from client_ca. May be equal to osd_cert - different OSDs
don't require separate certificates at the moment because their permissions don't differ.
info_ru: |
Доверенный TLS-сертификат для проверки сертификатов OSD Vitastor. Также обязателен
для шифрования протокола Vitastor. Должен отличаться от client_ca. Может быть равен
osd_cert - разные OSD не требуют разных сертификатов, потому что на данный момент
привилегии разных OSD никак не отличаются.
- name: mon_ca
type: string
info: |
Trusted TLS CA to verify Vitastor Monitor certificates. Used only for separate Antietcd,
not required when a monitor built-in Antietcd is used. May be equal to mon_client_etcd_cert.
info_ru: |
Доверенный TLS-сертификат для проверки сертификатов мониторов Vitastor. Используется
только отдельно установленным Antietcd, не требуется при использовании встроенного в монитор
Antietcd. Может быть равен mon_client_etcd_cert.
- name: antietcd_cert
type: string
info: Server TLS certificate for Antietcd built into the Monitor.
info_ru: Серверный TLS-сертификат для Antietcd, встроенного в монитор.
- name: antietcd_key
type: string
info: Private key for antietcd_cert.
info_ru: Закрытый ключ для сертификата antietcd_cert.
- name: etcd_proxy.urls
type: string or array of strings
type_ru: строка или массив строк
info: |
etcd URLs for Antietcd etcd proxy mode.
See [Mon as Etcd proxy](../usage/security.en.md#mon-as-etcd-proxy) for details.
info_ru: |
Адреса etcd для режима Antietcd etcd-прокси.
Смотрите подробности в разделе [Mon в роли Etcd proxy](../usage/security.ru.md#mon-в-роли-etcd-proxy).
- name: etcd_proxy.cert
type: string
info: Client certificate for Antietcd connections to etcd in proxy mode.
info_ru: Клиентский сертификат для подключений от Antietcd к etcd в режиме прокси.
- name: etcd_proxy.key
type: string
info: Private key for etcd_proxy.cert.
info_ru: Закрытый ключ для сертификата etcd_proxy.cert.
- name: etcd_proxy.ca
type: string
info: Trusted TLS CA to verify etcd server certificate when connecting to it from Antietcd.
info_ru: Доверенный TLS-сертификат для проверки сертификата сервера etcd при подключениях от Antietcd.
- name: osd_cert
type: string
info: |
Vitastor OSD server certificate. Required for Vitastor protocol encryption. May be equal
to [osd_ca](#osd_ca) - all OSDs share the same permission set for now. Also used as the client
certificate for connections from OSD to etcd/Antietcd by default.
info_ru: |
Сертификат сервера Vitastor OSD. Требуется для шифрования протокола Vitastor. Может быть равен
[osd_ca](#osd_ca) - все OSD на данный момент имеют одинаковые привилегии. Также по умолчанию
используется как клиентский сертификат для подключения от OSD к etcd/Antietcd.
- name: osd_pkey
type: string
info: Private key for osd_cert.
info_ru: Закрытый ключ для сертификата osd_cert.
- name: api_cert
type: string
info: Server TLS certificate for [vitastor-cli serve](../usage/cli.en.md#serve) API server.
info_ru: Серверный TLS-сертификат для API-сервера [vitastor-cli serve](../usage/cli.ru.md#serve).
- name: api_pkey
type: string
info: Private key for api_cert.
info_ru: Закрытый ключ для сертификата api_cert.
- name: etcd_client_cert
type: string
info: |
Client TLS certificate to use for connections from Vitastor clients to etcd/Antietcd if you don't want
to use the common client certificate [cert](#cert).
Client TLS certificate to use for Vitastor client (not OSD and not monitor)
etcd https connections. May be path to a file or just a PEM string with certificate.
In the latter case, string must begin with "-----BEGIN CERTIFICATE-----".
info_ru: |
Клиентский TLS сертификат для подключений от клиентов Vitastor к etcd/Antietcd, если вы не хотите
использовать общий клиентский сертификат [cert](#cert).
Клиентский TLS сертификат для https-подключений к etcd для клиентов Vitastor
(не OSD и не мониторов). Может быть путём к файлу или просто строкой с
сертификатом в формате PEM. В последнем случае строка должна начинаться с
"-----BEGIN CERTIFICATE-----".
- name: etcd_client_key
type: string
info: Private key for etcd_client_cert.
info_ru: Закрытый ключ для сертификата etcd_client_cert.
info: Private key for etcd_client_cert (also a file or a PEM string).
info_ru: Закрытый ключ для сертификата etcd_client_cert (также путь к файлу или PEM строка).
- name: etcd_ca
type: string
info: |
Trusted TLS CA to verify etcd server certificate. May be path to a file,
directory or just a PEM string with certificate.
info_ru: |
Доверенный корневой TLS-сертификат для проверки сертификата сервера etcd.
Может быть путём к файлу, директории или просто строкой с сертификатом в
формате PEM.
- name: osd_etcd_client_cert
type: string
info: |
Client TLS certificate to use for connections from Vitastor OSDs to etcd/Antietcd if you don't want
to use the common OSD certificate [osd_cert](#osd_cert).
Same as [etcd_client_cert](#etcd_client_cert), but only for OSDs.
OSDs, clients and monitors should have different permissions, so they should
use different certificates.
info_ru: |
Клиентский TLS сертификат для подключений от Vitastor OSD к etcd/Antietcd, если вы не хотите
использовать общий сертификат OSD [osd_cert](#osd_cert).
Аналогично [etcd_client_cert](#etcd_client_cert), но только для OSD.
OSD, клиенты и мониторы должны иметь разные привилегии, поэтому они должны
использовать разные сертификаты.
- name: osd_etcd_client_key
type: string
info: Private key for osd_etcd_client_cert.
info_ru: Закрытый ключ для сертификата osd_etcd_client_cert.
info: Same as [etcd_client_key](#etcd_client_key), but only for OSDs.
info_ru: Аналогично [etcd_client_key](#etcd_client_key), но только для OSD.
- name: mon_etcd_client_cert
type: string
info: |
Client TLS certificate to use for connections from Vitastor Monitors to etcd/Antietcd - required
if you don't use the built-in Antietcd. In case you use it Monitor has direct access to Antietcd data
and doesn't require any connection.
info_ru: |
Клиентский TLS сертификат для подключений от мониторов Vitastor к etcd/Antietcd - требуется, если
вы не используете встроенный в монитор Antietcd. Если вы используете его, то монитор и так имеет
прямой доступ к данным Antietcd и не требует никаких соединений.
info: Same as [etcd_client_cert](#etcd_client_cert), but only for Vitastor monitors.
info_ru: Аналогично [etcd_client_cert](#etcd_client_cert), но только для мониторов Vitastor.
- name: mon_etcd_client_key
type: string
info: Private key for mon_etcd_client_cert.
info_ru: Закрытый ключ для сертификата mon_etcd_client_cert.
- name: proto_checksums
type: string
default: payload
info: |
One of "full", "payload", "gcm", "none":
- "full" means calculate and verify transport level checksums from the full message data
including the header - recommended for unencrypted setups.
- "payload" enables checksums only for the actual read/write data, but skips them for message
headers - recommended for encrypted setups because headers are already protected by AES-GCM.
- "gcm" disables checksums and enables AES-GCM encryption of the whole messages including headers
and data - AES-GCM already includes MAC which is actually a stronger checksum. This option is
slower and is only recommended for untrusted networks.
- "none" disables transport level checksums at all.
info_ru: |
Одно из значений "full", "payload", "gcm" и "none":
- "full" означает расчёт и проверку контрольных сумм на транспортном уровне от полных сообщений,
включая их заголовки и данные - рекомендуется для кластеров без шифрования.
- "payload" включает контрольные суммы только для данных сообщений, но пропускает заголовки -
такая настройка рекомендуется для кластеров с включённым шифрованием, потому что в них заголовки
и так защищены шифрованием AES-GCM.
- "gcm" отключает контрольные суммы и включает шифрование полных сообщений включая заголовки и
данные - AES-GCM уже включает в себя MAC, который по сути является криптостойкой контрольной
суммой. Такая настройка медленнее и рекомендуется только для недоверенных сетей.
- "none" полностью отключает контрольные суммы на транспортном уровне.
- name: force_proto_checksums
type: string
info: |
To allow older clients to connect to a Vitastor cluster with enabled checksums, Vitastor OSDs
allow clients to downgrade their proto_checksums by default. force_proto_checksums sets the
minimum security level allowed for connecting clients. When encryption is disabled, default
force_proto_checksums is none and clients without checksums are allowed. With enabled
encryption, force_proto_checksums becomes "payload" by default to block unauthenticated data
on the transport level.
info_ru: |
Чтобы старые клиенты Vitastor могли подключаться к кластеру с включёнными контрольными
суммами, Vitastor OSD по умолчанию разрешают клиентам отключать контрольные суммы
данных (proto_checksums). Настройка force_proto_checksums задаёт минимальный уровень
безопасности, разрешённый для подключающихся клиентов. Когда шифрование отключено,
force_proto_checksums по умолчанию равно none и подключения клиентов без контрольных
сумм разрешаются. При включённом шифровании значение по умолчанию force_proto_checksums
становится "payload", чтобы блокировать подключения с неаутентифицированными данными.
- name: max_cipher_pool_size
type: int
default: 256
info: |
Maximum number of OpenSSL cipher contexts cached in OSD memory, counted separately
for each cipher and for encryption/decryption. Probably doesn't require modification.
info_ru: |
Максимальное количество кэшируемых в памяти OSD контекстов шифра OpenSSL, учитываемое
отдельно для каждого шифра и для шифрования и расшифровки. Вряд ли требует изменения.
info: Same as [etcd_client_key](#etcd_client_key), but only for Vitastor monitors.
info_ru: Аналогично [etcd_client_key](#etcd_client_key), но только для мониторов Vitastor.
- name: vault_url
type: string
info: |
@@ -241,15 +81,15 @@
- name: vault_client_cert
type: string
info: |
Client TLS certificate to use for Vault connections if you don't want to use the common Vitastor
client certificate [cert](#cert) which is also used for Vault connections by default.
Client TLS certificate to use for Vault connections. Just like [etcd_client_cert](#etcd_client_cert),
may be path to a file or just a certificate in PEM string.
info_ru: |
Клиентский TLS сертификат для подключений к Vault на тот случай, если вы не хотите использовать
общий сертификат клиента Vitastor [cert](#cert), используемый для подключений к Vault по умолчанию.
Клиентский TLS сертификат для подключений к Vault. Как и [etcd_client_cert](#etcd_client_cert),
может быть путём к файлу или просто PEM-строкой с сертификатом.
- name: vault_client_key
type: string
info: Private key for the vault_client_cert certificate.
info_ru: Закрытый ключ для сертификата vault_client_cert.
info: Private key for vault_client_cert (also a file or a PEM string).
info_ru: Закрытый ключ для сертификата vault_client_cert (также путь к файлу или PEM строка).
- name: vault_ca
type: string
info: |
@@ -280,3 +120,12 @@
info_ru: |
Зазор времени (в секундах), чтобы обновлять токены Vault чуть раньше их реального
lease_timeout, на случай "ухода" системных часов.
- name: max_aes_xts_pool_size
type: int
default: 256
info: |
Maximum number of OpenSSL encryption contexts cached in OSD memory. Probably
doesn't require modification.
info_ru: |
Максимальное количество кэшируемых в памяти OSD контекстов шифрования OpenSSL.
Вряд ли требует изменения.

View File

@@ -26,9 +26,9 @@ at Vitastor Kubernetes operator: https://github.com/Antilles7227/vitastor-operat
The instruction is very simple.
1. Download a Docker image of the desired version: \
`docker pull vitalif/vitastor:v3.1.0`
`docker pull vitalif/vitastor:v3.0.5`
2. Install scripts to the host system: \
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.1.0 install.sh`
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.5 install.sh`
3. Reload udev rules: \
`udevadm control --reload-rules`
4. Enable the vitastor-host service: \

View File

@@ -25,9 +25,9 @@ Vitastor можно установить в Docker/Podman. При этом etcd,
Инструкция по установке максимально простая.
1. Скачайте Docker-образ желаемой версии: \
`docker pull vitalif/vitastor:v3.1.0`
`docker pull vitalif/vitastor:v3.0.5`
2. Установите скрипты в хост-систему командой: \
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.1.0 install.sh`
`docker run --rm -it -v /etc:/host-etc -v /usr/bin:/host-bin vitalif/vitastor:v3.0.5 install.sh`
3. Перезагрузите правила udev: \
`udevadm control --reload-rules`
4. Включите сервис vitastor-host: \

View File

@@ -14,9 +14,11 @@
- Debian 13 (Trixie/Sid): `deb https://vitastor.io/debian trixie main`
- Debian 12 (Bookworm): `deb https://vitastor.io/debian bookworm main`
- Debian 11 (Bullseye): `deb https://vitastor.io/debian bullseye main`
- Ubuntu 26.04 (Resolute): `deb https://vitastor.io/debian resolute main`
- Ubuntu 24.04 (Noble): `deb https://vitastor.io/debian noble main`
- Debian 10 (Buster): `deb https://vitastor.io/debian buster main`
- Ubuntu 22.04 (Jammy): `deb https://vitastor.io/debian jammy main`
- Ubuntu 24.04 (Noble): `deb https://vitastor.io/debian noble main`
- Add `-oldstable` to bookworm/bullseye/buster in this line to install the last
stable version from 0.9.x branch instead of 1.x
- To always prefer vitastor-patched QEMU and Libvirt versions, add the following to `/etc/apt/preferences`:
```
Package: *
@@ -28,14 +30,17 @@
## CentOS
- Add Vitastor package repository:
- CentOS 7: `yum install https://vitastor.io/rpms/centos/7/vitastor-release.rpm`
- CentOS 8: `dnf install https://vitastor.io/rpms/centos/8/vitastor-release.rpm`
- AlmaLinux 9 and other RHEL 9 clones (Rocky, Oracle...): `dnf install https://vitastor.io/rpms/centos/9/vitastor-release.rpm`
- AlmaLinux 10 and other RHEL 10 clones: `dnf install https://vitastor.io/rpms/centos/10/vitastor-release.rpm`
- Enable EPEL: `yum/dnf install epel-release`
- Enable additional CentOS repositories:
- CentOS 7: `yum install centos-release-scl`
- CentOS 8: `dnf install centos-release-advanced-virtualization`
- RHEL 9/10 clones: not required
- Enable elrepo-kernel:
- CentOS 7: `yum install https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm`
- CentOS 8: `dnf install https://www.elrepo.org/elrepo-release-8.el8.elrepo.noarch.rpm`
- RHEL 9 clones: `dnf install https://www.elrepo.org/elrepo-release-9.el9.elrepo.noarch.rpm`
- RHEL 10 clones: not required

View File

@@ -14,9 +14,11 @@
- Debian 13 (Trixie/Sid): `deb https://vitastor.io/debian trixie main`
- Debian 12 (Bookworm): `deb https://vitastor.io/debian bookworm main`
- Debian 11 (Bullseye): `deb https://vitastor.io/debian bullseye main`
- Ubuntu 26.04 (Resolute): `deb https://vitastor.io/debian resolute main`
- Ubuntu 24.04 (Noble): `deb https://vitastor.io/debian noble main`
- Debian 10 (Buster): `deb https://vitastor.io/debian buster main`
- Ubuntu 22.04 (Jammy): `deb https://vitastor.io/debian jammy main`
- Ubuntu 24.04 (Noble): `deb https://vitastor.io/debian noble main`
- Добавьте `-oldstable` к слову bookworm/bullseye/buster в этой строке, чтобы
установить последнюю стабильную версию из ветки 0.9.x вместо 1.x
- Чтобы всегда предпочитались версии пакетов QEMU и Libvirt с патчами Vitastor, добавьте в `/etc/apt/preferences`:
```
Package: *
@@ -28,14 +30,17 @@
## CentOS
- Добавьте в систему репозиторий Vitastor:
- CentOS 7: `yum install https://vitastor.io/rpms/centos/7/vitastor-release.rpm`
- CentOS 8: `dnf install https://vitastor.io/rpms/centos/8/vitastor-release.rpm`
- AlmaLinux 9 и другие клоны RHEL 9 (Rocky, Oracle...): `dnf install https://vitastor.io/rpms/centos/9/vitastor-release.rpm`
- AlmaLinux 10 и другие клоны RHEL 10: `dnf install https://vitastor.io/rpms/centos/10/vitastor-release.rpm`
- Включите EPEL: `yum/dnf install epel-release`
- Включите дополнительные репозитории CentOS:
- CentOS 7: `yum install centos-release-scl`
- CentOS 8: `dnf install centos-release-advanced-virtualization`
- Клоны RHEL 9/10: не нужно
- Включите elrepo-kernel:
- CentOS 7: `yum install https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm`
- CentOS 8: `dnf install https://www.elrepo.org/elrepo-release-8.el8.elrepo.noarch.rpm`
- Клоны RHEL 9: `dnf install https://www.elrepo.org/elrepo-release-9.el9.elrepo.noarch.rpm`
- Клоны RHEL 10: не нужно

View File

@@ -6,10 +6,10 @@
# Proxmox VE
To enable Vitastor support in Proxmox Virtual Environment (7.x-9.x are supported):
To enable Vitastor support in Proxmox Virtual Environment (6.4-9.x are supported):
- Add the corresponding Vitastor Debian repository into sources.list on Proxmox hosts:
trixie for 9.0+, bookworm for 8.1+, pve8.0 for 8.0, bullseye for 7.4, pve7.3 for 7.3, pve7.2 for 7.2, pve7.1 for 7.1
trixie for 9.0+, bookworm for 8.1+, pve8.0 for 8.0, bullseye for 7.4, pve7.3 for 7.3, pve7.2 for 7.2, pve7.1 for 7.1, buster for 6.4
- 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),

View File

@@ -6,10 +6,10 @@
# Proxmox VE
Чтобы подключить Vitastor к Proxmox Virtual Environment (поддерживаются версии 7.x-9.x):
Чтобы подключить Vitastor к Proxmox Virtual Environment (поддерживаются версии 6.4-9.x):
- Добавьте соответствующий Debian-репозиторий Vitastor в sources.list на хостах Proxmox:
trixie для 9.0+, bookworm для 8.1+, pve8.0 для 8.0, bullseye для 7.4, pve7.3 для 7.3, pve7.2 для 7.2, pve7.1 для 7.1
trixie для 9.0+, bookworm для 8.1+, pve8.0 для 8.0, bullseye для 7.4, pve7.3 для 7.3, pve7.2 для 7.2, pve7.1 для 7.1, buster для 6.4
- Установите пакеты vitastor-client, pve-qemu-kvm, pve-storage-vitastor (* или см. сноску) из репозитория Vitastor
- Определите тип хранилища в `/etc/pve/storage.cfg` (см. ниже)
- Обязательно заблокируйте доступ от виртуальных машин к сети Vitastor (OSD и etcd), т.к. Vitastor (пока) не поддерживает аутентификацию

View File

@@ -17,6 +17,7 @@
- CMake
- jerasure, c-ares headers and libraries
- ISA-L, libibverbs, librdmacm, libnl3 headers and libraries (optional)
- tcmalloc (google-perftools-dev)
## Basic instructions

View File

@@ -17,6 +17,7 @@
- CMake
- Заголовки и библиотеки jerasure, c-ares
- Опционально - заголовки и библиотеки ISA-L, libibverbs, librdmacm, libnl3
- tcmalloc (google-perftools-dev)
## Базовая инструкция

View File

@@ -41,8 +41,8 @@
- [Built-in Prometheus metric exporter](../config/monitor.en.md#enable_prometheus)
- [NFS RDMA support](../usage/nfs.en.md#rdma) (probably also usable for GPUDirect)
- [S3](../installation/s3.en.md)
- [Full transport encryption and access control](../usage/security.en.md)
- [AES-256-XTS end-to-end image encryption](../usage/security.en.md#end-to-end-image-data-encryption-aes-xts) with [Vault support](../usage/security.en.md#vaultopenbao-setup) for key storage
- [TLS support for etcd connections](../config/security.en.md)
- [AES-256-XTS image encryption](../usage/cli.en.md#create) and [Vault support](../config/security.en.md#vault_url) for key storage
## Plugins and tools

View File

@@ -43,8 +43,8 @@
- [Встроенный Prometheus-экспортер метрик](../config/monitor.ru.md#enable_prometheus)
- [Поддержка NFS RDMA](../usage/nfs.ru.md#rdma) (вероятно, также подходящая для GPUDirect)
- [S3](../installation/s3.ru.md)
- [Полное транспортное шифрование и контроль доступа](../usage/security.ru.md)
- [AES-256-XTS сквозное шифрование данных](../usage/security.ru.md#сквозное-шифрование-данных-образов-aes-xts) с [поддержкой Vault](../usage/security.ru.md#настройка-vaultopenbao) для хранения ключей
- [Поддержка TLS-соединений с etcd](../config/security.ru.md)
- [AES-256-XTS шифрование данных](../usage/cli.ru.md#create) и [поддержка Vault](../config/security.ru.md#vault_url) для хранения ключей
## Драйверы и инструменты

View File

@@ -41,16 +41,12 @@
## Configure monitors
On the monitor hosts:
- Create minimal configuration in `/etc/vitastor/vitastor.conf`:
- Put identical etcd_address into `/etc/vitastor/vitastor.conf`. Example:
```
{
"etcd_address": ["http://10.200.1.10:2379","http://10.200.1.11:2379","http://10.200.1.12:2379"],
"osd_network": "10.200.1.0/24",
"use_perms": false
"etcd_address": ["10.200.1.10:2379","10.200.1.11:2379","10.200.1.12:2379"]
}
```
- Note that you can enable encryption by using `https://` and `use_perms` option.
[Details](security.en.md#quick-setup) about encryption setup with make-etcd.
- Create systemd units for etcd by running: `/usr/lib/vitastor/mon/make-etcd`
Or, if you installed Vitastor in Docker, run `systemctl start vitastor-host; docker exec vitastor make-etcd`.
- Start etcd and monitors: `systemctl enable --now vitastor-etcd vitastor-mon`

View File

@@ -41,23 +41,25 @@
## Настройте мониторы
На хостах, выделенных под мониторы:
- Создайте минимальную конфигурацию в `/etc/vitastor/vitastor.conf`:
- Пропишите одинаковые etcd_address в `/etc/vitastor/vitastor.conf`. Например:
```
{
"etcd_address": ["http://10.200.1.10:2379","http://10.200.1.11:2379","http://10.200.1.12:2379"],
"osd_network": "10.200.1.0/24",
"use_perms": false
"etcd_address": ["10.200.1.10:2379","10.200.1.11:2379","10.200.1.12:2379"]
}
```
- Обратите внимание, что с помощью схемы `https://` и опции `use_perms` можно включить шифрование.
[Подробно](security.ru.md#быстрая-настройка) о настройке шифрования через make-etcd.
- Инициализируйте сервисы etcd, запустив `/usr/lib/vitastor/mon/make-etcd`.\
Либо, если вы установили Vitastor в Docker, запустите `systemctl start vitastor-host; docker exec vitastor make-etcd`.
- Запустите etcd и мониторы: `systemctl enable --now vitastor-etcd vitastor-mon`
## Настройте OSD
- Создайте/скопируйте с узлов с мониторами файл конфигурации `/etc/vitastor/vitastor.conf`.
- Пропишите etcd_address и [osd_network](../config/network.ru.md#osd_network) в `/etc/vitastor/vitastor.conf`. Например:
```
{
"etcd_address": ["10.200.1.10:2379","10.200.1.11:2379","10.200.1.12:2379"],
"osd_network": "10.200.1.0/24"
}
```
- Инициализуйте OSD:
- Только SSD или только HDD: `vitastor-disk prepare /dev/sdXXX [/dev/sdYYY ...]`.
Если вы используете десктопные SSD без конденсаторов, добавьте опцию `--disable_data_fsync off`,

View File

@@ -33,11 +33,6 @@ It supports the following commands:
- [modify-pool](#modify-pool)
- [ls-pools](#ls-pools)
- [rm-pool](#rm-pool)
- [ls-user](#ls-user)
- [modify-user](#modify-user)
- [rm-user](#rm-user)
- [serve](#serve)
- [cpubench](#cpubench)
Global options:
@@ -112,7 +107,6 @@ Options:
--sort FIELD Sort by specified field (name, size, used_size, <read|write|delete>_<iops|bps|lat|queue>)
-r|--reverse Sort in descending order
-n|--count N Only list first N items
--ids ID1,ID2 Only list images with specified full IDs
--tree Show image snapshot/clone tree
```
@@ -142,10 +136,6 @@ Create an image. Options:
* `--enc-key random` - Generate a new random AES-256-XTS encryption key for the new image.
* `--enc-key HEX` - Set a specified AES-256-XTS key (64 bytes in hex) for the new image.
* `--enc-key vault:ID` - Use an encryption key from an external Vault secret with specified ID.
* `--owner USERNAME` - Set image owner. The default owner is the current user taken from the
CN of the TLS certificate used to connect to the cluster.
* `--owner-group NAME` - Set image owner group. Users in this group get full access to the image.
* `--reader-group NAME` - Set image reader group. Users in this group get read-only access to the image.
```
vitastor-cli create --snapshot <snapshot> [OPTIONS] <image>
@@ -175,9 +165,6 @@ You should resize file system in the image, if present, before shrinking it.
* `-f|--force` - Proceed with shrinking or setting readwrite flag even if the image has children.
* `--down-ok` - Proceed with shrinking even if some data will be left on unavailable OSDs.
* `--enc-key HEX` - Change image encryption key (allowed only with `--force`).
* `--owner USERNAME` - Change image owner.
* `--owner-group NAME` - Change image owner group name.
* `--reader-group NAME` - Change image reader group name.
## dd
@@ -433,7 +420,6 @@ Optional parameters:
| `--used_for_app s3:<name>` | Mark pool as used for S3 location with name `<name>` |
| `--pg_stripe_size <number>` | Increase object grouping stripe |
| `--max_osd_combinations 10000` | Maximum number of random combinations for LP solver input |
| `--creator_group <group>` | User group allowed to create images in this pool |
| `--wait` | Wait for the new pool to come online |
| `-f` or `--force` | Do not check that cluster has enough OSDs to create the pool |
@@ -453,9 +439,8 @@ Modify an existing pool. Modifiable parameters:
```
[-s|--pg_size <number>] [--pg_minsize <number>] [-n|--pg_count <count>]
[--failure_domain <level>] [--root_node <node>] [--osd_tags <tags>] [--used_for_app <type>:<name>]
[--failure_domain <level>] [--root_node <node>] [--osd_tags <tags>] [--no_inode_stats 0|1]
[--max_osd_combinations <number>] [--primary_affinity_tags <tags>] [--scrub_interval <time>]
[--level_placement <rules>] [--raw_placement <rules>] [--creator_group <group>]
```
Non-modifiable parameters (changing them WILL lead to data loss):
@@ -483,7 +468,7 @@ Remove a pool. Refuses to remove pools with images without `--force`.
## ls-pools
`vitastor-cli ls-pools|pool-ls|ls-pool|pools [-l] [--detail] [--sort FIELD] [-r] [-n N] [<glob> ...]`
`vitastor-cli ls-pools|pool-ls|ls-pool|pools [-l] [--detail] [--sort FIELD] [-r] [-n N] [--stats] [<glob> ...]`
List pools (only matching <glob> patterns if passed).
@@ -494,77 +479,3 @@ List pools (only matching <glob> patterns if passed).
| `--sort FIELD` | Sort by specified field (see fields in --json output) |
| `-r` or `--reverse` | Sort in descending order |
| `-n` or `--count N` | Only list first N items |
## ls-user
`vitastor-cli ls-users|user-ls|ls-user|list-users [<name> ...]`
List users. If names are passed, only list users with those names. User names must
match CN (Common Name) of TLS client certificates used to connect to the cluster.
See [Users and access rights](security.en.md#users-and-access-rights) for details on
the Vitastor authorization model.
## modify-user
`vitastor-cli modify-user --groups <group1,group2,...> <username>`
Create or update a user with the given name (matching TLS certificate CN).
Options:
* `--groups GROUPS` - Set user's groups (comma-separated). Groups are used together with
`--owner-group` / `--reader-group` fields of images and `--creator_group` of the pool to
authorize operations. Pass an empty string to clear the groups.
## rm-user
`vitastor-cli rm-user|remove-user|delete-user <username>`
Remove a user.
## serve
`vitastor-cli serve`
Start an HTTP server that handles vitastor-cli commands over a REST API in JSON format.
Options:
| <!-- --> | <!-- --> |
|-----------------------|-------------------------------------------------------------------------------|
| `--bind_address ADDR` | Server IP address(es), separated by space. Default is `127.0.0.1`. |
| `--port 8080` | Server port. Default is `8080`. |
| `--api_cert FILE` | Server TLS certificate. |
| `--api_pkey FILE` | Private key for `--api_cert`. |
| `--cert FILE` | Administrator or client certificate used by the server to access the cluster. |
| `--pkey FILE` | Private key for `--cert`. |
When `api_cert`/`api_pkey` are set the server operates in HTTPS mode. If `client_ca`
is also set (in configuration or on the command line), connecting clients are authenticated by their
TLS certificates. When [use_perms](../config/security.en.md#use_perms) is enabled, HTTPS with client
authentication is mandatory and per-user permissions are enforced. For regular clients the server only
allows operations on images that the user owns or has group access to; all other operations require
administrator privileges.
The vitastor-cli process itself must be authenticated as an admin user (its own client
[cert](../config/security.en.md#cert) must be signed with [admin_ca](../config/security.en.md#admin_ca))
to be able to serve all requests.
`vitastor-cli serve` exposes a full OpenAPI specification at `/openapi` endpoint.
Start the server and check it out for the information about available API methods.
See also [Security in Vitastor](security.en.md) for the full picture of how authentication
and authorization work.
## cpubench
`vitastor-cli cpubench [--json]`
Run CPU crypto performance tests: AES-256-GCM, AES-256-XTS and xxhash3. This benchmark
does not connect to the cluster — it only measures encryption/hashing speed locally.
Specify `--json` to get the output as machine-readable JSON.
See [Encryption performance](security.en.md#encryption-performance) for example results
on modern and older CPUs.

View File

@@ -34,11 +34,6 @@ vitastor-cli - интерфейс командной строки для адм
- [modify-pool](#modify-pool)
- [ls-pools](#ls-pools)
- [rm-pool](#rm-pool)
- [ls-user](#ls-user)
- [modify-user](#modify-user)
- [rm-user](#rm-user)
- [serve](#serve)
- [cpubench](#cpubench)
Глобальные опции:
@@ -114,7 +109,6 @@ kaveri 2/1 32 0 B 10 G 0 B 100% 0%
--sort FIELD Сортировать по заданному полю (name, size, used_size, <read|write|delete>_<iops|bps|lat|queue>)
-r|--reverse Сортировать в обратном порядке
-n|--count N Показывать только первые N записей
--ids ID1,ID2 Показывать только образы с заданными полными ID
--tree Вывести снапшоты и клоны в виде дерева
```
@@ -144,12 +138,6 @@ bench-kaveri kaveri 10 G 10 G 0 B/s 0 0 0 us 0 B/s 0
* `--enc-key random` - Сгенерировать случайный ключ шифрования AES-256-XTS для нового образа.
* `--enc-key HEX` - Установить заданный ключ AES-256-XTS (64 байта в hex) для нового образа.
* `--enc-key vault:ID` - Использовать ключ из внешнего секрета с заданным ID из Vault.
* `--owner USERNAME` - Задать владельца образа. По умолчанию владельцем становится текущий
пользователь, определяемый по CN TLS-сертификата, используемого для подключения к кластеру.
* `--owner-group NAME` - Задать группу владельцев образа. Пользователи из этой группы получают
полный доступ к образу.
* `--reader-group NAME` - Задать группу читателей образа. Пользователи из этой группы получают
доступ к образу только на чтение.
```
vitastor-cli create --snapshot <snapshot> [ОПЦИИ] <image>
@@ -182,9 +170,6 @@ vitastor-cli snap-create [ОПЦИИ] <image>@<snapshot>
* `-f|--force` - Разрешить уменьшение или перевод в чтение-запись образа, у которого есть клоны.
* `--down-ok` - Разрешить уменьшение, даже если часть данных останется неудалённой на недоступных OSD.
* `--enc-key HEX` - Изменить ключ шифрования образа (разрешено только с `--force`).
* `--owner USERNAME` - Изменить владельца образа.
* `--owner-group NAME` - Изменить имя группы владельцев образа.
* `--reader-group NAME` - Изменить имя группы читателей образа.
## dd
@@ -450,7 +435,6 @@ OSD PARENT UP SIZE USED% TAGS WEIGHT BLOCK BITMAP
| `--scrub_interval <time>` | Включить скрабы с заданным интервалом времени (число + единица s/m/h/d/M/y) |
| `--pg_stripe_size <number>` | Увеличить блок группировки объектов по PG |
| `--max_osd_combinations 10000` | Максимальное число случайных комбинаций OSD для ЛП-солвера |
| `--creator_group <group>` | Группа пользователей, которым разрешено создавать образы в данном пуле |
| `--wait` | Подождать, пока новый пул будет активирован |
| `-f` или `--force` | Не проверять, что в кластере достаточно доменов отказа для создания пула |
@@ -470,9 +454,8 @@ OSD PARENT UP SIZE USED% TAGS WEIGHT BLOCK BITMAP
```
[-s|--pg_size <number>] [--pg_minsize <number>] [-n|--pg_count <count>]
[--failure_domain <level>] [--root_node <node>] [--osd_tags <tags>] [--used_for_app <type>:<name>]
[--failure_domain <level>] [--root_node <node>] [--osd_tags <tags>]
[--max_osd_combinations <number>] [--primary_affinity_tags <tags>] [--scrub_interval <time>]
[--level_placement <rules>] [--raw_placement <rules>] [--creator_group <group>]
```
Неизменяемые параметры (их изменение ПРИВЕДЁТ к потере данных):
@@ -500,7 +483,7 @@ OSD PARENT UP SIZE USED% TAGS WEIGHT BLOCK BITMAP
## ls-pools
`vitastor-cli ls-pools|pool-ls|ls-pool|pools [-l] [--detail] [--sort FIELD] [-r] [-n N] [<glob> ...]`
`vitastor-cli ls-pools|pool-ls|ls-pool|pools [-l] [--detail] [--sort FIELD] [-r] [-n N] [--stats] [<glob> ...]`
Показать список пулов. Если передан(ы) шаблон(ы) `<glob>`, то только с именами,
соответствующими одному из шаблонов (стандартные ФС-шаблоны с * и ?).
@@ -512,79 +495,3 @@ OSD PARENT UP SIZE USED% TAGS WEIGHT BLOCK BITMAP
| `--sort FIELD` | Сортировать по заданному полю (поля см. в выводе с --json) |
| `-r` или `--reverse` | Сортировать в обратном порядке |
| `-n` или `--count N` | Выводить только первые N записей |
## ls-user
`vitastor-cli ls-users|user-ls|ls-user|list-users [<name> ...]`
Показать список пользователей. Если переданы имена, показать только пользователей с указанными
именами. Имена пользователей должны совпадать с CN (Common Name) TLS-сертификатов
клиентов, используемых для подключения к кластеру.
Подробнее о модели авторизации в Vitastor см. в разделе
[Пользователи и права доступа](security.ru.md#пользователи-и-права-доступа).
## modify-user
`vitastor-cli modify-user --groups <group1,group2,...> <username>`
Создать или изменить пользователя с указанным именем (совпадающим с CN TLS-сертификата).
Опции:
* `--groups GROUPS` - Задать группы пользователя (через запятую). Группы используются
вместе с полями `owner-group` / `reader-group` образов и полем `--creator_group` пулов
для авторизации операций. Пустая строка очищает список групп.
## rm-user
`vitastor-cli rm-user|remove-user|delete-user <username>`
Удалить пользователя.
## serve
`vitastor-cli serve`
Запустить HTTP-сервер, обрабатывающий команды vitastor-cli через REST API в формате JSON.
Опции:
| <!-- --> | <!-- --> |
|-----------------------|----------------------------------------------------------------------------------|
| `--bind_address ADDR` | IP-адрес(а) сервера через пробел. По умолчанию `127.0.0.1`. |
| `--port 8080` | Порт сервера. По умолчанию `8080`. |
| `--api_cert FILE` | TLS-сертификат сервера. |
| `--api_pkey FILE` | Закрытый ключ для сертификата `--api_cert`. |
| `--cert FILE` | Сертификат администратора/клиента, используемый сервером для доступа к кластеру. |
| `--pkey FILE` | Закрытый ключ для сертификата `--cert`. |
Если заданы `api_cert`/`api_pkey`, сервер работает в режиме HTTPS. Если дополнительно
задан `client_ca` (в файле конфигурации или в командной строке), подключающиеся клиенты аутентифицируются
по своим TLS-сертификатам. Если включена опция [use_perms](../config/security.ru.md#use_perms), HTTPS с
аутентификацией клиентов обязателен, и права доступа проверяются для каждого пользователя. Обычным клиентам
сервер разрешает только операции с образами, которыми они владеют или к которым имеют доступ через группы;
остальные операции разрешены только администраторам.
Сам процесс vitastor-cli при этом должен использовать сертификат администратора ([cert](../config/security.en.md#cert),
подписанный [admin_ca](../config/security.en.md#admin_ca)), чтобы иметь возможность обслуживать
все виды запросов.
`vitastor-cli serve` выставляет полную OpenAPI-спецификацию по адресу `/openapi`.
Запустите сервер и посмотрите её для подробной информации о доступных методах API.
Смотрите также подробную информацию о том, как устроены аутентификация и авторизация:
[Безопасность в Vitastor](security.ru.md).
## cpubench
`vitastor-cli cpubench [--json]`
Запустить тесты производительности CPU для криптографических операций: AES-256-GCM,
AES-256-XTS и xxhash3. Эта команда не обращается к кластеру — она только замеряет
скорость шифрования/хеширования локально.
Укажите опцию `--json`, чтобы получить вывод в машиночитаемом JSON-формате.
Примеры вывода на современных и старых процессорах см. в разделе
[Производительность шифрования](security.ru.md#производительность-шифрования).

View File

@@ -8,27 +8,15 @@
Vitastor has two file system implementations. Both can be used via `vitastor-nfs`.
Commands:
- [mount](#mount)
- [start](#start)
- [upgrade](#upgrade)
- [defrag](#defrag)
⚠️ Important: follow the instructions from [Linux NFS write size](#linux-nfs-write-size)
for optimal Vitastor NFS performance if you use EC and HDD and mount your NFS from Linux.
- [Pseudo-FS](#pseudo-fs)
- [VitastorFS](#vitastorfs)
- [Supported POSIX features](#supported-posix-features)
- [Limitations](#limitations)
- [Linux NFS](#linux-nfs)
- [Sync vs async safety](#sync-vs-async-safety)
- [Linux NFS readahead](#linux-nfs-readahead)
- [Linux NFS write size](#linux-nfs-write-size)
- [Horizontal scaling](#horizontal-scaling)
- [RDMA](#rdma)
- [Commands](#commands)
- [mount](#mount)
- [start](#start)
- [upgrade](#upgrade)
- [defrag](#defrag)
- [Mounting from /etc/fstab](#mounting-from-etc-fstab)
- [Common options](#common-options)
## Pseudo-FS
Simplified pseudo-FS proxy is used for file-based image access emulation. It's not
@@ -54,7 +42,7 @@ vitastor-nfs start --block --etcd_address 192.168.5.10:2379 --portmap 0 --port 2
To mount the FS exported by this server, run:
```
mount server:/ /mnt/ -o port=2050,mountport=2050,nfsvers=3,soft,sync,nolock,tcp
mount server:/ /mnt/ -o port=2050,mountport=2050,nfsvers=3,soft,nolock,tcp
```
## VitastorFS
@@ -117,57 +105,7 @@ Other notable missing features which should be addressed in the future:
in the DB. The FS is implemented is such way that this garbage doesn't affect its
function, but having a tool to clean it up still seems a right thing to do.
## Linux NFS
Linux NFS client has some specific features of behaviour important for Vitastor:
### Sync vs async safety
Linux NFS has 2 options which affect the mount mode: **soft/hard** and **sync/async**.
**⚠️ Warning: soft,async mode is dangerous and may lead to data loss!**
How these options work:
- In the **hard** mode all requests to the server are retried infinitely. If the NFS server becomes
unavailable, it becomes impossible to unmount the FS until you reboot the client host.
- In the **soft** each request is only tried 2 times (default **retrans** option value for TCP connections).
In this case, unmounting a dead FS is possible.
- In the **sync** mode all modification requests are sent to the server immediately. This is slower,
but allows the client to see all errors if they happen.
- In the **async** mode, modification requests are accumulated and sent to the server in the background
mode in parallel. It is faster, but it can hide write errors from the client. Instead of receiving an
error from a write operation, the client will only get an error when he closes the file (unsuccessful
close() call). Another problem is that there's no convenient way to set write parallelism for NFS, so
the client can overload the server.
That's exactly why **soft,async** is dangerous. When a large file is copied, the server is easily overwhelmed
with write requests and starts to respond slowly. Write requests time out, soft mode retries them only 1 time
and discards them, which results in parts of the file being lost.
So to recap: always use either **soft,sync** mode, or **hard,async**. By default, `vitastor-nfs mount` uses
**soft,sync** if mode options are not specified.
### Linux NFS readahead
By default, Linux uses rather small readahead value of 128 KB for NFS mounts.
For simple reading/copuing of files it's way too low because it stops VitastorFS from
using parallel reads.
However, you can increase readahead. You have 3 options:
1. Use the built-in `vitastor-nfs mount --readahead <SIZE>` option (for example, `--readahead 2M`).
2. Edit `[nfsrahead]` section in `/etc/nfs.conf` - add `nfs=2048` in it (global setting for all NFS 3.0 mounts).
3. Manually with the following command: \
`echo 2048 > /sys/class/bdi/$(findmnt -no MAJ:MIN /mnt/nfs 2>/dev/null | tr -d ' ')/read_ahead_kb`
Here, `2048` is the desired readahead in KB, and `/mnt/nfs` is your FS mountpoint.
You can easily see 2x-3x linear read improvement after you increase the parameter, so we
really urge you to try increasing it!
### Linux NFS write size
## Linux NFS write size
Linux NFS client (nfs/nfsv3/nfsv4 kernel modules) has a hard-coded maximum I/O size,
currently set to 1 MB - see `rsize` and `wsize` in [man 5 nfs](https://linux.die.net/man/5/nfs).
@@ -223,7 +161,7 @@ modprobe nfsv3
After these (not much complicated 🙂) manipulations NFS begins to be mounted
with new wsize and rsize by default and it fixes Vitastor-NFS linear write performance.
### Horizontal scaling
## Horizontal scaling
Linux NFS 3.0 client doesn't support built-in scaling or failover, i.e. you can't
specify multiple server addresses when mounting the FS.
@@ -262,7 +200,6 @@ Use regular `umount <MOUNTPOINT>` to unmount the FS.
The server will be automatically stopped when the FS is unmounted.
- `-o|--options <OPT>` - Pass additional NFS mount options (ex.: -o async).
- `--readahead <SIZE>` - Set Linux FS readahead to `<SIZE>`. Default is 128 KB, larger values increase sequential read speed (for example, try `--readahead 2M`).
### start
@@ -312,31 +249,6 @@ Options:
| `--include-empty` | Include old and empty volumes; make sure to restart NFS servers before using it |
| `--no-rm` | Move, but do not delete data |
## Mounting from /etc/fstab
Vitastor packages include mount helpers for `/etc/fstab`: **/usr/sbin/mount.vitastorfs** and **/usr/sbin/mount.vitastorblk**.
Both are simple symbolic links to `vitastor-nfs` and allow to handle `mount` calls correctly.
Example `/etc/fstab` entries:
```
myfs /S vitastorfs pool=fspool,readahead=2M,--etcd_address=10.0.2.15:2379,soft,sync 0 0
none /mnt/vitastor-block vitastorblk pool=ecpool 0 0
```
When you run `mount /S`, the helper is called internally and starts VitastorFS.
Helper call syntax is:
```
mount.vitastorfs <FSNAME> <MOUNTPOINT> -o <OPTS>
```
`<OPTS>` is comma-separated and includes all mount options from fstab. You can include
both Vitastor and NFS mount options in it. An option is treated as an NFS option if it
does not start with `--`, `pool=`, `readahead=` or if it doesn't contain `=` or `_`.
Other options are treated as Vitastor options.
## Common options
| <!-- --> | <!-- --> |
@@ -350,4 +262,3 @@ Other options are treated as Vitastor options.
| `--logfile <FILE>` | log to the specified file |
| `--enforce 1` | enforce permissions at the server side (no by default) |
| `--foreground 1` | stay in foreground, do not daemonize |
| `--trace` | trace all NFS requests |

View File

@@ -8,27 +8,15 @@
В Vitastor есть две реализации файловой системы. Обе используются через `vitastor-nfs`.
Команды:
- [mount](#mount)
- [start](#start)
- [upgrade](#upgrade)
- [defrag](#defrag)
⚠️ Важно: для оптимальной производительности Vitastor NFS в Linux при использовании
HDD и EC (erasure кодов) выполните инструкции из раздела [Размер записи Linux NFS](#размер-записи-linux-nfs).
- [Псевдо-ФС](#псевдо-фс)
- [VitastorFS](#vitastorfs)
- [Поддерживаемые функции POSIX](#поддерживаемые-функции-posix)
- [Ограничения](#ограничения)
- [NFS в Linux](#nfs-в-linux)
- [Безопасность режимов sync и async](#безопасность-режимов-sync-и-async)
- [Упреждающее чтение Linux NFS](#упреждающее-чтение-linux-nfs)
- [Размер записи Linux NFS](#размер-записи-linux-nfs)
- [Горизонтальное масштабирование](#горизонтальное-масштабирование)
- [RDMA](#rdma)
- [Команды](#команды)
- [mount](#mount)
- [start](#start)
- [upgrade](#upgrade)
- [defrag](#defrag)
- [Монтирование из /etc/fstab](#монтирование-из-etc-fstab)
- [Общие опции](#общие-опции)
## Псевдо-ФС
Упрощённая реализация псевдо-ФС используется для эмуляции файлового доступа к блочным
@@ -54,7 +42,7 @@ vitastor-nfs start --block --etcd_address 192.168.5.10:2379 --portmap 0 --port 2
Примонтировать ФС, запущенную с такими опциями, можно следующей командой:
```
mount server:/ /mnt/ -o port=2050,mountport=2050,nfsvers=3,soft,sync,nolock,tcp
mount server:/ /mnt/ -o port=2050,mountport=2050,nfsvers=3,soft,nolock,tcp
```
## VitastorFS
@@ -121,58 +109,7 @@ JSON-формате :-). Для инспекции содержимого БД
записи. ФС устроена так, что на работу они не влияют, но для порядка и их стоит
уметь подчищать.
## NFS в Linux
NFS в Linux имеет некоторые важные для Vitastor особенности поведения:
### Безопасность режимов sync и async
В Linux NFS есть 2 опции, влияющие на режим монтирования: **soft/hard** и **sync/async**.
**⚠️ Внимание: сочетание soft,async опасно и может приводить к потере данных!**
Логика работы опций:
- В режиме **hard** все запросы к серверу повторяются бесконечно. Если NFS-сервер становится недоступен,
отмонтировать ФС становится невозможно до перезагрузки клиентской машины.
- В режиме **soft** каждый запрос повторяется только 2 раза (значение опции **retrans** по умолчанию для
TCP соединений). В этом случае отмонтировать умершую ФС можно.
- В режиме **sync** все запросы изменения отправляются на сервер сразу. Это медленнее, но зато любую
ошибку записи клиент видит сразу.
- В режиме **async** запросы изменения накапливаются и отправляются на сервер в фоновом режиме, параллельно.
Это быстрее, но ошибки записи могут быть незаметны для клиента и вместо ошибки записи клиент может
получить ошибку в момент закрытия файла (вызова close()). Помимо этого, в NFS нет нормального способа
регулировать параллелизм записи, поэтому клиент может легко перегружать сервер большим количеством команд.
Именно поэтому режим **soft,async** опасен. При копировании любого большого файла сервер перегружается и
начинает медленно отвечать. Команды записи заканчиваются таймаутом, soft режим делает 1 повторную попытку,
после чего забывает команды совсем. Часть блоков файла оказывается просто потерянной.
Резюме: всегда используйте либо режим **soft,sync**, либо **hard,async**. Если опции `vitastor-nfs mount`
не указаны, режим по умолчанию - **soft,sync**.
### Упреждающее чтение Linux NFS
По умолчанию Linux использует довольно небольшой размер упреждающего чтения для
файловых систем NFS - 128 КБ.
Для простого чтения/копирования файлов его не достаточно, потому что оно мешает VitastorFS
нормально утилизировать параллелизм чтения.
Однако, вы можете увеличить эту настройку целыми 3 способами:
1. Встроенной опцией `vitastor-nfs mount --readahead <SIZE>` (например, `--readahead 2M`).
2. Добавив в секцию `[nfsrahead]` файла конфигурации `/etc/nfs.conf` строку `nfs=2048` (глобальная
настройка для всех NFS 3.0 точек монтирования).
3. Вручную следующей командой: \
`echo 2048 > /sys/class/bdi/$(findmnt -no MAJ:MIN /mnt/nfs 2>/dev/null | tr -d ' ')/read_ahead_kb`
Здесь `2048` - размер readahead, а `/mnt/nfs` - путь к вашей ФС.
После увеличения параметра скорость линейного чтения легко может увеличиться в 2-3 раза, так что
советуем обязательно попробовать!
### Размер записи Linux NFS
## Размер записи Linux NFS
Клиент Linux NFS (модули ядра nfs/nfsv3/nfsv4) имеет фиксированный в коде максимальный
размер запроса ввода-вывода, равный 1 МБ - см. `rsize` и `wsize` в [man 5 nfs](https://linux.die.net/man/5/nfs).
@@ -232,7 +169,7 @@ modprobe nfsv3
монтироваться с новыми wsize и rsize, и производительность линейной записи в Vitastor-NFS
исправляется.
### Горизонтальное масштабирование
## Горизонтальное масштабирование
Клиент Linux NFS 3.0 не поддерживает встроенное масштабирование или отказоустойчивость.
То есть, вы не можете задать несколько адресов серверов при монтировании ФС.
@@ -272,9 +209,6 @@ VitastorFS из GPUDirect.
Сервер автоматически останавливается при отмонтировании ФС.
- `-o|--options <OPT>` - Передать дополнительные опции монтирования NFS (пример: -o async).
- `--readahead <SIZE>` - Устанавливает размер упреждающей записи ФС Linux в `<SIZE>`.
Значение по умолчанию 128 KB, бОльшие значения значительно улучшают скорость линейного чтения.
Попробуйте, например, `--readahead 2M`.
### start
@@ -327,32 +261,6 @@ VitastorFS из GPUDirect.
| `--include-empty` | Дефрагментировать старые и пустые тома; обязательно перезапустите NFS-сервера после использования этой опции |
| `--no-rm` | Перемещать, но не удалять данные |
## Монтирование из /etc/fstab
Пакеты Vitastor включают хелперы для монтирования из `/etc/fstab`: **/usr/sbin/mount.vitastorfs** и **/usr/sbin/mount.vitastorblk**.
Оба являются простыми символическими ссылками на `vitastor-nfs` и дают возможность
корректно обрабатывать вызовы монтирования утилитой `mount`.
Примеры записей `/etc/fstab`:
```
myfs /S vitastorfs pool=fspool,readahead=2M,--etcd_address=10.0.2.15:2379,soft,sync 0 0
none /mnt/vitastor-block vitastorblk pool=ecpool 0 0
```
Когда вы запускаете `mount /S`, внутри вызывается хелпер и запускает VitastorFS.
Синтаксис вызова самого хелпера:
```
mount.vitastorfs <FSNAME> <MOUNTPOINT> -o <OPTS>
```
`<OPTS>` - это набор опций, разделённых запятой, и в нём передаются все опции
монтирования из fstab. В него можно включать и опции Vitastor, и опции NFS. Опция
считается опцией NFS, если она не начинается с `--`, `pool=` и `readahead=`, а также
если она не содержит `=` или `_`. Все прочие опции считаются опциями Vitastor.
## Общие опции
| <!-- --> | <!-- --> |
@@ -366,4 +274,3 @@ mount.vitastorfs <FSNAME> <MOUNTPOINT> -o <OPTS>
| `--logfile <FILE>` | записывать логи в заданный файл |
| `--enforce 1` | проверять права доступа на стороне сервера (по умолчанию нет) |
| `--foreground 1` | не уходить в фон после запуска |
| `--trace` | логгировать все запросы NFS |

View File

@@ -1,665 +0,0 @@
[Documentation](../../README.md#documentation) → Usage → Security in Vitastor
-----
[Читать на русском](security.ru.md)
# Security in Vitastor
- [Overview](#overview)
- [Quick setup](#quick-setup)
- Principles of operation
- [etcd transport encryption (TLS)](#etcd-transport-encryption-tls)
- [OSD transport encryption (AES-GCM)](#osd-transport-encryption-aes-gcm)
- [End-to-end image data encryption (AES-XTS)](#end-to-end-image-data-encryption-aes-xts)
- [Certificate-based authentication](#certificate-based-authentication)
- [Users and access rights](#users-and-access-rights)
- [etcd privileges](#etcd-privileges)
- Manual setup
- [Configuring OSD transport encryption](#configuring-osd-transport-encryption)
- etcd/Antietcd setup options
- [Mon with embedded Antietcd](#mon-with-embedded-antietcd)
- [Mon as an Etcd proxy](#mon-as-an-etcd-proxy)
- [Mon with a separate Antietcd Proxy](#mon-with-a-separate-antietcd-proxy)
- [Standalone Antietcd without etcd](#standalone-antietcd-without-etcd)
- [Vault/OpenBao setup](#vaultopenbao-setup)
- [Vault setup example](#vault-setup-example)
- Lists of allowed operations
- [etcd data access rights](#etcd-data-access-rights)
- [OSD data access rights](#osd-data-access-rights)
- [API access rights](#api-access-rights)
- [Encryption performance](#encryption-performance)
## Overview
Starting from version 3.1.0, Vitastor provides full data protection:
control plane protection (etcd), data plane protection (OSDs), and end-to-end data encryption.
- Control plane protection:
- etcd transport encryption (TLS)
- Authentication via client TLS (X.509) certificates
- Access control of clients to etcd data
- Data plane protection:
- Full AES-GCM encryption of OSD transport (similar to TLS, but faster)
- Alternatively, AES-GCM encryption of just operation headers with data checksums using a secret "salt"
- Authentication via client TLS (X.509) certificates
- Access control of clients on the OSD side
- End-to-end encryption:
- Data is encrypted using AES-XTS on the client side, the Vitastor cluster has no access to plaintext data
- AES-XTS keys can be stored in etcd or in an external Vault/OpenBao
All features are optional and disabled in the simplest configuration. By default, only
transport-level data checksums ([proto_checksums](../config/security.en.md#proto_checksums)=payload)
are enabled for clients that support them (>= 3.1.0). For older clients, connections
without data checksums are allowed by default ([force_proto_checksums](../config/security.en.md#force_proto_checksums) is empty).
For a quick setup, jump to the [Quick setup](#quick-setup) section.
Descriptions of all security-related parameters can be found [here](../config/security.en.md).
## Quick setup
For a quick setup, use the `/usr/lib/vitastor/mon/make-etcd` script:
1. Log in to the node where the first monitor and etcd will be located.
2. Create `/etc/vitastor/vitastor.conf` with minimal parameters: etcd_address,
osd_network and, if you want to enable privileges, use_perms (note `https://`
in etcd addresses):
```
{
"etcd_address": ["https://10.0.0.10:2379","https://10.0.0.11:2379","https://10.0.0.12:2379"],
"osd_network": "10.0.0.0/24",
"use_perms": true
}
```
3. Run `/usr/lib/vitastor/mon/make-etcd` without parameters or with the `--antietcd-only`
parameter if you want to initialize the cluster with Antietcd only, without etcd.
4. The script will generate all necessary certificates and offer to copy them to the other
monitor nodes. Root ssh access to these nodes is required to do it, so agree if it's
available.
5. If you don't have root ssh access from the first node to other nodes, copy configuration
manually (vitastor.conf and all certificates/keys, except CA `*_ca.key`) and call
`/usr/lib/vitastor/mon/make-etcd` on every node.
After that, you can proceed with OSD initialization.
If you want to understand the setup in more detail, read the [Principles of operation](#principles-of-operation)
and [Manual setup](#manual-setup) sections below.
## Principles of operation
### etcd transport encryption (TLS)
Possible setups:
- Without encryption (http)
- With encryption (https)
- With encryption and client certificate authentication. Either the same certificate
used for authentication on the OSD side (`cert`+`pkey` / `osd_cert`+`osd_pkey`)
is used, or a separately specified certificate (`etcd_client_cert`+`etcd_client_key`).
### OSD transport encryption (AES-GCM)
Possible setups:
- Unencrypted transport without checksums: `proto_checksums=none`.
- Unencrypted transport with data checksums: `proto_checksums=payload` (may be omitted,
this is the default value). It's allowed to disable checksums on the client side, or
use an older client that does not support checksums. If you want to block connections
from clients without checksums, use the option `force_proto_checksums=payload`.
- Header-only encryption with data checksums: activated when the options
`cert`, `pkey`, `osd_ca` are set on the client side and `osd_cert`, `osd_pkey`, `osd_ca`, `client_ca`
on the OSD side, with `proto_checksums=payload`. In this mode, disabling checksums on the client
side is forbidden by default, i.e. `force_proto_checksums=payload` is used.
- Full transport encryption of all traffic: same as the previous option, but with `proto_checksums=gcm`.
In this case, clients are by default allowed to downgrade to checksums only, but this
can also be forbidden via `force_proto_checksums=gcm`. This is the slowest setup and
it's only recommended for insecure (public) networks. In particular, full traffic
encryption together with end-to-end AES-XTS image encryption encrypts data twice.
Encryption uses the AES-256-GCM algorithm and a custom simplified key exchange protocol,
fully analogous to TLS 1.3 ECDHE.
### End-to-end image data encryption (AES-XTS)
The Vitastor client supports encrypting each image's data with its own key. In this case,
data is encrypted by the client before sending it to OSDs and OSDs can't see it in plain.
The encryption key can be changed when cloning/creating image snapshots. For example,
you can make a base VM image (say, Debian Linux) unencrypted, but have encrypted client VM
images inheriting from it.
Image encryption keys can be stored in etcd or in an external Vault. In the latter case,
etcd only stores key IDs and Vitastor cluster can't decrypt the data at all. To use
Vault, create an image with the `--enc_key vault:ID` option, specify vault_url and vault_ca
options in the configuration, create accounts for all clients in Vault, and grant them access
to the required v1 secrets.
Once again, if AES-XTS is used together with full traffic encryption (`proto_checksums=gcm`),
image data is encrypted twice — first with AES-XTS, and then with AES-GCM. Use it only if
you are completely paranoid :-).
### Certificate-based authentication
When encryption is enabled, Vitastor clients, OSDs, and monitors authenticate via certificates
for both etcd (Antietcd) and OSD connections.
Separate certificates must be used for OSDs and monitors — either self-signed, or signed
by separate CAs (`osd_ca` and `mon_ca`). All OSDs can use the same certificate, and all
monitors can also use the same certificate, since the privileges of different OSDs or
different monitors do not differ (theoretically, one could differentiate OSD certificates
by pool, but there has been no need for this so far).
Also, a monitor certificate may not be needed at all if Antietcd is embedded into the monitor
itself. In this case, the monitor already has access to all etcd data directly in memory.
### Users and access rights
When transport encryption is disabled, Vitastor operates without access control, i.e.,
any cluster client has full access to both the management layer and the data layer. This
option is suitable for dedicated trusted storage networks.
When OSD transport encryption is enabled (at least for headers), you can enable access
control by turning on the `use_perms=true` option. When this option is enabled, each user
(and even an OSD or a monitor) can perform only operations allowed for him.
Each user, administrator, OSD or monitor must have their own certificate signed by a specific
root certificate:
- `client_ca` for regular users. Common Name of the certificate must be equal to the user's
name. Regular users can only read and modify explicitly permitted images and can't modify
cluster state.
- `admin_ca` for administrators. Administrators can read and modify all images, and also
adminster the cluster: view overall statistics and status, create and delete OSDs, etc.
Common Name is not checked for admin users.
- `osd_ca` for OSDs and `mon_ca` for monitors (if Antietcd runs separately). OSDs and monitors
also have distinct permission sets.
User privileges for regular users are stored in etcd data and currently consist of only
one property in `/vitastor/config/user/<name>`:
- `groups` - List of group names the user is a member of.
Images have the following properties:
- Owner (`owner`) — the user name that is allowed to both read and modify the image
- Owner group (`owner_group`) — the owner group name
- Reader group (`reader_group`) — the name of the group of users allowed to read the image
And there is also a property on the pool:
- Creator group (`creator_group`) — the name of the group of users allowed to create images in the pool
For the list of allowed operations on image data on the OSD side, see the
[OSD data access rights](#osd-data-access-rights) section.
### etcd privileges
etcd privileges are implemented through Antietcd in all modes of operation.
Built-in etcd privileges are not supported due to numerous inconveniences:
- Certificate-based authentication does not work at all in etcd's REST interface,
- Privileges are stored separately from k/v data and cannot participate in transactions,
- Only the administrator (root) can change privileges,
- There is no support for filtering range read responses by privileges.
If etcd is used, Antietcd acts as a filtering proxy and can be embedded in the Vitastor
monitor or run separately. In this case, etcd must allow incoming connections only from
Antietcd, and all other components must connect to Antietcd.
If Antietcd runs as a part of the Vitastor monitor, it is sufficient to enable the option
`use_perms=true` and set the required certificates. If Antietcd is run separately, privileges
have to be enabled separately using Antietcd options. For more details on the setup, see
the [etcd/Antietcd setup options](#etcdantietcd-setup-options) section.
For the list of allowed operations with etcd data, see the
[etcd data access rights](#etcd-data-access-rights) section.
## Manual setup
### Configuring OSD transport encryption
You need 3 certificates: one for OSDs, one for signing client certificates and one for
signing administrator certificates. For OSDs, you can use a self-signed certificate (osd_ca.crt)
or a separate certificate (osd.crt) signed by a trusted osd_ca.crt certificate. For clients,
you must use separate certificates signed by a common CA certificate (client_ca.crt). One more
CA certificate (admin_ca.crt) is required for signing administrator certificates.
Add to the Vitastor configuration on OSD servers:
- use_perms: true
- osd_ca: osd_ca.crt
- client_ca: client_ca.crt
- admin_ca: admin_ca.crt
- osd_cert: osd_ca.crt
- osd_pkey: osd_ca.key
On the client side:
- use_perms: true
- cert: client.crt
- pkey: client.key
### etcd/Antietcd setup options
The following configuration options are available:
#### Mon with embedded Antietcd
The simplest option. Apart from OSD certificates, you only need 1 certificate for Antietcd (antietcd.crt).
Vitastor settings (`/etc/vitastor/vitastor.conf`):
- etcd_address: [ "http://mon1:2379", ... ] (addresses of your monitors with port 2379)
- use_perms: true
- use_antietcd: true
- antietcd_cert: antietcd.crt
- antietcd_key: antietcd.key
- etcd_ca: antietcd.crt
- osd_ca: osd_ca.crt
- client_ca: client_ca.crt
- admin_ca: admin_ca.crt
#### Mon as an Etcd proxy
If you want to enable privileges, but stay on etcd, you can use etcd proxy mode.
You will need 2 separate certificates: one for etcd (etcd.crt) and one for antietcd (antietcd.crt).
The etcd client port must be different from the standard 2379 — for example, you can pick 2381.
Vitastor settings:
- etcd_address: [ "http://mon1:2379", ... ] (addresses of your monitors with port 2379)
- use_perms: true
- use_antietcd: true
- etcd_proxy:
```
{
"urls": [ "http://mon1:2381", ... ], // addresses of your etcd with port 2381
"cert": "antietcd.crt",
"key": "antietcd.key",
"ca": "etcd.crt"
}
```
- antietcd_cert: antietcd.crt
- antietcd_key: antietcd.key
- etcd_ca: antietcd.crt
- osd_ca: osd_ca.crt
- client_ca: client_ca.crt
- admin_ca: admin_ca.crt
etcd command-line options:
```
--advertise-client-urls=https://<ADDRESS>:2381 --listen-client-urls=https://<ADDRESS>:2381 \
--client-cert-auth --cert-file=etcd.crt --key-file=etcd.key --trusted-ca-file=antietcd.crt \
--peer-client-cert-auth --peer-cert-file=etcd.crt --peer-key-file=etcd.key --peer-trusted-ca-file=etcd.crt
```
#### Mon with a separate Antietcd Proxy
If in addition to the previous option you want to offload Vitastor monitor's tasks from Antietcd,
you can run it separately.
Similar to the previous option, 2 certificates are needed: one for etcd and one for antietcd,
plus a separate certificate for the monitor.
Vitastor settings:
- etcd_address: [ "http://mon1:2379", ... ] (addresses of your monitors with port 2379)
- use_perms: true
- use_antietcd: false
- etcd_ca: antietcd.crt
- osd_ca: osd_ca.crt
- client_ca: client_ca.crt
- admin_ca: admin_ca.crt
- mon_etcd_client_cert: mon_ca.crt
- mon_etcd_client_key: mon_ca.key
Antietcd command-line options:
```
--port 2379 --cert /etc/vitastor/antietcd.crt --key /etc/vitastor/antietcd.key \
--client_cert_auth 1 --auth_filter /usr/lib/vitastor/mon/vitastor_auth_filter.js \
--ca /etc/vitastor/client_ca.crt --admin_ca /etc/vitastor/admin_ca.crt \
--osd_ca /etc/vitastor/osd_ca.crt --mon_ca /etc/vitastor/mon_ca.crt \
--etcd_proxy url1,url2,... --etcd_ca /etc/vitastor/etcd.crt \
--etcd_cert /etc/vitastor/antietcd.crt --etcd_key /etc/vitastor/antietcd.key
```
etcd command-line options (same as in the previous option):
```
--advertise-client-urls=https://<ADDRESS>:2381 --listen-client-urls=https://<ADDRESS>:2381 \
--client-cert-auth --cert-file=etcd.crt --key-file=etcd.key --trusted-ca-file=antietcd.crt \
--peer-client-cert-auth --peer-cert-file=etcd.crt --peer-key-file=etcd.key --peer-trusted-ca-file=etcd.crt
```
#### Standalone Antietcd without etcd
Same as the previous option, but etcd and its certificate are not needed:
Vitastor settings (same as in the previous option):
- etcd_address: [ "http://mon1:2379", ... ] (addresses of your monitors with port 2379)
- use_perms: true
- use_antietcd: false
- etcd_ca: antietcd.crt
- osd_ca: osd_ca.crt
- client_ca: client_ca.crt
- admin_ca: admin_ca.crt
- mon_etcd_client_cert: mon_ca.crt
- mon_etcd_client_key: mon_ca.key
Antietcd command-line options:
```
--port 2379 --cert /etc/vitastor/antietcd.crt --key /etc/vitastor/antietcd.key \
--client_cert_auth 1 --auth_filter /usr/lib/vitastor/mon/vitastor_auth_filter.js \
--ca /etc/vitastor/client_ca.crt --admin_ca /etc/vitastor/admin_ca.crt \
--osd_ca /etc/vitastor/osd_ca.crt --mon_ca /etc/vitastor/mon_ca.crt \
--persist_filter /usr/lib/vitastor/mon/vitastor_persist_filter.js
```
### Vault/OpenBao setup
To use Vault, each client that needs to get image keys from Vault needs a Vault account.
Vitastor only supports client certificate-based authentication, so all client certificates
(`cert`+`pkey`) must be registered in Vault, and they must be granted access to the
corresponding secrets (v1 secrets API is supported).
The required format of a Vault secret is a single `key` field as a hexadecimal string.
The AES-256-XTS algorithm is used, so the key length is 64 bytes, i.e., the string must
consist of 128 hexadecimal digits.
To connect to Vault, set the following settings in Vitastor.conf:
- `vault_url` — Vault address (e.g., `https://vault:8200`)
- `vault_ca` — Vault's own certificate
After that, if you create an image (`vitastor-cli create`) with the option `--enc_key vault:<ID>`,
Vitastor clients will first contact Vault to obtain a token at `/v1/auth/cert/login`,
and then request the actual secret from Vault at `/v1/secret/<ID>`.
#### Vault setup example
Step-by-step instructions for setting up a test Vault using OpenBao as an example:
1. If TLS is not yet configured, generate a self-signed TLS certificate for Vault:
```
openssl req -days 3650 -x509 -addext basicConstraints=critical,CA:TRUE,pathlen:1 --addext subjectAltName=DNS:vault \
-new -newkey rsa:4096 -nodes -keyout /etc/openbao/vault.key -out /etc/openbao/vault.crt
```
Configure it in `/etc/openbao/openbao.hcl`:
```
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/etc/openbao/vault.crt"
tls_key_file = "/etc/openbao/vault.key"
}
```
And restart OpenBao (`systemctl restart openbao`).
2. Copy Vault's TLS certificate for Vitastor:
```
cp /etc/openbao/vault.crt /etc/vitastor/vault.crt
```
Transfer it to all client nodes and specify it in `/etc/vitastor/vitastor.conf`:
```
{
...
"vault_url": "http://vault:8200",
"vault_ca": "/etc/vitastor/vault.crt"
}
```
3. Check Vault status:
```
bao status -ca-cert /etc/openbao/vault.crt -address=https://vault:8200
```
4. Initialize Vault in test mode from 1 node (with 1 key share):
```
bao operator init -n 1 -t 1 -ca-cert /etc/openbao/vault.crt -address=https://vault:8200
```
5. Unseal Vault:
```
bao operator unseal -ca-cert /etc/openbao/vault.crt -address=https://vault:8200
```
6. Enable certificate-based authentication:
```
bao auth enable -ca-cert /etc/openbao/vault.crt -address=https://vault:8200 cert
```
7. Enable v1 secrets:
```
bao secrets enable -ca-cert /etc/openbao/vault.crt -address=https://vault:8200 -path=secret kv-v1
```
8. Create a test secret:
```
bao kv put -ca-cert /etc/openbao/vault.crt -address=https://vault:8200 secret/vitastor/testimg3 key=$(openssl rand -hex 64)
```
9. Generate a signed certificate for a Vitastor user (on a machine where you have `client_ca.crt` and `client_ca.key`):
```
openssl req -subj '/CN=testimg3' -nodes -new -keyout testimg3.key -out testimg3.csr
openssl x509 -req -days 3650 -CA client_ca.crt -CAkey client_ca.key -CAcreateserial -in testimg3.csr -out testimg3.crt
rm testimg3.csr
```
10. Create a user in Vault and grant it access to the secret:
```
cat >testimg3.policy <<EOF
path "/secret/vitastor/testimg3" {
capabilities = ["read"]
}
EOF
bao policy write -ca-cert /etc/openbao/vault.crt -address=https://vault:8200 testimg3 testimg3.policy
bao write -ca-cert /etc/openbao/vault.crt -address=https://vault:8200 auth/cert/certs/testimg3 \
certificate=@testimg3.crt display_name=testimg3 token_ttl=24h token_policies=testimg3
```
11. Test access to the secret:
```
curl --cacert /etc/vitastor/vault.crt --cert testimg3.crt --key testimg3.key \
--json '{}' https://vault:8200/v1/auth/cert/login
```
A token will be printed, substitute it into the following request:
```
curl --cacert /etc/vitastor/vault.crt --cert testimg3.crt --key testimg3.key \
-H 'X-Vault-Token: <RECEIVED TOKEN>' https://vault:8200/v1/secret/vitastor/testimg3
```
12. Create an image in Vitastor with the given secret (as an administrator or someone who
has the right to create images in your pool):
```
vitastor-cli create -s 100G --enc_key vault:vitastor/testimg3 --owner testimg3 testimg3
```
13. Test access to the image as user testimg3:
```
vitastor-cli --cert testimg3.crt --pkey testimg3.key dd if=/dev/urandom oimg=testimg3 bs=1M count=100
```
## Lists of allowed operations
### etcd data access rights
Below, all key names are given without the common prefix `/vitastor`.
Allowed operations with keys in Antietcd for regular users:
- Read-only:
- Always allowed:
- `/config/global`
- `/config/node_placement`
- `/config/pools`
- `/pg/config`
- `/osd/state/*`
- `/pg/state/*`
- `/index/maxid/*`
- For images [readable by the user](#users-and-access-rights):
- `/config/inode/*`
- `/index/image/*`
- `/inode/stats/*`
- Read and write:
- For pools in which the user can create images:
- `/index/maxid/*`
- For images owned by the user:
- `/config/inode/*`
- `/index/image/*`
Allowed operations with keys in Antietcd for administrators:
- Read:
- `/stats`
- `/mon/*`
- `/pg/*`
- `/pgstats/*`
- `/inode/stats/*`
- `/pool/stats/*`
- Read and write:
- `/config/*`
- `/osd/*`
- `/index/*`
- `/pg/config`
- `/pg/history/*`
- `/stats`
- `/history/last_clean_pgs`
Allowed operations with keys in etcd for OSDs:
- Read:
- `/pg/config`
- `/config/*`
- Read and write:
- `/osd/*`
- `/pg/state/*`
- `/pg/history/*`
- `/pgstats/*`
Allowed operations with keys in etcd for monitors:
- Read:
- `/config/*`
- `/osd/*`
- `/pgstats/*`
- Read and write:
- `/pg/config`
- `/stats`
- `/history/last_clean_pgs`
- `/mon/*`
- `/pg/history/*`
- `/pg/stats/*`
- `/pgstats/*`
- `/inode/stats/*`
- `/pool/stats/*`
### OSD data access rights
When the `use_perms` option and encryption are enabled, OSDs authenticate clients via
certificates and allow each client only what is allowed by the access control model.
Client operations:
- READ — allowed for images the user has read access to.
- WRITE, DELETE, SCRUB — allowed for images the user has write access to.
- SYNC — the operation is not tied to an image and is always allowed.
- DESCRIBE — the operation is allowed only for administrators (used by the commands
`vitastor-cli describe` and `fix`).
- PING — the operation is always allowed.
- SHOW_CONFIG — the operation is always allowed, however, if the client presents
itself as an OSD in it, then it is verified that it uses a certificate signed by `osd_ca`.
- SEC_LIST (listing) — allowed for other OSDs and administrators with any parameters,
and for regular clients only allowed for requests limited to an image the user has
read access to.
Cluster operations — allowed only for other OSDs:
- SEC_READ
- SEC_WRITE
- SEC_WRITE_STABLE
- SEC_SYNC
- SEC_STABILIZE
- SEC_ROLLBACK
- SEC_DELETE
- SEC_READ_BMP
- SEC_LOCK
### API access rights
[vitastor-cli serve](../usage/cli.en.md#serve) also supports client authentication
via certificates. Only certificates signed by `client_ca` are accepted. A separate
certificate `api_cert` with the key `api_pkey` is used as the server certificate.
For `vitastor-cli serve` to work correctly, it must use a certificate of an administrator
(`cert`+`pkey` signed by `admin_ca`) to access Vitastor and serve all requests correctly.
Regular users (with `client_ca`-signed certificates), when accessing the API, are only
allowed operations on images available to them either for reading (for read-only operations)
or for writing (for modification):
- image/list — for images the user can read.
- image/create — for pools in which the user is allowed to create images, or for
creating snapshots of images owned by the user.
- image/delete, image/flatten, image/modify — for images owned by the user.
All other operations are allowed only for administrators.
## Encryption performance
You may wonder — how fast is all this wonderful encryption?
The answer is — it depends heavily on the CPU. On modern processors (with AVX512 with VAES
support) it is very fast — AES encryption speed can reach 10-20 GB/s and above. This
primarily concerns the CPU of client machines, because end-to-end encryption is performed
entirely on the client, and even though transport encryption runs both on clients and OSDs,
each client has only one thread, while there are many OSDs on the server side.
On older processors, the speed is noticeably worse — for example, on a Xeon E5 v4 it is
only 3 GB/s.
You can evaluate the performance of your processors using the `vitastor-cli cpubench` command.
Example output (💪 AMD EPYC 9575F):
```
$ vitastor-cli cpubench
Vitastor transport encryption benchmark (AES-256-GCM, AES-256-XTS and xxhash3)
Warmup...
No transport encryption, data checksums enabled, e2e unencrypted image
xxhash3 1 M block... 209000 iterations in 2001 ms = 104447.78 MB/s
xxhash3 4 K block... 37000000 iterations in 2022 ms = 71479.35 MB/s
Header encryption with payload checksums, e2e unencrypted image
AES-256-GCM encrypt header + xxhash3 1 M block... 210000 iterations in 2015 ms = 104218.36 MB/s
AES-256-GCM encrypt header + xxhash3 4 K block... 26000000 iterations in 2073 ms = 48993.01 MB/s
Full transport encryption, e2e unencrypted image
AES-256-GCM encrypt header and 1 M block... 54000 iterations in 2000 ms = 27000.00 MB/s
AES-256-GCM encrypt header and 4 K block... 11700000 iterations in 2014 ms = 22692.71 MB/s
No transport encryption, no checksums, e2e encrypted image
AES-256-XTS encrypt 1 M block... 50000 iterations in 2039 ms = 24521.82 MB/s
AES-256-XTS encrypt 4 K block... 12600000 iterations in 2009 ms = 24499.13 MB/s
No transport encryption, e2e encrypted image, data checksums enabled
AES-256-XTS encrypt + xxhash3 1 M block... 40000 iterations in 2013 ms = 19870.84 MB/s
AES-256-XTS encrypt + xxhash3 4 K block... 10200000 iterations in 2011 ms = 19812.90 MB/s
Header encryption with payload checksums, e2e encrypted image
AES-256-GCM encrypt header + AES-256-XTS encrypt + xxhash3 1 M block... 40000 iterations in 2014 ms = 19860.97 MB/s
AES-256-GCM encrypt header + AES-256-XTS encrypt + xxhash3 4 K block... 8700000 iterations in 2011 ms = 16899.24 MB/s
Full transport encryption, e2e encrypted image
AES-256-XTS + AES-256-GCM encrypt 1 M block... 26000 iterations in 2062 ms = 12609.12 MB/s
AES-256-XTS + AES-256-GCM encrypt 4 K block... 6300000 iterations in 2006 ms = 12267.88 MB/s
```
And here is Xeon E5-2680v4:
```
$ vitastor-cli cpubench
Vitastor transport encryption benchmark (AES-256-GCM, AES-256-XTS and xxhash3)
Warmup...
No transport encryption, data checksums enabled, e2e unencrypted image
xxhash3 1 M block... 62000 iterations in 2021 ms = 30677.88 MB/s
xxhash3 4 K block... 12400000 iterations in 2006 ms = 24146.31 MB/s
Header encryption with payload checksums, e2e unencrypted image
AES-256-GCM encrypt header + xxhash3 1 M block... 62000 iterations in 2027 ms = 30587.07 MB/s
AES-256-GCM encrypt header + xxhash3 4 K block... 6800000 iterations in 2011 ms = 13208.60 MB/s
Full transport encryption, e2e unencrypted image
AES-256-GCM encrypt header and 1 M block... 7000 iterations in 2317 ms = 3021.15 MB/s
AES-256-GCM encrypt header and 4 K block... 1500000 iterations in 2102 ms = 2787.52 MB/s
No transport encryption, no checksums, e2e encrypted image
AES-256-XTS encrypt 1 M block... 7000 iterations in 2317 ms = 3021.15 MB/s
AES-256-XTS encrypt 4 K block... 1600000 iterations in 2088 ms = 2993.30 MB/s
No transport encryption, e2e encrypted image, data checksums enabled
AES-256-XTS encrypt + xxhash3 1 M block... 6000 iterations in 2188 ms = 2742.23 MB/s
AES-256-XTS encrypt + xxhash3 4 K block... 1400000 iterations in 2053 ms = 2663.78 MB/s
Header encryption with payload checksums, e2e encrypted image
AES-256-GCM encrypt header + AES-256-XTS encrypt + xxhash3 1 M block... 6000 iterations in 2190 ms = 2739.73 MB/s
AES-256-GCM encrypt header + AES-256-XTS encrypt + xxhash3 4 K block... 1300000 iterations in 2101 ms = 2417.00 MB/s
Full transport encryption, e2e encrypted image
AES-256-XTS + AES-256-GCM encrypt 1 M block... 4000 iterations in 2666 ms = 1500.38 MB/s
AES-256-XTS + AES-256-GCM encrypt 4 K block... 800000 iterations in 2113 ms = 1478.94 MB/s
```

View File

@@ -1,670 +0,0 @@
[Документация](../../README-ru.md#документация) → Использование → Безопасность в Vitastor
-----
[Read in English](security.en.md)
# Безопасность в Vitastor
- [Обзор](#обзор)
- [Быстрая настройка](#быстрая-настройка)
- Принципы работы
- [Шифрование соединений с etcd (TLS)](#шифрование-соединений-с-etcd-tls)
- [Шифрование соединений с OSD (AES-GCM)](#шифрование-соединений-с-osd-aes-gcm)
- [Сквозное шифрование данных образов (AES-XTS)](#сквозное-шифрование-данных-образов-aes-xts)
- [Аутентификация по сертификатам](#аутентификация-по-сертификатам)
- [Пользователи и права доступа](#пользователи-и-права-доступа)
- [Привилегии etcd](#привилегии-etcd)
- Ручная настройка
- [Настройка шифрования соединений OSD](#настройка-шифрования-соединений-osd)
- Варианты настройки etcd/Antietcd
- [Mon со встроенным Antietcd](#mon-со-встроенным-antietcd)
- [Mon в роли Etcd proxy](#mon-в-роли-etcd-proxy)
- [Mon с отдельным Antietcd Proxy](#mon-с-отдельным-antietcd-proxy)
- [Отдельный Antietcd без etcd](#отдельный-antietcd-без-etcd)
- [Настройка Vault/OpenBao](#настройка-vaultopenbao)
- [Пример настройки Vault](#пример-настройки-vault)
- Списки разрешённых операций
- [Права доступа к данным etcd](#права-доступа-к-данным-etcd)
- [Права доступа к данным OSD](#права-доступа-к-данным-osd)
- [Права доступа к API](#права-доступа-к-api)
- [Производительность шифрования](#производительность-шифрования)
## Обзор
Начиная с версии 3.1.0, Vitastor предоставляет полную защиту данных: защиту слоя
управления (etcd), защиту слоя данных (OSD) и сквозное шифрование данных.
- Защита слоя управления:
- Шифрование соединений с etcd (TLS)
- Аутентификация по клиентским TLS (X.509) сертификатам
- Контроль доступа клиентов к данным etcd
- Защита слоя данных:
- Либо полное AES-GCM шифрование соединений с OSD (аналогично TLS, но быстрее)
- Либо шифрование AES-GCM только заголовков команд с контрольными суммами данных с секретной "солью"
- Аутентификация по клиентским TLS (X.509) сертификатам
- Контроль доступа клиентов на стороне OSD
- Сквозное шифрование:
- Данные шифруются AES-XTS на стороне клиента, кластер Vitastor не имеет доступа к открытым данным
- Ключи AES-XTS могут храниться в etcd или во внешнем Vault/OpenBao
Все функции опциональны и в простейшем варианте настройки выключены. По умолчанию включены
только контрольные суммы данных на транспортном уровне ([proto_checksums](../config/security.ru.md#proto_checksums)=payload) для
поддерживающих их клиентов (>= 3.1.0). Для более старых клиентов по умолчанию разрешены
соединения без контрольных сумм данных ([force_proto_checksums](../config/security.ru.md#force_proto_checksums) пусто).
Для быстрой настройки перейдите к разделу [Быстрая настройка](#быстрая-настройка).
Описания всех параметров, связанных с безопасностью, читайте [здесь](../config/security.ru.md).
## Быстрая настройка
Для быстрой настройки используйте скрипт `/usr/lib/vitastor/mon/make-etcd`:
1. Зайдите на узел, на котором будет располагаться первый монитор и etcd.
2. Создайте там минимальный `/etc/vitastor/vitastor.conf` с параметрами etcd_address,
osd_network и, если хотите включить привилегии - use_perms (обратите внимание на `https://`
в адресах etcd):
```
{
"etcd_address": ["https://10.0.0.10:2379","https://10.0.0.11:2379","https://10.0.0.12:2379"],
"osd_network": "10.0.0.0/24",
"use_perms": true
}
```
3. Запустите `/usr/lib/vitastor/mon/make-etcd` без параметров или с параметром `--antietcd-only`,
если хотите инициализировать кластер только с Antietcd без etcd.
4. Скрипт сгенерирует все необходимые сертификаты и предложит скопировать их на остальные узлы
мониторов. Для этого нужен root ssh доступ к этим узлам. Если он есть - соглашайтесь, скрипт
автоматически скопирует конфигурацию и инициализирует все etcd и мониторы.
5. Если root ssh доступа с первого узла на остальные узлы мониторов нет - скопируйте на них конфигурацию
вручную (vitastor.conf и все сертификаты и ключи, кроме ключей CA `*_ca.key`) и повторите на каждом
узле вызов `/usr/lib/vitastor/mon/make-etcd`.
После этого можете переходить к инициализации OSD.
Если хотите разобраться в настройке подробнее, читайте далее разделы [Принципы работы](#принципы-работы)
и [Ручная настройка](#ручная-настройка).
## Принципы работы
### Шифрование соединений с etcd (TLS)
Варианты настройки:
- Без шифрования (http)
- С шифрованием (https)
- С шифрованием и аутентификацией по клиентским сертификатам. Используется либо тот
же сертификат, что используется для аутентификации на стороне OSD (`cert`+`pkey` / `osd_cert`+`osd_pkey`),
либо отдельно указанный сертификат (`etcd_client_cert`+`etcd_client_key`)
### Шифрование соединений с OSD (AES-GCM)
Варианты настройки:
- Без шифрования и без контрольных сумм: `proto_checksums=none`.
- Без шифрования, с контрольными суммами данных: `proto_checksums=payload` (можно не указывать,
т.к. это значение по умолчанию). При этом контрольные суммы можно отключить на стороне
клиента и можно использовать более старые версии клиента, не поддерживающие контрольные суммы.
Если нужно запретить подключение клиентов без контрольных сумм, можно использовать опцию
`force_proto_checksums=payload`.
- С шифрованием заголовков и контрольными суммами данных: активируется при установленных опциях
`cert`, `pkey`, `osd_ca` на стороне клиента и `osd_cert`, `osd_pkey`, `osd_ca`, `client_ca`
на стороне OSD, при `proto_checksums=payload`. При этом по умолчанию запрещается
отключение контрольных сумм на уровне клиента, то есть используется `force_proto_checksums=payload`.
- С полным шифрованием всего трафика: аналогично прошлому варианту, но с `proto_checksums=gcm`.
Клиенту при этом по умолчанию разрешается понизить уровень защиты до контрольных сумм, но
это тоже можно запретить через `force_proto_checksums=gcm`. Данный вариант самый медленный и
рекомендуется только для небезопасных (публичных) сетей. В том числе потому, что при использовании
и полного шифрования трафика, и сквозного шифрования образов AES-XTS, данные шифруются дважды.
Для шифрования используется алгоритм AES-256-GCM и собственный упрощённый протокол согласования
ключей, полностью аналогичный TLS 1.3 ECDHE.
### Сквозное шифрование данных образов (AES-XTS)
Клиент Vitastor поддерживает шифрование данных каждого образа своим ключом. В этом случае на OSD
уходят уже зашифрованные данные и сами OSD не видят исходные данные клиента. При этом ключ можно
менять при клонировании/создании снимков образов. Например, можно сделать базовый образ ВМ
(условный Debian Linux) нешифрованным, но наследовать от него шифрованные образы клиентских ВМ.
Ключи шифрования образов могут храниться либо в etcd, либо во внешнем Vault. Во втором случае
в etcd хранятся только ID ключей, а Vitastor вообще не имеет доступа к данным образов. Для
использования Vault нужно создать образ с опцией `--enc_key vault:ID`, в конфигурации указать
опции vault_url и vault_ca, создать всем клиентам учётные записи в Vault и дать им доступ
к требуемым секретам v1.
Ещё раз повторимся, что если AES-XTS используется с полным шифрованием трафика (`proto_checksums=gcm`),
то данные образов шифруются дважды - сначала AES-XTS, а потом AES-GCM. Можете использовать,
только если вы совсем параноик :-).
### Аутентификация по сертификатам
При включённом шифровании клиенты, OSD и мониторы Vitastor аутентифицируются по сертификатам
как при соединениях с etcd (Antietcd), так и с OSD.
Для OSD и мониторов должны использоваться отдельные сертификаты - либо самоподписанные, либо
подписанные отдельными CA (`osd_ca` и `mon_ca`). При этом все OSD могут использовать один и
тот же сертификат и все мониторы тоже могут использовать один и тот же сертификат, так как
привилегии разных OSD или разных мониторов ничем не отличаются (теоретически можно было бы
сделать разграничение сертификатов OSD по пулам, но пока что такой необходимости не было).
Также сертификат монитора может быть вообще не нужен, если Antietcd встраивается в сам монитор.
В этом случае монитор и так имеет доступ ко всем данным etcd прямо в памяти.
### Пользователи и права доступа
При отключённом шифровании трафика Vitastor работает без контроля доступа, то есть,
любой клиент кластера имеет полный доступ как к слою управлению, так и к слою данных. Такой
вариант подходит для выделенных доверенных сетей хранения.
При включённом шифровании трафика OSD (хотя бы заголовков) есть возможность задействовать
контроль доступа, включив опцию `use_perms=true`. При включённой опции каждый пользователь
(и даже OSD и монитор) может выполнять только те операции, которые ему разрешены.
Каждый обычный пользователь, администратор, OSD или монитор должен иметь свой сертификат,
подписанный отдельным корневым сертификатом:
- Для обычных пользователей - `client_ca`. Common Name сертификата при этом должен быть равен
имени пользователя. Обычные пользователи могут только читать и модифицировать явным образом
разрешённые им образы и не могут менять общее состояние кластера.
- Для администраторов - `admin_ca`. Администраторы могут читать и модифицировать все образы,
а также администрировать кластер: смотреть общую статистику и состояние, создавать и удалять
OSD и так далее.
- Для OSD - `osd_ca`, для мониторов - `mon_ca` (если Antietcd запускаются отдельно от мониторов).
OSD и мониторы тоже имеют свои наборы разрешённых операций.
Настройки привилегий для обычных пользователей хранятся в etcd и на данный момент состоят
всего из одного свойства в ключах `/vitastor/config/user/<имя>`:
- `groups` - Список имён групп, членом которых пользователь является.
У образов есть следующие свойства:
- Владелец (`owner`) - имя пользователя, которому разрешено и читать, и менять образ
- Группа владельцев (`owner_group`) - имя группы владельцев
- Группа читателей (`reader_group`) - имя группы пользователей, которым разрешено читать образ
И также есть свойство у пула:
- Группа создателей (`creator_group`) - имя группы пользователей, которым разрешено создавать образы в пуле
Перечень разрешённых операций с данными образов на стороне OSD смотрите в разделе
[Права доступа к данным OSD](#права-доступа-к-данным-osd).
### Привилегии etcd
Привилегии etcd реализуются через Antietcd во всех режимах работы.
Встроенные привилегии etcd не поддерживаются по причине их многочисленных неудобств:
- Аутентификация по сертификатам вообще не работает в REST интерфейсе etcd,
- Привилегии хранятся отдельно от k/v данных и не могут участвовать в транзакциях,
- Менять привилегии может только администратор (root),
- Нет поддержки фильтрации диапазонных ответов чтения по привилегиям.
Если используется etcd, то Antietcd выступает в роли фильтрующего прокси, при этом он
может быть встроен в монитор Vitastor или запущен отдельно. В этом случае etcd должен
разрешать входящие подключения только от Antietcd, а все остальные компоненты должны
соединяться с Antietcd.
Если Antietcd запускается в составе монитора Vitastor, то достаточно включить опцию
`use_perms=true` и задать нужные сертификаты. Если Antietcd запускается отдельно, то
привилегии нужно включать отдельно опциями Antietcd. Подробнее о настройке смотрите
раздел [Варианты настройки etcd/Antietcd](#варианты-настройки-etcdantietcd).
Перечень разрешённых операций с данными etcd смотрите в разделе
[Права доступа к данным etcd](#права-доступа-к-данным-etcd).
## Ручная настройка
### Настройка шифрования соединений OSD
Вам нужно 3 сертификата: один для OSD и по одному для подписи сертификатов клиентов и администраторов.
Для OSD можно использовать самоподписанный сертификат (osd_ca.crt) или отдельный сертификат (osd.crt),
подписанный доверенным сертификатом osd_ca.crt. Для клиентов нужно использовать отдельные
сертификаты, подписанные общим доверенным (client_ca.crt), для администраторов - отдельным доверенным
(admin_ca.crt).
В конфигурацию Vitastor на серверах OSD нужно добавить:
- use_perms: true
- osd_ca: osd_ca.crt
- client_ca: client_ca.crt
- admin_ca: admin_ca.crt
- osd_cert: osd_ca.crt
- osd_pkey: osd_ca.key
На стороне клиентов:
- use_perms: true
- osd_ca: osd_ca.crt
- cert: client.crt
- pkey: client.key
### Варианты настройки etcd/Antietcd
Доступны следующие варианты настройки:
#### Mon со встроенным Antietcd
Самый простой вариант. Кроме сертификатов OSD вам нужен всего 1 сертификат для Antietcd (antietcd.crt).
Настройки Vitastor (`/etc/vitastor/vitastor.conf`):
- etcd_address: [ "http://mon1:2379", ... ] (адреса ваших мониторов с портом 2379)
- use_perms: true
- use_antietcd: true
- antietcd_cert: antietcd.crt
- antietcd_key: antietcd.key
- etcd_ca: antietcd.crt
- osd_ca: osd_ca.crt
- client_ca: client_ca.crt
- admin_ca: admin_ca.crt
#### Mon в роли Etcd proxy
Если вы хотите включить привилегии, но остаться на etcd, можно задействовать режим etcd proxy.
Вам понадобится 2 отдельных сертификата: один для etcd (etcd.crt) и один для antietcd (antietcd.crt).
Клиентский порт etcd должен отличаться от стандартного 2379, например, можно выбрать 2381.
Настройки Vitastor:
- etcd_address: [ "http://mon1:2379", ... ] (адреса ваших мониторов с портом 2379)
- use_perms: true
- use_antietcd: true
- etcd_proxy:
```
{
"urls": [ "http://mon1:2381", ... ], // адреса ваших etcd с портом 2381
"cert": "antietcd.crt",
"key": "antietcd.key",
"ca": "etcd.crt"
}
```
- antietcd_cert: antietcd.crt
- antietcd_key: antietcd.key
- etcd_ca: antietcd.crt
- osd_ca: osd_ca.crt
- client_ca: client_ca.crt
- admin_ca: admin_ca.crt
Опции командной строки etcd:
```
--advertise-client-urls=https://<АДРЕС>:2381 --listen-client-urls=https://<АДРЕС>:2381 \
--client-cert-auth --cert-file=etcd.crt --key-file=etcd.key --trusted-ca-file=antietcd.crt \
--peer-client-cert-auth --peer-cert-file=etcd.crt --peer-key-file=etcd.key --peer-trusted-ca-file=etcd.crt
```
#### Mon с отдельным Antietcd Proxy
Если в дополнение к предыдущему варианту вы хотите разгрузить Antietcd от задач монитора Vitastor,
можно запустить его отдельно.
Аналогично предыдущему варианту нужно 2 сертификата: один для etcd и один для antietcd, плюс понадобится
отдельный сертификат для монитора.
Настройки Vitastor:
- etcd_address: [ "http://mon1:2379", ... ] (адреса ваших мониторов с портом 2379)
- use_perms: true
- use_antietcd: false
- etcd_ca: antietcd.crt
- osd_ca: osd_ca.crt
- client_ca: client_ca.crt
- admin_ca: admin_ca.crt
- mon_etcd_client_cert: mon_ca.crt
- mon_etcd_client_key: mon_ca.key
Опции командной строки Antietcd:
```
--port 2379 --cert /etc/vitastor/antietcd.crt --key /etc/vitastor/antietcd.key \
--client_cert_auth 1 --auth_filter /usr/lib/vitastor/mon/vitastor_auth_filter.js \
--ca /etc/vitastor/client_ca.crt --admin_ca /etc/vitastor/admin_ca.crt \
--osd_ca /etc/vitastor/osd_ca.crt --mon_ca /etc/vitastor/mon_ca.crt \
--etcd_proxy url1,url2,... --etcd_ca /etc/vitastor/etcd.crt \
--etcd_cert /etc/vitastor/antietcd.crt --etcd_key /etc/vitastor/antietcd.key
```
Опции командной строки etcd (не отличаются от предыдущего варианта):
```
--advertise-client-urls=https://<АДРЕС>:2381 --listen-client-urls=https://<АДРЕС>:2381 \
--client-cert-auth --cert-file=etcd.crt --key-file=etcd.key --trusted-ca-file=antietcd.crt \
--peer-client-cert-auth --peer-cert-file=etcd.crt --peer-key-file=etcd.key --peer-trusted-ca-file=etcd.crt
```
#### Отдельный Antietcd без etcd
Аналогично предыдущему варианту, но etcd и его сертификат не нужны:
Настройки Vitastor (не отличаются от предыдущего варианта):
- etcd_address: [ "http://mon1:2379", ... ] (адреса ваших мониторов с портом 2379)
- use_perms: true
- use_antietcd: false
- etcd_ca: antietcd.crt
- osd_ca: osd_ca.crt
- client_ca: client_ca.crt
- admin_ca: admin_ca.crt
- mon_etcd_client_cert: mon_ca.crt
- mon_etcd_client_key: mon_ca.key
Опции командной строки Antietcd:
```
--port 2379 --cert /etc/vitastor/antietcd.crt --key /etc/vitastor/antietcd.key \
--client_cert_auth 1 --auth_filter /usr/lib/vitastor/mon/vitastor_auth_filter.js \
--ca /etc/vitastor/client_ca.crt --admin_ca /etc/vitastor/admin_ca.crt \
--osd_ca /etc/vitastor/osd_ca.crt --mon_ca /etc/vitastor/mon_ca.crt \
--persist_filter /usr/lib/vitastor/mon/vitastor_persist_filter.js
```
### Настройка Vault/OpenBao
Для использования Vault каждому клиенту, который будет получать из Vault ключи
образов, нужна учётная запись в Vault. Vitastor поддерживает только аутентификацию
по клиентским сертификатам, так что все сертификаты клиентов (`cert`+`pkey`) должны
быть зарегистрированы в Vault и им должен быть дан доступ к соответствующим секретам
(поддерживается API секретов v1).
Требуемый формат секрета Vault - одно поле `key` в формате шестнадцатеричной строки.
Используется алгоритм AES-256-XTS, так что длина ключа - 64 байта, то есть строка
должна состоять из 128 шестнадцатеричных цифр.
Для подключения Vault включите следующие настройки в Vitastor.conf:
- `vault_url` - адрес Vault (например, `https://vault:8200`)
- `vault_ca` - сертификат самого Vault
После этого, если создать образ (`vitastor-cli create`) с опцией `--enc_key vault:<ID>`,
то для получения ключа клиенты Vitastor сначала обратятся к Vault для получения токена
по адресу `/v1/auth/cert/login`, а потом запросят из Vault сам секрет по адресу `/v1/secret/<ID>`.
#### Пример настройки Vault
Пошаговая инструкция для настройки тестового Vault на примере OpenBao:
1. Если ещё не настроен TLS, генерируем самоподписанный TLS сертификат для Vault:
```
openssl req -days 3650 -x509 -addext basicConstraints=critical,CA:TRUE,pathlen:1 --addext subjectAltName=DNS:vault \
-new -newkey rsa:4096 -nodes -keyout /etc/openbao/vault.key -out /etc/openbao/vault.crt
```
Настраиваем его в `/etc/openbao/openbao.hcl`:
```
listener "tcp" {
address = "0.0.0.0:8200"
tls_cert_file = "/etc/openbao/vault.crt"
tls_key_file = "/etc/openbao/vault.key"
}
```
И перезапускаем OpenBao (`systemctl restart openbao`).
2. Копируем TLS сертификат Vault для Vitastor:
```
cp /etc/openbao/vault.crt /etc/vitastor/vault.crt
```
Переносим его на все клиентские ноды и прописываем в `/etc/vitastor/vitastor.conf`:
```
{
...
"vault_url": "http://vault:8200",
"vault_ca": "/etc/vitastor/vault.crt"
}
```
3. Проверяем статус Vault:
```
bao status -ca-cert /etc/openbao/vault.crt -address=https://vault:8200
```
4. Инициализируем Vault в тестовом режиме из 1 ноды (с 1 частью ключа):
```
bao operator init -n 1 -t 1 -ca-cert /etc/openbao/vault.crt -address=https://vault:8200
```
5. Разблокируем Vault:
```
bao operator unseal -ca-cert /etc/openbao/vault.crt -address=https://vault:8200
```
6. Включаем аутентификацию по сертификатам:
```
bao auth enable -ca-cert /etc/openbao/vault.crt -address=https://vault:8200 cert
```
7. Включаем секреты v1:
```
bao secrets enable -ca-cert /etc/openbao/vault.crt -address=https://vault:8200 -path=secret kv-v1
```
8. Создаём тестовый секрет:
```
bao kv put -ca-cert /etc/openbao/vault.crt -address=https://vault:8200 secret/vitastor/testimg3 key=$(openssl rand -hex 64)
```
9. Генерируем подписанный сертификат для пользователя Vitastor (там, где у вас есть `client_ca.crt` и `client_ca.key`):
```
openssl req -subj '/CN=testimg3' -nodes -new -keyout testimg3.key -out testimg3.csr
openssl x509 -req -days 3650 -CA client_ca.crt -CAkey client_ca.key -CAcreateserial -in testimg3.csr -out testimg3.crt
rm testimg3.csr
```
10. Создаём пользователя в Vault и даём ему доступ к секрету:
```
cat >testimg3.policy <<EOF
path "/secret/vitastor/testimg3" {
capabilities = ["read"]
}
EOF
bao policy write -ca-cert /etc/openbao/vault.crt -address=https://vault:8200 testimg3 testimg3.policy
bao write -ca-cert /etc/openbao/vault.crt -address=https://vault:8200 auth/cert/certs/testimg3 \
certificate=@testimg3.crt display_name=testimg3 token_ttl=24h token_policies=testimg3
```
11. Тестируем доступ к секрету:
```
curl --cacert /etc/vitastor/vault.crt --cert testimg3.crt --key testimg3.key \
--json '{}' https://vault:8200/v1/auth/cert/login
```
Будет выведен токен, подставляем его в следующий запрос:
```
curl --cacert /etc/vitastor/vault.crt --cert testimg3.crt --key testimg3.key \
-H 'X-Vault-Token: <ПОЛУЧЕННЫЙ ТОКЕН>' https://vault:8200/v1/secret/vitastor/testimg3
```
12. Создаём образ в Vitastor с заданным секретом (от имени администратора или того, кто имеет
право создавать образы в вашем пуле):
```
vitastor-cli create -s 100G --enc_key vault:vitastor/testimg3 --owner testimg3 testimg3
```
13. Тестируем доступ к образу от имени пользователя testimg3:
```
vitastor-cli --cert testimg3.crt --pkey testimg3.key dd if=/dev/urandom oimg=testimg3 bs=1M count=100
```
## Списки разрешённых операций
### Права доступа к данным etcd
Ниже все названия ключей приведены без общего префикса `/vitastor`.
Разрешённые операции с ключами в Antietcd для обычных пользователей:
- Только чтение:
- Разрешено всегда:
- `/config/global`
- `/config/node_placement`
- `/config/pools`
- `/pg/config`
- `/osd/state/*`
- `/pg/state/*`
- `/index/maxid/*`
- Для образов, которые [может читать пользователь](#пользователи-и-права-доступа):
- `/config/inode/*`
- `/index/image/*`
- `/inode/stats/*`
- Чтение и запись:
- Для пулов, в которых может создавать образы пользователь:
- `/index/maxid/*`
- Для образов, которыми владеет пользователь:
- `/config/inode/*`
- `/index/image/*`
Разрешённые операции с ключами в Antietcd для администраторов:
- Чтение:
- `/stats`
- `/mon/*`
- `/pg/*`
- `/pgstats/*`
- `/inode/stats/*`
- `/pool/stats/*`
- Чтение и запись:
- `/config/*`
- `/osd/*`
- `/index/*`
- `/pg/config`
- `/pg/history/*`
- `/stats`
- `/history/last_clean_pgs`
Разрешённые операции с ключами в etcd для OSD:
- Чтение:
- `/pg/config`
- `/config/*`
- Чтение и запись:
- `/osd/*`
- `/pg/state/*`
- `/pg/history/*`
- `/pgstats/*`
Разрешённые операции с ключами в etcd для мониторов:
- Чтение:
- `/config/*`
- `/osd/*`
- `/pgstats/*`
- Чтение и запись:
- `/pg/config`
- `/stats`
- `/history/last_clean_pgs`
- `/mon/*`
- `/pg/history/*`
- `/pg/stats/*`
- `/pgstats/*`
- `/inode/stats/*`
- `/pool/stats/*`
### Права доступа к данным OSD
При включённой опции `use_perms` и шифровании OSD аутентифицирует клиентов по сертификатам
и разрешает каждому клиенту только то, что ему разрешено согласно модели прав доступа.
Клиентские операции:
- READ - разрешено для образов, доступных пользователю на чтение.
- WRITE, DELETE, SCRUB - разрешены для образов, доступных пользователю на запись.
- SYNC - операция не связана с образом и разрешена всегда.
- DESCRIBE - операция разрешена только для администраторов (используются командами
`vitastor-cli describe` и `fix`).
- PING - операция разрешена всегда.
- SHOW_CONFIG - операция разрешена всегда, однако если в ней клиент представляется
как OSD, то проверяется, что он использует сертификат, подписанный `osd_ca`.
- SEC_LIST (листинг) - разрешена другим OSD и администраторам с любыми параметрами,
а обычным клиентам разрешена только для запросов, ограниченных образом, доступным
пользователю на чтение.
Кластерные операции - разрешаются только другим OSD:
- SEC_READ
- SEC_WRITE
- SEC_WRITE_STABLE
- SEC_SYNC
- SEC_STABILIZE
- SEC_ROLLBACK
- SEC_DELETE
- SEC_READ_BMP
- SEC_LOCK
### Права доступа к API
[vitastor-cli serve](../usage/cli.ru.md#serve) также поддерживает клиентскую
аутентификацию по сертификатам. Принимаются только сертификаты, подписанные
`client_ca`. В качестве серверного сертификата используется отдельный сертификат
`api_cert` с ключом `api_pkey`.
При этом для корректной работы `vitastor-cli serve` он сам должен использовать
для доступа в Vitastor сертификат администратора (`cert`+`pkey`, подписанный `admin_ca`),
чтобы корректно обрабатывать любые запросы.
Обычным пользователям (с сертификатами, подписанными `client_ca`) при доступе к API
разрешаются только API-операции с образами, доступными им либо на чтение (для чтения),
либо на запись (для модификации):
- image/list - для образов, которые пользователь может читать.
- image/create - для пулов, в которых пользователю разрешено создавать образы, либо
для создания снимков образов, которыми пользователь владеет.
- image/delete, image/flatten, image/modify - для образов, которыми пользователь владеет.
Все остальные операции разрешаются только администраторам.
## Производительность шифрования
У вас может возникнуть вопрос - а как быстро всё это прекрасное шифрование работает?
Ответ - сильно зависит от процессора. На современных процессорах (при наличии AVX512 с VAES)
очень быстро - скорость шифрования AES может составлять 10-20 Гбайт/с и выше. В первую очередь
подразумевается CPU клиентских машин, потому что сквозное шифрование выполняется целиком на
клиенте, а транспортное хоть также и затрагивает OSD, но у клиента поток один, а OSD на стороне
сервера много и добавить там ресурсов легче.
На более старых процессорах скорость заметно хуже, например, на Xeon E5 v4 она составляет
буквально 3 Гбайт/с.
Вы можете оценить производительность своих процессоров с помощью команды `vitastor-cli cpubench`.
Пример вывода (💪 AMD EPYC 9575F):
```
$ vitastor-cli cpubench
Vitastor transport encryption benchmark (AES-256-GCM, AES-256-XTS and xxhash3)
Warmup...
No transport encryption, data checksums enabled, e2e unencrypted image
xxhash3 1 M block... 209000 iterations in 2001 ms = 104447.78 MB/s
xxhash3 4 K block... 37000000 iterations in 2022 ms = 71479.35 MB/s
Header encryption with payload checksums, e2e unencrypted image
AES-256-GCM encrypt header + xxhash3 1 M block... 210000 iterations in 2015 ms = 104218.36 MB/s
AES-256-GCM encrypt header + xxhash3 4 K block... 26000000 iterations in 2073 ms = 48993.01 MB/s
Full transport encryption, e2e unencrypted image
AES-256-GCM encrypt header and 1 M block... 54000 iterations in 2000 ms = 27000.00 MB/s
AES-256-GCM encrypt header and 4 K block... 11700000 iterations in 2014 ms = 22692.71 MB/s
No transport encryption, no checksums, e2e encrypted image
AES-256-XTS encrypt 1 M block... 50000 iterations in 2039 ms = 24521.82 MB/s
AES-256-XTS encrypt 4 K block... 12600000 iterations in 2009 ms = 24499.13 MB/s
No transport encryption, e2e encrypted image, data checksums enabled
AES-256-XTS encrypt + xxhash3 1 M block... 40000 iterations in 2013 ms = 19870.84 MB/s
AES-256-XTS encrypt + xxhash3 4 K block... 10200000 iterations in 2011 ms = 19812.90 MB/s
Header encryption with payload checksums, e2e encrypted image
AES-256-GCM encrypt header + AES-256-XTS encrypt + xxhash3 1 M block... 40000 iterations in 2014 ms = 19860.97 MB/s
AES-256-GCM encrypt header + AES-256-XTS encrypt + xxhash3 4 K block... 8700000 iterations in 2011 ms = 16899.24 MB/s
Full transport encryption, e2e encrypted image
AES-256-XTS + AES-256-GCM encrypt 1 M block... 26000 iterations in 2062 ms = 12609.12 MB/s
AES-256-XTS + AES-256-GCM encrypt 4 K block... 6300000 iterations in 2006 ms = 12267.88 MB/s
```
А вот Xeon E5-2680v4:
```
$ vitastor-cli cpubench
Vitastor transport encryption benchmark (AES-256-GCM, AES-256-XTS and xxhash3)
Warmup...
No transport encryption, data checksums enabled, e2e unencrypted image
xxhash3 1 M block... 62000 iterations in 2021 ms = 30677.88 MB/s
xxhash3 4 K block... 12400000 iterations in 2006 ms = 24146.31 MB/s
Header encryption with payload checksums, e2e unencrypted image
AES-256-GCM encrypt header + xxhash3 1 M block... 62000 iterations in 2027 ms = 30587.07 MB/s
AES-256-GCM encrypt header + xxhash3 4 K block... 6800000 iterations in 2011 ms = 13208.60 MB/s
Full transport encryption, e2e unencrypted image
AES-256-GCM encrypt header and 1 M block... 7000 iterations in 2317 ms = 3021.15 MB/s
AES-256-GCM encrypt header and 4 K block... 1500000 iterations in 2102 ms = 2787.52 MB/s
No transport encryption, no checksums, e2e encrypted image
AES-256-XTS encrypt 1 M block... 7000 iterations in 2317 ms = 3021.15 MB/s
AES-256-XTS encrypt 4 K block... 1600000 iterations in 2088 ms = 2993.30 MB/s
No transport encryption, e2e encrypted image, data checksums enabled
AES-256-XTS encrypt + xxhash3 1 M block... 6000 iterations in 2188 ms = 2742.23 MB/s
AES-256-XTS encrypt + xxhash3 4 K block... 1400000 iterations in 2053 ms = 2663.78 MB/s
Header encryption with payload checksums, e2e encrypted image
AES-256-GCM encrypt header + AES-256-XTS encrypt + xxhash3 1 M block... 6000 iterations in 2190 ms = 2739.73 MB/s
AES-256-GCM encrypt header + AES-256-XTS encrypt + xxhash3 4 K block... 1300000 iterations in 2101 ms = 2417.00 MB/s
Full transport encryption, e2e encrypted image
AES-256-XTS + AES-256-GCM encrypt 1 M block... 4000 iterations in 2666 ms = 1500.38 MB/s
AES-256-XTS + AES-256-GCM encrypt 4 K block... 800000 iterations in 2113 ms = 1478.94 MB/s
```

2
json11

Submodule json11 updated: edcd85b8bd...fd37016cf8

View File

@@ -3,7 +3,6 @@
const AntiEtcd = require('antietcd');
const vitastor_auth_filter = require('./vitastor_auth_filter.js');
const vitastor_persist_filter = require('./vitastor_persist_filter.js');
const { b64, local_ips } = require('./utils.js');
@@ -28,17 +27,7 @@ class AntiEtcdAdapter
is_local['::'] = true;
is_local[''] = true;
// split :, 3 -> <schema>:<//ip>:<port>
const selected = [];
for (let i = 0; i < cluster.length; i++)
{
const m = /^(https?:\/\/)?(?:\[(.*)\]|([^\[\:]+))(?::(\d+))?$/.exec(cluster[i]);
if (!m)
continue;
const ip = m[3] || m[2];
const port = m[4] || 2379;
if (is_local[ip] && (!cfg_port || port == cfg_port))
selected.push({ idx: i, ip, port });
}
const selected = cluster.map(s => s.split(':', 3)).filter(ip => is_local[ip[1].substr(2)] && (!cfg_port || ip[2] == cfg_port));
if (selected.length > 1)
{
console.error('More than 1 etcd_address matches local IPs, please specify port');
@@ -47,46 +36,19 @@ class AntiEtcdAdapter
else if (selected.length == 1)
{
const antietcd_config = {
ip: selected[0].ip,
port: selected[0].port,
ip: selected[0][1].substr(2),
port: selected[0][2],
cert: config.antietcd_cert,
key: config.antietcd_key,
ca: config.client_ca,
data: config.antietcd_data_file || ((config.antietcd_data_dir || '/var/lib/vitastor') + '/mon_'+selected[0].port+'.json.gz'),
ca: config.etcd_ca,
data: config.antietcd_data_file || ((config.antietcd_data_dir || '/var/lib/vitastor') + '/mon_'+selected[0][2]+'.json.gz'),
persist_filter: vitastor_persist_filter({ vitastor_prefix: config.etcd_prefix || '/vitastor' }),
node_id: cluster[selected[0].idx].replace(/^(https?:\/\/)/, ''), // same as in <cluster> below
node_id: selected[0][1].substr(2)+':'+selected[0][2], // node_id = ip:port
cluster: (cluster.length == 1 ? null : cluster.reduce((a, c) => { a[c.replace(/^(https?:\/\/)/, '')] = c; return a; }, {})),
cluster_key: (config.etcd_prefix || '/vitastor'),
stale_read: 1,
log_level: 1,
logs: { cluster: true },
};
if (config.etcd_proxy)
{
// Monitor may use the builtin etcd_proxy mode
if (!config.etcd_proxy.urls)
{
console.error('etcd_proxy.urls are empty');
process.exit(1);
}
antietcd_config.etcd_proxy = config.etcd_proxy.urls;
antietcd_config.etcd_cert = config.etcd_proxy.cert;
antietcd_config.etcd_key = config.etcd_proxy.key;
antietcd_config.etcd_ca = config.etcd_proxy.ca;
delete antietcd_config.data;
delete antietcd_config.persist_filter;
delete antietcd_config.cluster;
delete antietcd_config.cluster_key;
}
if (config.use_perms)
{
antietcd_config.client_cert_auth = true;
antietcd_config.auth_filter = vitastor_auth_filter;
antietcd_config.ca = config.client_ca;
antietcd_config.osd_ca = config.osd_ca;
antietcd_config.mon_ca = config.mon_ca;
antietcd_config.admin_ca = config.admin_ca;
}
for (const key in config)
{
if (key.substr(0, 9) === 'antietcd_')
@@ -211,7 +173,7 @@ class AntiEtcdAdapter
await new Promise(ok => setTimeout(ok, timeout-(Date.now()-prev)));
}
prev = Date.now();
const res = await this.antietcd.api(path.replace(/^\/+/, '').replace(/\/+$/, '').replace(/\/+/g, '_'), body, { user_type: 'mon' });
const res = await this.antietcd.api(path.replace(/^\/+/, '').replace(/\/+$/, '').replace(/\/+/g, '_'), body);
if (res.error)
{
console.error('Failed to query antietcd '+path+' (retry '+retry+'/'+retries+'): '+res.error);

View File

@@ -16,7 +16,6 @@ const etcd_allow = new RegExp('^'+[
'config/pools',
'config/osd/[1-9]\\d*',
'config/pgs', // old name
'config/user/.*',
'pg/config',
'config/inode/[1-9]\\d*/[1-9]\\d*',
'osd/state/[1-9]\\d*',
@@ -65,8 +64,6 @@ const etcd_tree = {
placement_levels: { datacenter: 1, rack: 2, host: 3, osd: 4, ... },
use_old_pg_combinator: false,
osd_backfillfull_ratio: 0.99,
mon_metrics_path: '/prometheus',
mon_healthcheck_path: '/healthcheck',
// client and osd
tcp_header_buffer_size: 65536,
use_sync_send_recv: false,
@@ -211,8 +208,6 @@ const etcd_tree = {
primary_affinity_tags?: 'nvme' | [ 'nvme', ... ],
// scrub interval
scrub_interval?: '30d',
// users allowed to create images in this pool
creator_group?: '',
},
...
}, */
@@ -230,20 +225,11 @@ const etcd_tree = {
readonly?: boolean,
deleted?: boolean,
enc_key?: string,
owner?: string,
owner_group?: string,
reader_group?: string,
meta?: any,
}
}
}, */
inode: {},
/* user: {
<username>: {
type: 'osd'|'mon'|'admin'|'client',
groups: string[],
},
}, */
user: {},
},
osd: {
state: {
@@ -406,6 +392,7 @@ const etcd_tree = {
/* <name>: {
id: uint64_t,
pool_id: uint64_t,
// ...plus a copy of everything from config/inode/x/y
}, */
},
maxid: {

View File

@@ -112,10 +112,9 @@ function make_cyclic(pgs, parity_space)
{
if (parity_space > 1)
{
for (const id in pgs)
for (const pg in pgs)
{
const pg = pgs[id];
for (let i = 1; i < pg.length; i++)
for (let i = 1; i < pg.size; i++)
{
const cyclic = [ ...pg.slice(i), ...pg.slice(0, i) ];
pgs['pg_'+cyclic.join('_')] = cyclic;

View File

@@ -87,7 +87,7 @@ class Mon
this.http = await create_http_server(this.config, (req, res) =>
{
const u = new URL(req.url, 'http://'+(req.headers.host || 'localhost'));
if (u.pathname.replace(/\/+$/, '') == (this.config.mon_metrics_path||'/metrics'))
if (u.pathname.replace(/\/+$/, '') == (this.config.prometheus_path||'/metrics'))
{
if (!this.watcher_active)
{
@@ -100,19 +100,6 @@ class Mon
res.write(export_prometheus_metrics(this.state));
}
}
else if (u.pathname.replace(/\/+$/, '') == (this.config.mon_healthcheck_path||'/healthcheck'))
{
if (!this.watcher_active)
{
res.writeHead(503);
res.write('Monitor is in standby mode. Please retrieve metrics from master monitor instance\n');
}
else
{
res.setHeader('Content-Type', 'text/plain; version=0.0.4; charset=utf-8');
res.write('Monitor is active');
}
}
else
{
res.writeHead(404);
@@ -640,7 +627,7 @@ class Mon
if (this.state.pg.history[pool_id] &&
this.state.pg.history[pool_id][pg])
{
pg_history[pg-1] = JSON.parse(JSON.stringify(this.state.pg.history[pool_id][pg]));
pg_history[pg-1] = this.state.pg.history[pool_id][pg];
}
}
const real_prev_pgs = [];
@@ -732,7 +719,7 @@ class Mon
this.next_recheck_timer = null;
this.next_recheck_at = 0;
this.schedule_recheck();
}, (this.next_recheck_at-now)*1000);
}, now-this.next_recheck_at);
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "vitastor-mon",
"version": "3.1.0",
"version": "3.0.5",
"description": "Vitastor SDS monitor service",
"main": "mon-main.js",
"scripts": {
@@ -9,7 +9,7 @@
"author": "Vitaliy Filippov",
"license": "UNLICENSED",
"dependencies": {
"antietcd": "^1.3.3",
"antietcd": "^1.2.4",
"sprintf-js": "^1.1.2",
"ws": "^7.2.5"
},

View File

@@ -84,7 +84,7 @@ function scale_pg_history(prev_pg_history, prev_pgs, new_pgs)
finish_pg_history(merged_history[1]);
for (let i = 0; i < new_pg_count; i++)
{
new_pg_history[i] = JSON.parse(JSON.stringify(merged_history[1]));
new_pg_history[i] = { ...merged_history[1] };
}
}
// Mark history keys for removed PGs as removed
@@ -102,7 +102,7 @@ function scale_pg_count(prev_pgs, new_pg_count)
{
for (let i = prev_pgs.length; i < new_pg_count; i++)
{
prev_pgs[i] = [ ...prev_pgs[i % prev_pgs.length] ];
prev_pgs[i] = prev_pgs[i % prev_pgs.length];
}
}
else if (prev_pgs.length > new_pg_count)

View File

@@ -3,9 +3,6 @@
const { parse_level_indexes, parse_pg_dsl } = require('./lp_optimizer/dsl_pgs.js');
const MIN_DATA_BLOCK_SIZE = 4096;
const MAX_DATA_BLOCK_SIZE = 128*1024*1024;
function validate_pool_cfg(pool_id, pool_cfg, placement_levels, warn)
{
pool_cfg.pg_size = Math.floor(pool_cfg.pg_size);
@@ -26,8 +23,7 @@ function validate_pool_cfg(pool_id, pool_cfg, placement_levels, warn)
console.log('Pool '+pool_id+' has invalid coding scheme (one of "xor", "replicated", "ec" and "jerasure" required)');
return false;
}
// OSDs don't support pg_size > 64 for now
if (!pool_cfg.pg_size || pool_cfg.pg_size < 1 || pool_cfg.pg_size > 64 ||
if (!pool_cfg.pg_size || pool_cfg.pg_size < 1 || pool_cfg.pg_size > 256 ||
pool_cfg.scheme !== 'replicated' && pool_cfg.pg_size < 3)
{
if (warn)
@@ -54,18 +50,6 @@ function validate_pool_cfg(pool_id, pool_cfg, placement_levels, warn)
console.log('Pool '+pool_id+' has invalid parity_chunks (must be between 1 and pg_size-2)');
return false;
}
if (pool_cfg.block_size &&
(!Number(pool_cfg.block_size) || (pool_cfg.block_size & (pool_cfg.block_size-1)) ||
pool_cfg.block_size < MIN_DATA_BLOCK_SIZE || pool_cfg.block_size > MAX_DATA_BLOCK_SIZE ||
pool_cfg.scheme !== 'replicated' && pool_cfg.block_size*pool_cfg.pg_size > 0xFFFFFFFF))
{
if (warn)
{
console.log('Pool '+pool_id+' has invalid block_size (must be a power of two between '+
MIN_DATA_BLOCK_SIZE+' and '+MAX_DATA_BLOCK_SIZE+'; must not exceed 2^32-1 when multiplied by pg_size)');
}
return false;
}
if (!pool_cfg.pg_count || pool_cfg.pg_count < 1)
{
if (warn)

View File

@@ -1,104 +1,50 @@
#!/usr/bin/node
// Simple Vitastor etcd / antietcd / TLS configurator
// Simple systemd unit generator for etcd
// Copyright (c) Vitaliy Filippov, 2019+
// License: MIT
// USAGE:
// 1) Put the same etcd_address into /etc/vitastor/vitastor.conf on all monitor nodes
// 2) Run ./make-etcd.js. It will create the etcd service on one of specified IPs
const child_process = require('child_process');
const fs = require('fs');
const os = require('os');
const path = require('path');
const readline = require('readline');
const help_text = `Initialize a Vitastor cluster (etcd, vitastor.conf and TLS certificates)
(c) Vitaliy Filippov, 2026+ (MIT)
USAGE:
1) Create a minimal vitastor.conf with etcd_address, osd_network and (optionally) use_perms.
Non-encrypted: {"etcd_address":["http://10.0.0.10:2379","http://10.0.0.11:2379","http://10.0.0.12:2379"],"osd_network":"10.0.0.0/24"}
Encrypted: {"etcd_address":["https://10.0.0.10:2379","https://10.0.0.11:2379","https://10.0.0.12:2379"],"use_perms":true,"osd_network":"10.0.0.0/24"}
(Note https:// etcd URLs!)
2) Run: ${process.argv[1]} [./vitastor.conf] [--antietcd-only]
You can run it on etcd/monitor nodes or on an external node.
It configures etcd, generates TLS certificates (on the first or external node), copies
them to other etcd/monitor nodes using root ssh, and updates vitastor.conf with TLS options.
You can also copy configuration and certificates manually.
3) If you have OSD-only nodes, run:
${process.argv[1]} --copy-to-osd-node NODE_NAME ./vitastor.conf
It copies vitastor.conf and required TLS certificates to that node.
OPTIONS:
--antietcd-only
disable etcd (proxy or direct mode), use only antietcd
--gen-certs
force certificate generation even if it's not the first node
--no-certs
disable certificate generation
--copy yes|no|ask
copy vitastor.conf and TLS certificates for monitor&etcd to monitor nodes using scp
(default is ask)
--copy-to-osd-node NODE[,NODE2,...]
copy vitastor.conf and TLS certificates for OSDs to NODES using scp
`;
run().catch(e => { console.error(e); process.exit(1); });
async function run()
{
let config_path = '/etc/vitastor/vitastor.conf';
let config_dir = '/etc/vitastor';
let gen_certs = 'auto';
let antietcd_only = false;
let copy = 'ask';
let copy_to_osd = null;
for (let i = 2; i < process.argv.length; i++)
const config_path = process.argv[2] || '/etc/vitastor/vitastor.conf';
if (config_path == '-h' || config_path == '--help')
{
const arg = process.argv[i];
if (arg == '-h' || arg == '--help')
{
console.log(help_text);
process.exit(0);
}
else if (arg == '--gen-certs')
{
gen_certs = true;
}
else if (arg == '--no-certs')
{
gen_certs = false;
}
else if (arg == '--antietcd-only')
{
antietcd_only = true;
}
else if (arg == '--copy-to-osd-node' && i < process.argv.length-1)
{
i++;
copy_to_osd = process.argv[i].split(/,/);
}
else if (arg == '--copy' && i < process.argv.length-1)
{
i++;
copy = process.argv[i];
if (copy !== 'ask' && copy !== 'yes' && copy !== 'no')
{
console.error('--copy should be "ask", "yes" or "no"');
process.exit(1);
}
}
else if (arg[0] == '-')
{
console.error('Unknown option: '+arg[0]);
process.exit(1);
}
else
{
config_path = arg;
config_dir = path.dirname(arg);
}
console.log(
'Initialize systemd etcd service for Vitastor\n'+
'(c) Vitaliy Filippov, 2019+ (MIT)\n'+
'\n'+
'USAGE:\n'+
'1) Put the same etcd_address into /etc/vitastor/vitastor.conf on all monitor nodes\n'+
'2) Run '+process.argv[1]+' [config_path]\n'
);
process.exit(0);
}
if (!fs.existsSync(config_path))
{
console.log(config_path+' is missing');
process.exit(1);
}
const in_docker = fs.existsSync("/etc/vitastor/etcd.conf") &&
fs.existsSync("/etc/vitastor/docker.conf");
if (!in_docker && fs.existsSync("/etc/systemd/system/vitastor-etcd.service"))
{
console.log("/etc/systemd/system/vitastor-etcd.service already exists");
process.exit(1);
}
if (!in_docker && fs.existsSync("/etc/systemd/system/etcd.service"))
{
console.log("/etc/systemd/system/etcd.service already exists");
process.exit(1);
}
const config = JSON.parse(fs.readFileSync(config_path, { encoding: 'utf-8' }));
if (!config.etcd_address)
{
@@ -106,307 +52,30 @@ async function run()
process.exit(1);
}
const etcds = (config.etcd_address instanceof Array ? config.etcd_address : (''+config.etcd_address).split(/,/))
.map(s => /^(https?):\/\/(\[[^\]]+\]|[^\[\]\:\/]+)(?::(\d+))?/.exec(s.toLowerCase()))
.filter(s => s)
.map(s => ({
scheme: s[1],
addr: s[2].indexOf(':') >= 0 && s[2][0] != '[' ? '['+s[2]+']' : s[2],
ip: s[2][0] == '[' ? s[2].substr(1, s[2].length-2) : s[2],
port: s[3],
}));
const tls = etcds.filter(e => e.scheme === 'https').length > 0;
const use_perms = tls && config.use_perms;
.map(s => (''+s).replace(/^https?:\/\/|(:\d+)?(\/.*)?$/g, '').replace(/^\[(.*)\]$/, '$1').toLowerCase());
const num = select_local_etcd(etcds);
if (copy_to_osd)
{
copy_to_osd_nodes(copy_to_osd, config_dir, use_perms, antietcd_only);
process.exit(0);
}
if (tls)
{
const etcd_ca = config_dir+'/'+path.basename(config.etcd_ca || '/etc/vitastor/etcd_ca.crt');
if (gen_certs === true)
{
console.log('Certificate generation is requested explicitly, generating');
}
else if (gen_certs === false)
{
console.log('Certificate generation is disabled explicitly, skipping');
}
else if (num < 0)
{
gen_certs = true;
console.log('No matching IPs in etcd_address from '+config_path+', only generating certificates');
}
else if (config.etcd_ca && fs.existsSync(etcd_ca))
{
gen_certs = false;
console.log(etcd_ca+' already exists, assuming certificates are already generated');
}
else if (num === 0)
{
gen_certs = true;
console.log('This is monitor node 1, generating certificates');
}
else
{
console.log('This is monitor node '+(num+1)+', '+etcd_ca+' does not exist, please copy certificates to this node');
process.exit(1);
}
await write_auth_config(config, config_path, etcds, use_perms, antietcd_only);
if (gen_certs)
{
if (copy === 'ask')
copy = await ask_copy('Copy certificates and vitastor.conf to other nodes and start them (requires root ssh)? ');
copy = (copy === 'y' || copy === 'yes');
await make_certs(config_dir, copy, etcds, num, use_perms, antietcd_only);
}
}
else if (num <= 0)
{
if (copy === 'ask')
copy = await ask_copy('Copy vitastor.conf to other nodes and start them (requires root ssh)? ');
if (copy === 'y' || copy === 'yes')
await copy_nontls(config_dir, etcds, num);
}
if (num < 0)
{
console.log('No matching IPs in etcd_address from '+config_path);
process.exit(tls && gen_certs ? 0 : 1);
process.exit(0);
}
await configure_etcd(etcds, num, tls, use_perms);
process.exit(0);
}
async function ask_copy(question)
{
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: '> ',
});
let copy;
while (copy != 'y' && copy != 'n' && copy != 'yes' && copy != 'no')
{
if (copy)
console.log('Please type "yes" or "no"');
copy = await new Promise(ok => rl.question(question, ok));
}
return copy;
}
async function copy_to_osd_nodes(to, dir, use_perms, antietcd_only)
{
const osd_to_copy = [ 'vitastor.conf' ];
if (!antietcd_only && !use_perms)
osd_to_copy.push('etcd_ca.crt');
else
osd_to_copy.push('antietcd_ca.crt');
if (use_perms)
osd_to_copy.push('osd.crt', 'osd.key', 'client_ca.crt');
console.warn('Copying configuration to OSD nodes '+to.join(', '));
for (const node of to)
await system("scp "+dir+"/"+osd_to_copy.join(" "+dir+"/")+" root@"+(node.indexOf(':') >= 0 ? "["+node+"]" : node)+":/etc/vitastor/");
}
async function make_certs(dir, copy, etcds, num, use_perms, antietcd_only)
{
console.log(`-----
Generating certificates in ${dir}
-----
`);
const to_copy = [ 'vitastor.conf' ];
const osd_to_copy = [ 'vitastor.conf' ];
if (!antietcd_only)
{
await make_ca("/O=Vitastor etcd CA", dir+"/etcd_ca");
await make_signed("/CN=Vitastor etcd", dir+"/etcd", dir+"/etcd_ca", etcds.map(e => "IP:"+e.ip).join(','));
to_copy.push('etcd_ca.crt', 'etcd.crt', 'etcd.key');
if (!use_perms)
osd_to_copy.push('etcd_ca.crt');
}
if (use_perms || antietcd_only)
{
await make_ca("/O=Vitastor Antietcd CA", dir+"/antietcd_ca");
await make_signed("/CN=Vitastor Antietcd", dir+"/antietcd", dir+"/antietcd_ca", etcds.map(e => "IP:"+e.ip).join(','));
to_copy.push('antietcd_ca.crt', 'antietcd.crt', 'antietcd.key');
osd_to_copy.push('antietcd_ca.crt');
}
if (use_perms)
{
await make_ca("/CN=Vitastor OSD", dir+"/osd");
await make_ca("/O=Vitastor Client CA", dir+"/client_ca");
await make_ca("/O=Vitastor Admin CA", dir+"/admin_ca");
await make_signed("/CN=admin", dir+"/admin", dir+"/admin_ca");
to_copy.push('osd.crt', 'osd.key', 'client_ca.crt', 'admin_ca.crt', 'admin.crt', 'admin.key');
osd_to_copy.push('osd.crt', 'osd.key', 'client_ca.crt', 'admin_ca.crt', 'admin.crt', 'admin.key');
}
console.log(`-----
Certificates generated, commands to copy them:
- Monitor+OSD node:
cd ${dir} && scp ${to_copy.join(' ')} root@NODE:/etc/vitastor/
- Monitor node:
cd ${dir} && scp ${to_copy.filter(f => f != 'osd.key').join(' ')} root@NODE:/etc/vitastor/
- OSD node:
cd ${dir} && scp ${osd_to_copy.join(' ')} root@NODE:/etc/vitastor/
Then don't forget to run:
chown vitastor:vitastor /etc/vitastor/*.key && /usr/lib/vitastor/mon/make-etcd
-----
`);
if (copy)
{
for (let i = 0; i < etcds.length; i++)
{
if (i == num)
continue;
const node = etcds[i];
await system("scp "+dir+"/"+to_copy.join(" "+dir+"/")+" root@"+(node.ip.indexOf(':') >= 0 ? "["+node.ip+"]" : node.ip)+
":/etc/vitastor/ && ssh root@"+node.ip+" 'chown vitastor:vitastor /etc/vitastor/*.key && /usr/lib/vitastor/mon/make-etcd'");
}
}
else
{
console.warn('Certificates generated in '+dir+', please copy them to other nodes');
}
}
async function copy_nontls(dir, etcds, num)
{
const to_copy = [ 'vitastor.conf' ];
for (let i = 0; i < etcds.length; i++)
{
if (i == num)
continue;
const node = etcds[i];
await system("scp "+dir+"/"+to_copy.join(" "+dir+"/")+" root@"+(node.ip.indexOf(':') >= 0 ? "["+node.ip+"]" : node.ip)+
":/etc/vitastor/ && ssh root@"+node.ip+" '/usr/lib/vitastor/mon/make-etcd'");
}
}
async function write_auth_config(config, config_path, etcds, use_perms, antietcd_only)
{
const auth = {};
if (use_perms)
{
auth["use_antietcd"] = true;
if (!antietcd_only)
{
auth["etcd_proxy"] = {
urls: etcds.map(e => (e.ip.indexOf(':') >= 0 ? '['+e.ip+']' : e.ip)+':2381'),
cert: "/etc/vitastor/antietcd.crt",
key: "/etc/vitastor/antietcd.key",
ca: "/etc/vitastor/etcd_ca.crt",
};
}
auth["antietcd_cert"] = "/etc/vitastor/antietcd.crt";
auth["antietcd_key"] = "/etc/vitastor/antietcd.key";
auth["etcd_ca"] = "/etc/vitastor/antietcd_ca.crt";
auth["osd_cert"] = "/etc/vitastor/osd.crt";
auth["osd_pkey"] = "/etc/vitastor/osd.key";
auth["osd_ca"] = "/etc/vitastor/osd.crt";
auth["client_ca"] = "/etc/vitastor/client_ca.crt";
auth["admin_ca"] = "/etc/vitastor/admin_ca.crt";
auth["cert"] = "/etc/vitastor/admin.crt";
auth["pkey"] = "/etc/vitastor/admin.key";
}
else
{
if (antietcd_only)
{
auth["use_antietcd"] = true;
auth["antietcd_cert"] = "/etc/vitastor/antietcd.crt";
auth["antietcd_key"] = "/etc/vitastor/antietcd.key";
auth["etcd_ca"] = "/etc/vitastor/antietcd_ca.crt";
}
else
{
auth["etcd_ca"] = "/etc/vitastor/etcd.crt";
}
}
for (const k in auth)
{
if ((k in config) && JSON.stringify(auth[k]) != JSON.stringify(config[k]))
{
// Auth options already overridden with non-default
console.log(k+" is already overridden in "+config_path+", skipping config update");
return;
}
}
for (const k in auth)
{
config[k] = auth[k];
}
console.log(`-----
Updating ${config_path}
-----
`);
fs.writeFileSync(config_path, JSON.stringify(config, 0, 4));
}
async function configure_etcd(etcds, num, tls, use_perms)
{
const in_docker = fs.existsSync("/etc/vitastor/etcd.conf") &&
fs.existsSync("/etc/vitastor/docker.conf");
if (!in_docker && fs.existsSync("/etc/systemd/system/vitastor-etcd.service") &&
fs.existsSync("/etc/vitastor/etcd.conf"))
{
console.log("/etc/systemd/system/vitastor-etcd.service and /etc/vitastor/etcd.conf already exists. Remove them to reinitialize the cluster");
process.exit(1);
}
if (!in_docker && fs.existsSync("/etc/systemd/system/etcd.service"))
{
console.log("/etc/systemd/system/etcd.service already exists");
process.exit(1);
}
const etcd_url = etcds[num].scheme + '://' + etcds[num].addr;
const options = {
name: 'etcd'+etcds[num].ip.replace(/[^0-9a-z_]/ig, '_'),
advertise_client_urls: etcd_url+':'+(use_perms ? 2381 : 2379),
listen_client_urls: etcd_url+':'+(use_perms ? 2381 : 2379),
initial_advertise_peer_urls: etcd_url+':2380',
listen_peer_urls: etcd_url+':2380',
initial_cluster_token: 'vitastor-etcd-1',
initial_cluster_state: 'new',
initial_cluster: etcds.map(e => `etcd${e.ip.replace(/[^0-9a-z_]/ig, '_')}=${e.scheme}://${e.addr}:2380`).join(','),
snapshot_count: 10000,
max_txn_ops: 100000,
max_request_bytes: 104857600,
auto_compaction_retention: 10,
auto_compaction_mode: 'revision',
};
if (tls)
{
options['cert_file'] = '/etc/vitastor/etcd.crt';
options['key_file'] = '/etc/vitastor/etcd.key';
if (use_perms)
{
options['client_cert_auth'] = '1';
options['trusted_ca_file'] = '/etc/vitastor/antietcd_ca.crt';
}
options['peer_cert_file'] = '/etc/vitastor/etcd.crt';
options['peer_key_file'] = '/etc/vitastor/etcd.key';
if (use_perms)
{
options['peer_client_cert_auth'] = '1';
options['peer_trusted_ca_file'] = '/etc/vitastor/etcd_ca.crt';
}
}
let etcd_conf = fs.existsSync("/etc/vitastor/etcd.conf")
? fs.readFileSync("/etc/vitastor/etcd.conf", { encoding: 'utf-8' })
: "";
for (const k in options)
{
etcd_conf = replace_env(etcd_conf, 'ETCD_'+k.toUpperCase().replace(/-/, '_'), options[k]);
}
fs.writeFileSync("/etc/vitastor/etcd.conf", etcd_conf);
const etcd_url = 'http://' + (etcds[num].indexOf(':') >= 0 ? '['+etcds[num]+']' : etcds[num]);
const etcd_name = 'etcd'+etcds[num].replace(/[^0-9a-z_]/ig, '_');
const etcd_cluster = etcds.map(e => `etcd${e.replace(/[^0-9a-z_]/ig, '_')}=http://${e.indexOf(':') >= 0 ? '['+e+']' : e}:2380`).join(',');
if (in_docker)
{
let etcd_conf = fs.readFileSync("/etc/vitastor/etcd.conf", { encoding: 'utf-8' });
etcd_conf = replace_env(etcd_conf, 'ETCD_NAME', etcd_name);
etcd_conf = replace_env(etcd_conf, 'ETCD_IP', etcds[num]);
etcd_conf = replace_env(etcd_conf, 'ETCD_INITIAL_CLUSTER', etcd_cluster);
fs.writeFileSync("/etc/vitastor/etcd.conf", etcd_conf);
console.log('etcd for Vitastor configured. Run `systemctl enable --now vitastor-etcd` to start etcd');
process.exit(0);
}
await system(`mkdir -p /var/lib/etcd/vitastor`);
fs.writeFileSync(
"/etc/systemd/system/vitastor-etcd.service", `[Unit]
"/etc/systemd/system/vitastor-etcd.service",
`[Unit]
Description=etcd for vitastor
After=network-online.target local-fs.target time-sync.target
Wants=network-online.target local-fs.target time-sync.target
@@ -414,8 +83,12 @@ Wants=network-online.target local-fs.target time-sync.target
[Service]
Restart=always
Environment=GOGC=50
EnvironmentFile=/etc/vitastor/etcd.conf
ExecStart=etcd --data-dir /var/lib/etcd/vitastor
ExecStart=etcd --name ${etcd_name} --data-dir /var/lib/etcd/vitastor \\
--snapshot-count 10000 --advertise-client-urls ${etcd_url}:2379 --listen-client-urls ${etcd_url}:2379 \\
--initial-advertise-peer-urls ${etcd_url}:2380 --listen-peer-urls ${etcd_url}:2380 \\
--initial-cluster-token vitastor-etcd-1 --initial-cluster ${etcd_cluster} \\
--initial-cluster-state new --max-txn-ops=100000 --max-request-bytes=104857600 \\
--auto-compaction-retention=10 --auto-compaction-mode=revision
WorkingDirectory=/var/lib/etcd/vitastor
ExecStartPre=+chown -R etcd /var/lib/etcd/vitastor
User=etcd
@@ -429,77 +102,37 @@ RestartSec=10
WantedBy=multi-user.target
`);
await system(`useradd etcd`);
await system(`chown vitastor:vitastor /etc/vitastor/*.key`);
if (options['key_file'])
{
await system(`chown etcd `+options['key_file']);
}
await system(`systemctl daemon-reload`);
// Disable distribution etcd unit and enable our one
await system(`systemctl disable --now etcd`);
await system(`systemctl enable --now vitastor-etcd`);
await system(`systemctl enable --now vitastor-mon`);
process.exit(0);
}
function replace_env(text, key, value)
{
let found = false;
text = text.replace(new RegExp('^'+key+'\\s*=.*', 'm'), () => { found = true; return key+'='+value; });
if (found)
return text;
return (text != '' ? text.replace(/\s*$/, '\n') : '')+key+'='+value+'\n';
return found ? text : text.replace(/\s*$/, '\n')+key+'='+value+'\n';
}
function select_local_etcd(etcds)
{
const ifaces = os.networkInterfaces();
const local = {};
for (const ifname in ifaces)
{
for (const iface of ifaces[ifname])
{
const addr = iface.address;
if (iface.family == 'IPv6')
local[addr.toLowerCase()] = local['['+addr.toLowerCase()+']'] = true;
else
local[addr] = true;
}
}
for (let i = 0; i < etcds.length; i++)
{
if (local[etcds[i].addr])
return i;
}
for (let i = 0; i < etcds.length; i++)
if (etcds[i] == iface.address.toLowerCase())
return i;
return -1;
}
async function system(cmd)
{
console.log('Running '+cmd);
process.stdin.pause();
const cp = child_process.spawn(cmd, { shell: true, stdio: [ 0, 1, 2 ] });
let finish_cb;
cp.on('exit', () => finish_cb && finish_cb());
if (cp.exitCode == null)
await new Promise(ok => finish_cb = ok);
process.stdin.resume();
return cp.exitCode;
}
async function make_ca(subj, filename)
{
if (await system("openssl req -days 3650 -x509 -subj '"+subj+"' -addext basicConstraints=critical,CA:TRUE,pathlen:1"+
" -new -newkey rsa:4096 -nodes -keyout "+filename+".key -out "+filename+".crt"))
process.exit(1);
}
async function make_signed(subj, f, ca, san)
{
if (await system(`openssl req -subj '${subj}' ${san ? "-addext 'subjectAltName="+san+"'" : ""} -nodes -new -keyout ${f}.key -out ${f}.csr`))
process.exit(1);
if (await system(`openssl x509 -req -days 3650 -copy_extensions copy -CA ${ca}.crt -CAkey ${ca}.key -CAcreateserial -in ${f}.csr -out ${f}.crt`))
process.exit(1);
fs.unlinkSync(f+".csr");
}
run().catch(e => { console.error(e); process.exit(1); });

View File

@@ -37,7 +37,6 @@ function derive_osd_stats(st, prev, prev_diff)
const n = c.count - BigInt(pr && pr.count||0);
diff.recovery_stats[op] = { ...c, bps: n > 0 ? b*1000n/timediff : 0n, iops: n > 0 ? n*1000n/timediff : 0n };
}
diff.inode_stats = {};
for (const pool_id in st.inode_stats||{})
{
diff.inode_stats[pool_id] = {};

View File

@@ -1,530 +0,0 @@
// AntiEtcd authentication filter for Vitastor
// (c) Vitaliy Filippov, 2026
// License: Mozilla Public License 2.0 or Vitastor Network Public License 1.1
// Permissions are based on:
// 1. Users.
// Stored in /vitastor/config/user/<username>.
// Has 1 property:
// - groups, a list of group names the user is included in.
// 2. Images.
// Stored in /vitastor/config/inode/<pool>/<inode>. Has the following properties:
// - owner (user name)
// - owner_group (group name)
// - reader_group
// 3. Certificates.
// - osd_ca identifies OSDs.
// - admin_ca (optional) identifies administrators.
// - mon_ca (optional) identifies monitors.
const { X509Certificate } = require('node:crypto');
const static_perms = {
invalid: {
keys: {},
prefixes: {},
},
osd: {
keys: { '/pg/config': false },
prefixes: { '/config/': false, '/osd/': true, '/pg/state/': true, '/pg/history/': true, '/pgstats/': true },
},
mon: {
keys: { '/pg/config': true, '/stats': true, '/history/last_clean_pgs': true },
prefixes: {
'/config/': false, '/osd/': false, '/mon/': true, '/pg/history/': true,
'/pg/stats/': true, '/pgstats/': true, '/inode/stats/': true, '/pool/stats/': true,
},
},
admin: {
keys: { '/pg/config': true, '/stats': true, '/history/last_clean_pgs': true },
prefixes: {
'/config/': true, '/osd/': true, '/index/': true, '/pg/history/': true,
'/mon/': false, '/pg/': false, '/pgstats/': false, '/inode/stats/': false, '/pool/stats/': false,
},
},
client: {
keys: { '/config/global': false, '/config/node_placement': false, '/config/pools': false, '/pg/config': false },
prefixes: { '/osd/state/': false, '/pg/state/': false, '/index/maxid/': false },
},
};
const api_perms = {
osd: { lease_grant: true, lease_revoke: true, lease_keepalive: true, maintenance_status: true },
mon: { lease_grant: true, lease_revoke: true, lease_keepalive: true, maintenance_status: true },
admin: { maintenance_status: true },
client: { maintenance_status: true },
};
class VitastorAuthFilter
{
constructor(antietcd)
{
this.cfg = antietcd.cfg;
this.antietcd = antietcd;
this.prefix = this.cfg.vitastor_prefix || '/vitastor';
this.prefix_parts = this.prefix.split('/');
}
async init()
{
if (!this.cfg.cert || !this.cfg.key || !this.cfg.ca || !this.cfg.osd_ca || !this.cfg.client_cert_auth)
{
throw new Error('Authenticated Vitastor setups require enabled client_cert_auth, cert, key'+
' and separate ca (client CA), osd_ca and optionally admin_ca and mon_ca');
}
this.osd_ca = await this.antietcd.readPEM(this.cfg.osd_ca);
this.osd_ca_obj = new X509Certificate(this.osd_ca);
this.antietcd.tls.ca.push(this.osd_ca);
if (this.cfg.mon_ca)
{
this.mon_ca = await this.antietcd.readPEM(this.cfg.mon_ca);
this.mon_ca_obj = new X509Certificate(this.mon_ca);
this.antietcd.tls.ca.push(this.mon_ca);
}
if (this.cfg.admin_ca)
{
this.admin_ca = await this.antietcd.readPEM(this.cfg.admin_ca);
this.admin_ca_obj = new X509Certificate(this.admin_ca);
this.antietcd.tls.ca.push(this.admin_ca);
}
}
init_context(context, socket)
{
let cert = socket.getPeerX509Certificate();
if (cert.issuer == this.osd_ca_obj.subject && cert.verify(this.osd_ca_obj.publicKey))
{
context.user_type = 'osd';
}
else if (this.mon_ca_obj && cert.issuer == this.mon_ca_obj.subject && cert.verify(this.mon_ca_obj.publicKey))
{
context.user_type = 'mon';
}
else if (this.admin_ca_obj && cert.issuer == this.admin_ca_obj.subject && cert.verify(this.admin_ca_obj.publicKey))
{
context.user_type = 'admin';
}
}
_get(path, decode)
{
let cur = this.antietcd.etctree.state;
path = path instanceof Array ? path : path.split('/');
for (const p of path)
{
if (!cur.children)
{
return null;
}
cur = cur.children[p];
if (!cur)
{
return null;
}
}
if (decode)
{
return this._decode(path, cur.value);
}
return cur;
}
_decode(path, cur)
{
if (!cur)
{
return null;
}
if (cur)
{
try
{
cur = JSON.parse(cur);
}
catch (e)
{
console.warn('Invalid JSON in '+(path instanceof Array ? path.join('/') : path)+': '+e);
}
}
return cur;
}
// userInfo: { name: string, perms: static_perms[type], groups: { [string]: true } }
_check_compare(check, userInfo, checked)
{
let key = String(check.key);
if (key.substr(0, this.prefix.length) !== this.prefix)
{
return false;
}
key = key.substr(this.prefix.length);
if (key in userInfo.perms.keys)
{
return true;
}
for (const pfx in userInfo.perms.prefixes)
{
if (key.substr(0, pfx.length) == pfx)
{
return true;
}
}
if (userInfo.type == 'client')
{
// Image permissions
if (key.substr(0, 14) == '/config/inode/')
{
// Allowed to check that a key does not exist
if (check.target == 'VERSION' && check.version == 0)
{
checked['M'+key] = true;
return true;
}
else if (check.target == 'MOD')
{
const data = this._get(check.key);
if (!data || data.mod_revision != check.mod_revision)
{
// Break check to trigger CAS failure
check.mod_revision = '18446744073709551615'; // UINT64_MAX
return true;
}
const inode = this._decode(check.key, data.value);
if (inode && (inode.owner_group && userInfo.groups[inode.owner_group] ||
inode.owner === userInfo.name))
{
checked['M'+key] = true;
return true;
}
}
return false;
}
if (key.substr(0, 13) == '/index/image/')
{
// Allowed to check that a key does not exist
if (check.target == 'VERSION' && check.version == 0)
{
checked['M'+key] = true;
return true;
}
else if (check.target == 'MOD')
{
let data = this._get(check.key);
if (!data || data.mod_revision != check.mod_revision)
{
// Break check to trigger CAS failure
check.mod_revision = '18446744073709551615'; // UINT64_MAX
return true;
}
data = this._decode(check.key, data.value);
if (data)
{
const inode = this._get([ ...this.prefix_parts, 'config', 'inode', data.pool_id, data.id ], true);
if (inode && (inode.owner_group && userInfo.groups[inode.owner_group] ||
inode.owner === userInfo.name))
{
checked['M'+key] = true;
return true;
}
}
}
return false;
}
if (key.substr(0, 13) == '/index/maxid/')
{
const pool_id = key.substr(13);
const pool_cfg = this._get([ ...this.prefix_parts, 'config', 'pools' ], true);
if (!pool_cfg || !pool_cfg[pool_id] || !pool_cfg[pool_id].creator_group || !userInfo.groups[pool_cfg[pool_id].creator_group])
{
return false;
}
if (check.target == 'VERSION' && check.version == 0)
{
checked['I'+parseInt(key.substr(13))+'_0'] = true;
return true;
}
else if (check.target == 'MOD')
{
const data = this._get(check.key);
if (!data || data.mod_revision != check.mod_revision)
{
// Break check to trigger CAS failure
check.mod_revision = '18446744073709551615'; // UINT64_MAX
return true;
}
checked['I'+parseInt(key.substr(13))+'_'+data.value] = true;
return true;
}
return false;
}
}
return false;
}
_check_read(kv, userInfo)
{
let key = String(kv.key);
if (key.substr(0, this.prefix.length) !== this.prefix)
{
return false;
}
key = key.substr(this.prefix.length);
if (key in userInfo.perms.keys)
{
return true;
}
for (const pfx in userInfo.perms.prefixes)
{
if (key.substr(0, pfx.length) == pfx)
{
return true;
}
}
if (userInfo.type == 'client')
{
// Image permissions
if (key.substr(0, 14) == '/config/inode/')
{
const inode = this._decode(kv.key, kv.value);
if (inode && (inode.reader_group && userInfo.groups[inode.reader_group] ||
inode.owner_group && userInfo.groups[inode.owner_group] ||
inode.owner === userInfo.name))
{
return true;
}
return false;
}
if (key.substr(0, 13) == '/index/image/')
{
const data = this._decode(kv.key, kv.value);
const inode = this._get([ ...this.prefix_parts, 'config', 'inode', data.pool_id, data.id ], true);
if (inode && (inode.reader_group && userInfo.groups[inode.reader_group] ||
inode.owner_group && userInfo.groups[inode.owner_group] ||
inode.owner === userInfo.name))
{
return true;
}
return false;
}
if (key.substr(0, 13) == '/inode/stats/')
{
const [ pool_id, id ] = key.substr(13).split('/');
const inode = this._get([ ...this.prefix_parts, 'config', 'inode', pool_id, id ], true);
if (inode && (inode.reader_group && userInfo.groups[inode.reader_group] ||
inode.owner_group && userInfo.groups[inode.owner_group] ||
inode.owner === userInfo.name))
{
return true;
}
return false;
}
}
return false;
}
_check_write(put, userInfo, checked)
{
let key = String(put.key);
if (key.substr(0, this.prefix.length) !== this.prefix)
{
return false;
}
key = key.substr(this.prefix.length);
if (userInfo.perms.keys[key])
{
return true;
}
for (const pfx in userInfo.perms.prefixes)
{
if (userInfo.perms.prefixes[pfx] && key.substr(0, pfx.length) == pfx)
{
return true;
}
}
if (checked && userInfo.type == 'client')
{
if (key.substr(0, 13) == '/index/maxid/' &&
checked['I'+parseInt(key.substr(13))+'_'+(put.value-1)])
{
// Allowed to increment maxid
return true;
}
if (checked['M'+key])
{
// Allowed to modify known images with CAS checks
return true;
}
}
return false;
}
_check_req(req, userInfo, checked)
{
let r;
if ((r = (req.request_range || req.requestRange)))
{
// All range queries are allowed, but responses are filtered - it's simpler
}
else if ((r = (req.request_put || req.requestPut)))
{
if (!this._check_write(r, userInfo, checked))
return false;
}
else if ((r = (req.request_delete_range || req.requestDeleteRange)))
{
if (!r.range_end || r.range_end === r.key)
{
if (!this._check_write({ key: r.key }, userInfo))
return false;
}
else
{
// All keys in range must satisfy prefix
r.range_end = String(r.range_end);
if (r.key.length != r.range_end.length ||
r.key[r.key.length-1] != '/' ||
r.range_end[r.range_end.length-1] != '0')
{
return false;
}
let key = r.key.substr(this.prefix.length);
let found = false;
for (const pfx in userInfo.perms.prefixes)
{
if (userInfo.perms.prefixes[pfx] && key.substr(0, pfx.length) == pfx)
{
found = true;
break;
}
}
if (!found)
return false;
}
}
return true;
}
_get_user(context)
{
if (context.user_type === 'osd' || context.user_type === 'mon' || context.user_type === 'admin')
{
return {
name: context.user_type,
type: context.user_type,
perms: static_perms[context.user_type],
};
}
if (!context.username)
{
return {};
}
let userInfo = this._get([ ...this.prefix_parts, 'config', 'user', context.username ], true);
if (!userInfo)
{
userInfo = {};
}
userInfo.perms = static_perms[userInfo.type] || static_perms['invalid'];
userInfo.name = context.username;
if (userInfo.groups instanceof Array)
{
userInfo.groups = userInfo.groups.reduce((a, c) => { a[c] = true; return a; }, {});
}
else
{
userInfo.groups = {};
}
return userInfo;
}
filter_api(context, api/*, data*/)
{
let type = 'client';
if (context.user_type === 'osd' || context.user_type === 'mon' || context.user_type === 'admin')
{
type = context.user_type;
}
return api_perms[type] && api_perms[type][api];
}
filter_txn(context, txn)
{
const userInfo = this._get_user(context);
if (!userInfo)
{
return null;
}
const checked = {};
if (txn.compare)
{
for (const check of txn.compare)
{
if (!this._check_compare(check, userInfo, checked))
return null;
}
}
// Special transactions:
// 1. create image: create config/inode and index/image, increment index/maxid/<pool> (with CAS)
// 2. create snapshot: same as create image but also rename previous to @snap
if (txn.success)
{
for (const req of txn.success)
{
if (!this._check_req(req, userInfo, checked))
return null;
}
}
if (txn.failure)
{
for (const req of txn.failure)
{
if (!this._check_req(req, userInfo, null))
return null;
}
}
return txn;
}
filter_txn_response(context, txn, res)
{
if (!res.responses)
{
return;
}
const userInfo = this._get_user(context);
if (!userInfo)
{
for (const resp of res.responses)
{
if (resp.response_range && resp.response_range.kvs)
{
resp.response_range.kvs = [];
}
}
return;
}
for (const resp of res.responses)
{
if (resp.response_range && resp.response_range.kvs)
{
resp.response_range.kvs = resp.response_range.kvs.filter(kv => this._check_read(kv, userInfo));
}
}
}
filter_watch_message(context, msg)
{
if (!msg.result || !msg.result.events)
{
return;
}
const userInfo = this._get_user(context);
if (!userInfo)
{
msg.result.events = [];
return;
}
msg.result.events = msg.result.events.filter(ev => this._check_read(ev.kv, userInfo));
}
}
module.exports = VitastorAuthFilter;

View File

@@ -1,6 +1,6 @@
{
"name": "vitastor",
"version": "3.1.0",
"version": "3.0.5",
"description": "Low-level native bindings to Vitastor client library",
"main": "index.js",
"keywords": [

View File

@@ -366,38 +366,15 @@ sub map_volume
my $prefix = defined $scfg->{vitastor_prefix} ? $scfg->{vitastor_prefix} : 'pve/';
my ($vtype, $img_name, $vmid) = $class->parse_volname($volname);
my $name = $prefix.$img_name;
my $name = $img_name;
$name .= '@'.$snapname if $snapname;
my $mapped = run_cli($scfg, [ 'ls' ], binary => '/usr/bin/vitastor-nbd');
my ($kerneldev) = grep {
$mapped->{$_} && $mapped->{$_}->{image} && $mapped->{$_}->{image} eq $name
} keys %$mapped;
my ($kerneldev) = grep { $mapped->{$_}->{image} eq $prefix.$name } keys %$mapped;
return $kerneldev if $kerneldev && -b $kerneldev; # already mapped
if ($kerneldev && -b $kerneldev)
{
my $size = `/usr/sbin/blockdev --getsize64 $kerneldev`;
return $kerneldev if $size && $size > 0;
}
my $map_out = run_cli($scfg, [ 'map', '--image', $name ], binary => '/usr/bin/vitastor-nbd', json => 0);
$map_out =~ s/^\s+|\s+$//gso;
# Wait until the device is started
for (my $i = 0; $i < 100; $i++)
{
$mapped = run_cli($scfg, [ 'ls' ], binary => '/usr/bin/vitastor-nbd');
($kerneldev) = grep { $mapped->{$_} && $mapped->{$_}->{image} && $mapped->{$_}->{image} eq $name } keys %$mapped;
if ($kerneldev && -b $kerneldev)
{
my $size = `/usr/sbin/blockdev --getsize64 $kerneldev`;
return $kerneldev if $size && $size > 0;
}
select(undef, undef, undef, 0.1);
}
die "Failed to map Vitastor image $name via NBD".
($map_out ? ", vitastor-nbd map returned '$map_out'" : "")."\n";
$kerneldev = run_cli($scfg, [ 'map', '--image', $prefix.$name ], binary => '/usr/bin/vitastor-nbd', json => 0);
return $kerneldev;
}
sub unmap_volume
@@ -406,19 +383,13 @@ sub unmap_volume
my $prefix = defined $scfg->{vitastor_prefix} ? $scfg->{vitastor_prefix} : 'pve/';
my ($vtype, $name, $vmid) = $class->parse_volname($volname);
$name = $prefix.$name;
$name .= '@'.$snapname if $snapname;
my $mapped = run_cli($scfg, [ 'ls' ], binary => '/usr/bin/vitastor-nbd');
my @kerneldevs = grep {
$mapped->{$_} && $mapped->{$_}->{image} && $mapped->{$_}->{image} eq $name
} keys %$mapped;
for my $kerneldev (@kerneldevs)
my ($kerneldev) = grep { $mapped->{$_}->{image} eq $prefix.$name } keys %$mapped;
if ($kerneldev && -b $kerneldev)
{
next if !$kerneldev || !-b $kerneldev;
eval { run_cli($scfg, [ 'unmap', $kerneldev ], binary => '/usr/bin/vitastor-nbd', json => 0); };
warn "Failed to unmap Vitastor image $name from $kerneldev: $@" if $@;
run_cli($scfg, [ 'unmap', $kerneldev ], binary => '/usr/bin/vitastor-nbd', json => 0);
}
return 1;
@@ -434,13 +405,7 @@ sub activate_volume
sub deactivate_volume
{
my ($class, $storeid, $scfg, $volname, $snapname, $cache) = @_;
# Even with vitastor_nbd=0, Proxmox may call map_volume() for special
# volumes like tpmstate0 because swtpm needs a local file/block path.
# Therefore, always try to unmap an existing NBD mapping here.
# unmap_volume() is a no-op if the volume is not currently mapped.
$class->unmap_volume($storeid, $scfg, $volname, $snapname);
$class->unmap_volume($storeid, $scfg, $volname, $snapname) if $scfg->{vitastor_nbd};
return 1;
}

View File

@@ -50,7 +50,7 @@ from cinder.volume import configuration
from cinder.volume import driver
from cinder.volume import volume_utils
VITASTOR_VERSION = '3.1.0'
VITASTOR_VERSION = '3.0.5'
LOG = logging.getLogger(__name__)
@@ -608,7 +608,7 @@ class VitastorDriver(driver.CloneableImageVD,
"""Extend an existing volume."""
vol_name = utils.convert_str(volume.name)
size = int(new_size) * units.Gi
self._cli('extend volume', 'modify', vol_name, '--resize', size)
self._cli('extend volume', 'modify', vol_name, '--resize', new_size)
LOG.debug(
"Extend volume from %(old_size)s GB to %(new_size)s GB.",
{'old_size': volume.size, 'new_size': new_size}

View File

@@ -1,637 +0,0 @@
diff --git a/include/libvirt/libvirt-storage.h b/include/libvirt/libvirt-storage.h
index aaad4a3da1..5f5daa8341 100644
--- a/include/libvirt/libvirt-storage.h
+++ b/include/libvirt/libvirt-storage.h
@@ -326,6 +326,7 @@ typedef enum {
VIR_CONNECT_LIST_STORAGE_POOLS_ZFS = 1 << 17, /* (Since: 1.2.8) */
VIR_CONNECT_LIST_STORAGE_POOLS_VSTORAGE = 1 << 18, /* (Since: 3.1.0) */
VIR_CONNECT_LIST_STORAGE_POOLS_ISCSI_DIRECT = 1 << 19, /* (Since: 5.6.0) */
+ VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR = 1 << 20, /* (Since: 5.0.0) */
} virConnectListAllStoragePoolsFlags;
int virConnectListAllStoragePools(virConnectPtr conn,
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 9ca5c2450c..cc52f00c0c 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -7453,7 +7453,8 @@ virDomainDiskSourceNetworkParse(xmlNodePtr node,
src->configFile = virXPathString("string(./config/@file)", ctxt);
if (src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTP ||
- src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS)
+ src->protocol == VIR_STORAGE_NET_PROTOCOL_HTTPS ||
+ src->protocol == VIR_STORAGE_NET_PROTOCOL_VITASTOR)
src->query = virXMLPropString(node, "query");
if (virDomainStorageNetworkParseHosts(node, ctxt, &src->hosts, &src->nhosts) < 0)
@@ -32187,6 +32188,7 @@ virDomainStorageSourceTranslateSourcePool(virStorageSource *src,
case VIR_STORAGE_POOL_MPATH:
case VIR_STORAGE_POOL_RBD:
+ case VIR_STORAGE_POOL_VITASTOR:
case VIR_STORAGE_POOL_SHEEPDOG:
case VIR_STORAGE_POOL_GLUSTER:
case VIR_STORAGE_POOL_LAST:
diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c
index 7346a61731..83e94d762e 100644
--- a/src/conf/domain_validate.c
+++ b/src/conf/domain_validate.c
@@ -520,6 +520,7 @@ virDomainDiskDefValidateSourceChainOne(const virStorageSource *src)
case VIR_STORAGE_NET_PROTOCOL_RBD:
break;
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
case VIR_STORAGE_NET_PROTOCOL_NBD:
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
@@ -592,7 +593,7 @@ virDomainDiskDefValidateSourceChainOne(const virStorageSource *src)
}
}
- /* internal snapshots and config files are currently supported only with rbd: */
+ /* internal snapshots are currently supported only with rbd: */
if (virStorageSourceGetActualType(src) != VIR_STORAGE_TYPE_NETWORK &&
src->protocol != VIR_STORAGE_NET_PROTOCOL_RBD) {
if (src->snapshot) {
@@ -600,10 +601,14 @@ virDomainDiskDefValidateSourceChainOne(const virStorageSource *src)
_("<snapshot> element is currently supported only with 'rbd' disks"));
return -1;
}
-
+ }
+ /* config files are currently supported only with rbd and vitastor: */
+ if (virStorageSourceGetActualType(src) != VIR_STORAGE_TYPE_NETWORK &&
+ src->protocol != VIR_STORAGE_NET_PROTOCOL_RBD &&
+ src->protocol != VIR_STORAGE_NET_PROTOCOL_VITASTOR) {
if (src->configFile) {
virReportError(VIR_ERR_XML_ERROR, "%s",
- _("<config> element is currently supported only with 'rbd' disks"));
+ _("<config> element is currently supported only with 'rbd' and 'vitastor' disks"));
return -1;
}
}
diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng
index 114dd3f96f..c71f9a3277 100644
--- a/src/conf/schemas/domaincommon.rng
+++ b/src/conf/schemas/domaincommon.rng
@@ -2093,6 +2093,35 @@
</element>
</define>
+ <define name="diskSourceNetworkProtocolVitastor">
+ <element name="source">
+ <interleave>
+ <attribute name="protocol">
+ <value>vitastor</value>
+ </attribute>
+ <ref name="diskSourceCommon"/>
+ <optional>
+ <attribute name="name"/>
+ </optional>
+ <optional>
+ <attribute name="query"/>
+ </optional>
+ <zeroOrMore>
+ <ref name="diskSourceNetworkHost"/>
+ </zeroOrMore>
+ <optional>
+ <element name="config">
+ <attribute name="file">
+ <ref name="absFilePath"/>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
+ <empty/>
+ </interleave>
+ </element>
+ </define>
+
<define name="diskSourceNetworkProtocolISCSI">
<element name="source">
<attribute name="protocol">
@@ -2443,6 +2472,7 @@
<ref name="diskSourceNetworkProtocolSimple"/>
<ref name="diskSourceNetworkProtocolVxHS"/>
<ref name="diskSourceNetworkProtocolNFS"/>
+ <ref name="diskSourceNetworkProtocolVitastor"/>
</choice>
</define>
diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c
index 1dc9365bf2..a8a736be81 100644
--- a/src/conf/storage_conf.c
+++ b/src/conf/storage_conf.c
@@ -56,7 +56,7 @@ VIR_ENUM_IMPL(virStoragePool,
"logical", "disk", "iscsi",
"iscsi-direct", "scsi", "mpath",
"rbd", "sheepdog", "gluster",
- "zfs", "vstorage",
+ "zfs", "vstorage", "vitastor",
);
VIR_ENUM_IMPL(virStoragePoolFormatFileSystem,
@@ -242,6 +242,18 @@ static virStoragePoolTypeInfo poolTypeInfo[] = {
.formatToString = virStorageFileFormatTypeToString,
}
},
+ {.poolType = VIR_STORAGE_POOL_VITASTOR,
+ .poolOptions = {
+ .flags = (VIR_STORAGE_POOL_SOURCE_HOST |
+ VIR_STORAGE_POOL_SOURCE_NETWORK |
+ VIR_STORAGE_POOL_SOURCE_NAME),
+ },
+ .volOptions = {
+ .defaultFormat = VIR_STORAGE_FILE_RAW,
+ .formatFromString = virStorageVolumeFormatFromString,
+ .formatToString = virStorageFileFormatTypeToString,
+ }
+ },
{.poolType = VIR_STORAGE_POOL_SHEEPDOG,
.poolOptions = {
.flags = (VIR_STORAGE_POOL_SOURCE_HOST |
@@ -538,6 +550,11 @@ virStoragePoolDefParseSource(xmlXPathContextPtr ctxt,
_("element 'name' is mandatory for RBD pool"));
return -1;
}
+ if (pool_type == VIR_STORAGE_POOL_VITASTOR && source->name == NULL) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("element 'name' is mandatory for Vitastor pool"));
+ return -1;
+ }
if (options->formatFromString) {
g_autofree char *format = NULL;
@@ -1127,6 +1144,7 @@ virStoragePoolDefFormatBuf(virBuffer *buf,
/* RBD, Sheepdog, Gluster and Iscsi-direct devices are not local block devs nor
* files, so they don't have a target */
if (def->type != VIR_STORAGE_POOL_RBD &&
+ def->type != VIR_STORAGE_POOL_VITASTOR &&
def->type != VIR_STORAGE_POOL_SHEEPDOG &&
def->type != VIR_STORAGE_POOL_GLUSTER &&
def->type != VIR_STORAGE_POOL_ISCSI_DIRECT) {
diff --git a/src/conf/storage_conf.h b/src/conf/storage_conf.h
index fc67957cfe..720c07ef74 100644
--- a/src/conf/storage_conf.h
+++ b/src/conf/storage_conf.h
@@ -103,6 +103,7 @@ typedef enum {
VIR_STORAGE_POOL_GLUSTER, /* Gluster device */
VIR_STORAGE_POOL_ZFS, /* ZFS */
VIR_STORAGE_POOL_VSTORAGE, /* Virtuozzo Storage */
+ VIR_STORAGE_POOL_VITASTOR, /* Vitastor */
VIR_STORAGE_POOL_LAST,
} virStoragePoolType;
@@ -454,6 +455,7 @@ VIR_ENUM_DECL(virStoragePartedFs);
VIR_CONNECT_LIST_STORAGE_POOLS_SCSI | \
VIR_CONNECT_LIST_STORAGE_POOLS_MPATH | \
VIR_CONNECT_LIST_STORAGE_POOLS_RBD | \
+ VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR | \
VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG | \
VIR_CONNECT_LIST_STORAGE_POOLS_GLUSTER | \
VIR_CONNECT_LIST_STORAGE_POOLS_ZFS | \
diff --git a/src/conf/storage_source_conf.c b/src/conf/storage_source_conf.c
index d7b9bdfecb..38aefd0dd4 100644
--- a/src/conf/storage_source_conf.c
+++ b/src/conf/storage_source_conf.c
@@ -90,6 +90,7 @@ VIR_ENUM_IMPL(virStorageNetProtocol,
"ssh",
"vxhs",
"nfs",
+ "vitastor",
);
@@ -1317,6 +1318,7 @@ virStorageSourceNetworkDefaultPort(virStorageNetProtocol protocol)
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
return 24007;
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
case VIR_STORAGE_NET_PROTOCOL_RBD:
/* we don't provide a default for RBD */
return 0;
diff --git a/src/conf/storage_source_conf.h b/src/conf/storage_source_conf.h
index 22c35d420d..f1e32ea83d 100644
--- a/src/conf/storage_source_conf.h
+++ b/src/conf/storage_source_conf.h
@@ -131,6 +131,7 @@ typedef enum {
VIR_STORAGE_NET_PROTOCOL_SSH,
VIR_STORAGE_NET_PROTOCOL_VXHS,
VIR_STORAGE_NET_PROTOCOL_NFS,
+ VIR_STORAGE_NET_PROTOCOL_VITASTOR,
VIR_STORAGE_NET_PROTOCOL_LAST
} virStorageNetProtocol;
diff --git a/src/conf/virstorageobj.c b/src/conf/virstorageobj.c
index 59fa5da372..4739167f5f 100644
--- a/src/conf/virstorageobj.c
+++ b/src/conf/virstorageobj.c
@@ -1438,6 +1438,7 @@ virStoragePoolObjSourceFindDuplicateCb(const void *payload,
return 1;
break;
+ case VIR_STORAGE_POOL_VITASTOR:
case VIR_STORAGE_POOL_ISCSI_DIRECT:
case VIR_STORAGE_POOL_RBD:
case VIR_STORAGE_POOL_LAST:
@@ -1921,6 +1922,8 @@ virStoragePoolObjMatch(virStoragePoolObj *obj,
(obj->def->type == VIR_STORAGE_POOL_MPATH)) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_RBD) &&
(obj->def->type == VIR_STORAGE_POOL_RBD)) ||
+ (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR) &&
+ (obj->def->type == VIR_STORAGE_POOL_VITASTOR)) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG) &&
(obj->def->type == VIR_STORAGE_POOL_SHEEPDOG)) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_GLUSTER) &&
diff --git a/src/libvirt-storage.c b/src/libvirt-storage.c
index db7660aac4..561df34709 100644
--- a/src/libvirt-storage.c
+++ b/src/libvirt-storage.c
@@ -94,6 +94,7 @@ virStoragePoolGetConnect(virStoragePoolPtr pool)
* VIR_CONNECT_LIST_STORAGE_POOLS_SCSI
* VIR_CONNECT_LIST_STORAGE_POOLS_MPATH
* VIR_CONNECT_LIST_STORAGE_POOLS_RBD
+ * VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR
* VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG
* VIR_CONNECT_LIST_STORAGE_POOLS_GLUSTER
* VIR_CONNECT_LIST_STORAGE_POOLS_ZFS
diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c
index 2b988157fa..9d0eb47b25 100644
--- a/src/libxl/libxl_conf.c
+++ b/src/libxl/libxl_conf.c
@@ -1069,6 +1069,7 @@ libxlMakeNetworkDiskSrcStr(virStorageSource *src,
case VIR_STORAGE_NET_PROTOCOL_SSH:
case VIR_STORAGE_NET_PROTOCOL_VXHS:
case VIR_STORAGE_NET_PROTOCOL_NFS:
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
case VIR_STORAGE_NET_PROTOCOL_LAST:
case VIR_STORAGE_NET_PROTOCOL_NONE:
virReportError(VIR_ERR_NO_SUPPORT,
diff --git a/src/libxl/xen_xl.c b/src/libxl/xen_xl.c
index e72e7d7f44..8482c21805 100644
--- a/src/libxl/xen_xl.c
+++ b/src/libxl/xen_xl.c
@@ -1461,6 +1461,7 @@ xenFormatXLDiskSrcNet(virStorageSource *src)
case VIR_STORAGE_NET_PROTOCOL_SSH:
case VIR_STORAGE_NET_PROTOCOL_VXHS:
case VIR_STORAGE_NET_PROTOCOL_NFS:
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
case VIR_STORAGE_NET_PROTOCOL_LAST:
case VIR_STORAGE_NET_PROTOCOL_NONE:
virReportError(VIR_ERR_NO_SUPPORT,
diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c
index 9b43279797..459d8e8a65 100644
--- a/src/qemu/qemu_block.c
+++ b/src/qemu/qemu_block.c
@@ -743,6 +743,38 @@ qemuBlockStorageSourceGetRBDProps(virStorageSource *src,
}
+static virJSONValue *
+qemuBlockStorageSourceGetVitastorProps(virStorageSource *src)
+{
+ virJSONValue *ret = NULL;
+ virStorageNetHostDef *host;
+ size_t i;
+ g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
+ g_autofree char *etcd = NULL;
+
+ for (i = 0; i < src->nhosts; i++) {
+ host = src->hosts + i;
+ if ((virStorageNetHostTransport)host->transport != VIR_STORAGE_NET_HOST_TRANS_TCP) {
+ return NULL;
+ }
+ virBufferAsprintf(&buf, i > 0 ? ",%s:%u" : "%s:%u", host->name, host->port);
+ }
+ if (src->nhosts > 0) {
+ etcd = virBufferContentAndReset(&buf);
+ }
+
+ if (virJSONValueObjectAdd(&ret,
+ "S:etcd-host", etcd,
+ "S:etcd-prefix", src->query,
+ "S:config-path", src->configFile,
+ "s:image", src->path,
+ NULL) < 0)
+ return NULL;
+
+ return ret;
+}
+
+
static virJSONValue *
qemuBlockStorageSourceGetSshProps(virStorageSource *src)
{
@@ -1094,6 +1126,12 @@ qemuBlockStorageSourceGetBackendProps(virStorageSource *src,
return NULL;
break;
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
+ driver = "vitastor";
+ if (!(fileprops = qemuBlockStorageSourceGetVitastorProps(src)))
+ return NULL;
+ break;
+
case VIR_STORAGE_NET_PROTOCOL_SSH:
driver = "ssh";
if (!(fileprops = qemuBlockStorageSourceGetSshProps(src)))
@@ -1997,6 +2035,7 @@ qemuBlockGetBackingStoreString(virStorageSource *src,
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
case VIR_STORAGE_NET_PROTOCOL_RBD:
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
case VIR_STORAGE_NET_PROTOCOL_VXHS:
case VIR_STORAGE_NET_PROTOCOL_NFS:
case VIR_STORAGE_NET_PROTOCOL_SSH:
@@ -2377,6 +2416,12 @@ qemuBlockStorageSourceCreateGetStorageProps(virStorageSource *src,
return -1;
break;
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
+ driver = "vitastor";
+ if (!(location = qemuBlockStorageSourceGetVitastorProps(src)))
+ return -1;
+ break;
+
case VIR_STORAGE_NET_PROTOCOL_SSH:
if (srcPriv->nbdkitProcess) {
/* disk creation not yet supported with nbdkit, and even if it
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index ac56fc7cb4..9e407b4aab 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -4677,7 +4677,8 @@ qemuDomainValidateStorageSource(virStorageSource *src,
if (src->query &&
(actualType != VIR_STORAGE_TYPE_NETWORK ||
(src->protocol != VIR_STORAGE_NET_PROTOCOL_HTTPS &&
- src->protocol != VIR_STORAGE_NET_PROTOCOL_HTTP))) {
+ src->protocol != VIR_STORAGE_NET_PROTOCOL_HTTP &&
+ src->protocol != VIR_STORAGE_NET_PROTOCOL_VITASTOR))) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("query is supported only with HTTP(S) protocols"));
return -1;
@@ -9103,6 +9104,7 @@ qemuDomainPrepareStorageSourceTLS(virStorageSource *src,
break;
case VIR_STORAGE_NET_PROTOCOL_RBD:
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c
index e738afffc3..37d64f469b 100644
--- a/src/qemu/qemu_snapshot.c
+++ b/src/qemu/qemu_snapshot.c
@@ -665,6 +665,7 @@ qemuSnapshotPrepareDiskExternalInactive(virDomainSnapshotDiskDef *snapdisk,
case VIR_STORAGE_NET_PROTOCOL_NONE:
case VIR_STORAGE_NET_PROTOCOL_NBD:
case VIR_STORAGE_NET_PROTOCOL_RBD:
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
@@ -893,6 +894,7 @@ qemuSnapshotPrepareDiskInternal(virDomainDiskDef *disk,
case VIR_STORAGE_NET_PROTOCOL_NONE:
case VIR_STORAGE_NET_PROTOCOL_NBD:
case VIR_STORAGE_NET_PROTOCOL_RBD:
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c
index e19e032427..59f91f4710 100644
--- a/src/storage/storage_driver.c
+++ b/src/storage/storage_driver.c
@@ -1626,6 +1626,7 @@ storageVolLookupByPathCallback(virStoragePoolObj *obj,
case VIR_STORAGE_POOL_GLUSTER:
case VIR_STORAGE_POOL_RBD:
+ case VIR_STORAGE_POOL_VITASTOR:
case VIR_STORAGE_POOL_SHEEPDOG:
case VIR_STORAGE_POOL_ZFS:
case VIR_STORAGE_POOL_LAST:
diff --git a/src/storage_file/storage_source_backingstore.c b/src/storage_file/storage_source_backingstore.c
index 821378883c..2211f6891b 100644
--- a/src/storage_file/storage_source_backingstore.c
+++ b/src/storage_file/storage_source_backingstore.c
@@ -264,6 +264,75 @@ virStorageSourceParseRBDColonString(const char *rbdstr,
}
+static int
+virStorageSourceParseVitastorColonString(const char *colonstr,
+ virStorageSource *src)
+{
+ char *p, *e, *next;
+ g_autofree char *options = NULL;
+
+ /* optionally skip the "vitastor:" prefix if provided */
+ if (STRPREFIX(colonstr, "vitastor:"))
+ colonstr += strlen("vitastor:");
+
+ options = g_strdup(colonstr);
+
+ p = options;
+ while (*p) {
+ /* find : delimiter or end of string */
+ for (e = p; *e && *e != ':'; ++e) {
+ if (*e == '\\') {
+ e++;
+ if (*e == '\0')
+ break;
+ }
+ }
+ if (*e == '\0') {
+ next = e; /* last kv pair */
+ } else {
+ next = e + 1;
+ *e = '\0';
+ }
+
+ if (STRPREFIX(p, "image=")) {
+ src->path = g_strdup(p + strlen("image="));
+ } else if (STRPREFIX(p, "etcd-prefix=")) {
+ src->query = g_strdup(p + strlen("etcd-prefix="));
+ } else if (STRPREFIX(p, "config-path=")) {
+ src->configFile = g_strdup(p + strlen("config-path="));
+ } else if (STRPREFIX(p, "etcd-host=")) {
+ char *h, *sep;
+
+ h = p + strlen("etcd-host=");
+ while (h < e) {
+ for (sep = h; sep < e; ++sep) {
+ if (*sep == '\\' && (sep[1] == ',' ||
+ sep[1] == ';' ||
+ sep[1] == ' ')) {
+ *sep = '\0';
+ sep += 2;
+ break;
+ }
+ }
+
+ if (virStorageSourceRBDAddHost(src, h) < 0)
+ return -1;
+
+ h = sep;
+ }
+ }
+
+ p = next;
+ }
+
+ if (!src->path) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
static int
virStorageSourceParseNBDColonString(const char *nbdstr,
virStorageSource *src)
@@ -379,6 +448,11 @@ virStorageSourceParseBackingColon(virStorageSource *src,
return -1;
break;
+ case VIR_STORAGE_NET_PROTOCOL_VITASTOR:
+ if (virStorageSourceParseVitastorColonString(path, src) < 0)
+ return -1;
+ break;
+
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
case VIR_STORAGE_NET_PROTOCOL_LAST:
case VIR_STORAGE_NET_PROTOCOL_NONE:
@@ -953,6 +1027,54 @@ virStorageSourceParseBackingJSONRBD(virStorageSource *src,
return 0;
}
+static int
+virStorageSourceParseBackingJSONVitastor(virStorageSource *src,
+ virJSONValue *json,
+ const char *jsonstr G_GNUC_UNUSED,
+ int opaque G_GNUC_UNUSED)
+{
+ const char *filename;
+ const char *image = virJSONValueObjectGetString(json, "image");
+ const char *conf = virJSONValueObjectGetString(json, "config-path");
+ const char *etcd_prefix = virJSONValueObjectGetString(json, "etcd-prefix");
+ virJSONValue *servers = virJSONValueObjectGetArray(json, "server");
+ size_t nservers;
+ size_t i;
+
+ src->type = VIR_STORAGE_TYPE_NETWORK;
+ src->protocol = VIR_STORAGE_NET_PROTOCOL_VITASTOR;
+
+ /* legacy syntax passed via 'filename' option */
+ if ((filename = virJSONValueObjectGetString(json, "filename")))
+ return virStorageSourceParseVitastorColonString(filename, src);
+
+ if (!image) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing image name in Vitastor backing volume "
+ "JSON specification"));
+ return -1;
+ }
+
+ src->path = g_strdup(image);
+ src->configFile = g_strdup(conf);
+ src->query = g_strdup(etcd_prefix);
+
+ if (servers) {
+ nservers = virJSONValueArraySize(servers);
+
+ src->hosts = g_new0(virStorageNetHostDef, nservers);
+ src->nhosts = nservers;
+
+ for (i = 0; i < nservers; i++) {
+ if (virStorageSourceParseBackingJSONInetSocketAddress(src->hosts + i,
+ virJSONValueArrayGet(servers, i)) < 0)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
static int
virStorageSourceParseBackingJSONRaw(virStorageSource *src,
virJSONValue *json,
@@ -1130,6 +1252,7 @@ static const struct virStorageSourceJSONDriverParser jsonParsers[] = {
{"sheepdog", false, virStorageSourceParseBackingJSONSheepdog, 0},
{"ssh", false, virStorageSourceParseBackingJSONSSH, 0},
{"rbd", false, virStorageSourceParseBackingJSONRBD, 0},
+ {"vitastor", false, virStorageSourceParseBackingJSONVitastor, 0},
{"raw", true, virStorageSourceParseBackingJSONRaw, 0},
{"nfs", false, virStorageSourceParseBackingJSONNFS, 0},
{"vxhs", false, virStorageSourceParseBackingJSONVxHS, 0},
diff --git a/src/test/test_driver.c b/src/test/test_driver.c
index 1165689de7..bba846351c 100644
--- a/src/test/test_driver.c
+++ b/src/test/test_driver.c
@@ -7345,6 +7345,7 @@ testStorageVolumeTypeForPool(int pooltype)
case VIR_STORAGE_POOL_ISCSI_DIRECT:
case VIR_STORAGE_POOL_GLUSTER:
case VIR_STORAGE_POOL_RBD:
+ case VIR_STORAGE_POOL_VITASTOR:
return VIR_STORAGE_VOL_NETWORK;
case VIR_STORAGE_POOL_LOGICAL:
case VIR_STORAGE_POOL_DISK:
diff --git a/tests/storagepoolcapsschemadata/poolcaps-fs.xml b/tests/storagepoolcapsschemadata/poolcaps-fs.xml
index eee75af746..8bd0a57bdd 100644
--- a/tests/storagepoolcapsschemadata/poolcaps-fs.xml
+++ b/tests/storagepoolcapsschemadata/poolcaps-fs.xml
@@ -204,4 +204,11 @@
</enum>
</volOptions>
</pool>
+ <pool type='vitastor' supported='no'>
+ <volOptions>
+ <defaultFormat type='raw'/>
+ <enum name='targetFormatType'>
+ </enum>
+ </volOptions>
+ </pool>
</storagepoolCapabilities>
diff --git a/tests/storagepoolcapsschemadata/poolcaps-full.xml b/tests/storagepoolcapsschemadata/poolcaps-full.xml
index 805950a937..852df0de16 100644
--- a/tests/storagepoolcapsschemadata/poolcaps-full.xml
+++ b/tests/storagepoolcapsschemadata/poolcaps-full.xml
@@ -204,4 +204,11 @@
</enum>
</volOptions>
</pool>
+ <pool type='vitastor' supported='yes'>
+ <volOptions>
+ <defaultFormat type='raw'/>
+ <enum name='targetFormatType'>
+ </enum>
+ </volOptions>
+ </pool>
</storagepoolCapabilities>
diff --git a/tests/storagepoolxml2argvtest.c b/tests/storagepoolxml2argvtest.c
index d5c2531ab8..b19308ac38 100644
--- a/tests/storagepoolxml2argvtest.c
+++ b/tests/storagepoolxml2argvtest.c
@@ -57,6 +57,7 @@ testCompareXMLToArgvFiles(bool shouldFail,
case VIR_STORAGE_POOL_GLUSTER:
case VIR_STORAGE_POOL_ZFS:
case VIR_STORAGE_POOL_VSTORAGE:
+ case VIR_STORAGE_POOL_VITASTOR:
case VIR_STORAGE_POOL_LAST:
default:
VIR_TEST_DEBUG("pool type '%s' has no xml2argv test", defTypeStr);
diff --git a/tools/virsh-pool.c b/tools/virsh-pool.c
index 2010ef1356..072e2ff9e8 100644
--- a/tools/virsh-pool.c
+++ b/tools/virsh-pool.c
@@ -1187,6 +1187,9 @@ cmdPoolList(vshControl *ctl, const vshCmd *cmd G_GNUC_UNUSED)
case VIR_STORAGE_POOL_VSTORAGE:
flags |= VIR_CONNECT_LIST_STORAGE_POOLS_VSTORAGE;
break;
+ case VIR_STORAGE_POOL_VITASTOR:
+ flags |= VIR_CONNECT_LIST_STORAGE_POOLS_VITASTOR;
+ break;
case VIR_STORAGE_POOL_LAST:
break;
}

View File

@@ -1,172 +1,29 @@
diff --git a/block/meson.build b/block/meson.build
index 34b1b2a306..24ca0f1e52 100644
--- a/block/meson.build
+++ b/block/meson.build
@@ -114,6 +114,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()
diff --git a/meson.build b/meson.build
index 50c774a195..e5c7a3a4b1 100644
--- a/meson.build
+++ b/meson.build
@@ -1652,6 +1652,26 @@ if not get_option('rbd').auto() or have_block
endif
endif
diff --git a/src/client/qemu_driver.c b/src/client/qemu_driver.c
index d8356dab..5f4cd50d 100644
--- a/src/client/qemu_driver.c
+++ b/src/client/qemu_driver.c
@@ -974,14 +974,21 @@ static void vitastor_co_read_bitmap_cb(void *opaque, long retval, uint8_t *bitma
#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'))
+ 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
@@ -2547,6 +2567,7 @@ endif
config_host_data.set('CONFIG_OPENGL', opengl.found())
config_host_data.set('CONFIG_PLUGIN', get_option('plugins'))
config_host_data.set('CONFIG_RBD', rbd.found())
+config_host_data.set('CONFIG_VITASTOR', vitastor.found())
config_host_data.set('CONFIG_RDMA', rdma.found())
config_host_data.set('CONFIG_RELOCATABLE', get_option('relocatable'))
config_host_data.set('CONFIG_SAFESTACK', get_option('safe_stack'))
@@ -4972,6 +4993,7 @@ summary_info += {'fdt support': fdt_opt == 'internal' ? 'internal' : fdt}
summary_info += {'libcap-ng support': libcap_ng}
summary_info += {'bpf support': libbpf}
summary_info += {'rbd support': rbd}
+summary_info += {'vitastor support': vitastor}
summary_info += {'smartcard support': cacard}
summary_info += {'U2F support': u2f}
summary_info += {'libusb': libusb}
diff --git a/meson_options.txt b/meson_options.txt
index fff1521e58..f0844c0e00 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -202,6 +202,8 @@ option('pvg', type: 'feature', value: 'auto',
description: 'macOS paravirtualized graphics support')
option('rbd', type : 'feature', value : 'auto',
description: 'Ceph block device driver')
+option('vitastor', type : 'feature', value : 'auto',
+ description: 'Vitastor block device driver')
option('opengl', type : 'feature', value : 'auto',
description: 'OpenGL support')
option('rdma', type : 'feature', value : 'auto',
diff --git a/qapi/block-core.json b/qapi/block-core.json
index dc6eb4ae23..d043f4340e 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -3280,7 +3280,7 @@
'parallels', 'preallocate', 'qcow', 'qcow2', 'qed', 'quorum',
'raw', 'rbd',
{ 'name': 'replication', 'if': 'CONFIG_REPLICATION' },
- 'ssh', 'throttle', 'vdi', 'vhdx',
+ 'ssh', 'throttle', 'vdi', 'vhdx', 'vitastor',
{ 'name': 'virtio-blk-vfio-pci', 'if': 'CONFIG_BLKIO' },
{ 'name': 'virtio-blk-vhost-user', 'if': 'CONFIG_BLKIO' },
{ 'name': 'virtio-blk-vhost-vdpa', 'if': 'CONFIG_BLKIO' },
@@ -4363,6 +4363,28 @@
'*key-secret': 'str',
'*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:
#
@@ -4831,6 +4853,7 @@
'throttle': 'BlockdevOptionsThrottle',
'vdi': 'BlockdevOptionsGenericFormat',
'vhdx': 'BlockdevOptionsGenericFormat',
+ 'vitastor': 'BlockdevOptionsVitastor',
'virtio-blk-vfio-pci':
{ 'type': 'BlockdevOptionsVirtioBlkVfioPci',
'if': 'CONFIG_BLKIO' },
@@ -5304,6 +5327,20 @@
'*cluster-size' : 'size',
'*encrypt' : 'RbdEncryptionCreateOptions' } }
+##
+# @BlockdevCreateOptionsVitastor:
+#
+# Driver specific image creation options for Vitastor.
+#
+# @location: Where to store the new image file. This location cannot
+# point to a snapshot.
+#
+# @size: Size of the virtual disk in bytes
+##
+{ 'struct': 'BlockdevCreateOptionsVitastor',
+ 'data': { 'location': 'BlockdevOptionsVitastor',
+ 'size': 'size' } }
+
##
# @BlockdevVmdkSubformat:
#
@@ -5526,6 +5563,7 @@
'ssh': 'BlockdevCreateOptionsSsh',
'vdi': 'BlockdevCreateOptionsVdi',
'vhdx': 'BlockdevCreateOptionsVhdx',
+ 'vitastor': 'BlockdevCreateOptionsVitastor',
'vmdk': 'BlockdevCreateOptionsVmdk',
'vpc': 'BlockdevCreateOptionsVpc'
} }
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
index 0ebe6bc52a..2c37ad3892 100644
--- a/scripts/meson-buildoptions.sh
+++ b/scripts/meson-buildoptions.sh
@@ -175,6 +175,7 @@ meson_options_help() {
printf "%s\n" ' qga-vss build QGA VSS support (broken with MinGW)'
printf "%s\n" ' qpl Query Processing Library support'
printf "%s\n" ' rbd Ceph block device driver'
+ printf "%s\n" ' vitastor Vitastor block device driver'
printf "%s\n" ' rdma Enable RDMA-based migration'
printf "%s\n" ' replication replication support'
printf "%s\n" ' rust Rust support'
@@ -459,6 +460,8 @@ _meson_option_parse() {
--disable-qpl) printf "%s" -Dqpl=disabled ;;
--enable-rbd) printf "%s" -Drbd=enabled ;;
--disable-rbd) printf "%s" -Drbd=disabled ;;
+ --enable-vitastor) printf "%s" -Dvitastor=enabled ;;
+ --disable-vitastor) printf "%s" -Dvitastor=disabled ;;
--enable-rdma) printf "%s" -Drdma=enabled ;;
--disable-rdma) printf "%s" -Drdma=disabled ;;
--enable-relocatable) printf "%s" -Drelocatable=true ;;
-static int coroutine_fn vitastor_co_block_status(
- BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes,
- int64_t *pnum, int64_t *map, BlockDriverState **file)
+static int coroutine_fn vitastor_co_block_status(BlockDriverState *bs,
+#if QEMU_VERSION_MAJOR > 10 || QEMU_VERSION_MAJOR == 10 && QEMU_VERSION_MINOR >= 1
+ unsigned int mode,
+#else
+ bool want_zero,
+#endif
+ int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file)
{
// Allocated => return BDRV_BLOCK_DATA|BDRV_BLOCK_OFFSET_VALID
// Not allocated => return 0
// Error => return -errno
// Set pnum to length of the extent, `*map` = `offset`, `*file` = `bs`
+#if QEMU_VERSION_MAJOR > 10 || QEMU_VERSION_MAJOR == 10 && QEMU_VERSION_MINOR >= 1
+ int want_zero = (mode == BDRV_WANT_PRECISE);
+#endif
VitastorRPC task;
VitastorClient *client = bs->opaque;
uint64_t inode = client->watch ? vitastor_c_inode_get_num(client->watch) : client->inode;

View File

@@ -1,172 +0,0 @@
diff --git a/block/meson.build b/block/meson.build
index 34b1b2a306..24ca0f1e52 100644
--- a/block/meson.build
+++ b/block/meson.build
@@ -114,6 +114,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()
diff --git a/meson.build b/meson.build
index d9293294d8..776a5becc6 100644
--- a/meson.build
+++ b/meson.build
@@ -1665,6 +1665,26 @@ if not get_option('rbd').auto() or have_block
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'))
+ 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
@@ -2509,6 +2529,7 @@ endif
config_host_data.set('CONFIG_OPENGL', opengl.found())
config_host_data.set('CONFIG_PLUGIN', get_option('plugins'))
config_host_data.set('CONFIG_RBD', rbd.found())
+config_host_data.set('CONFIG_VITASTOR', vitastor.found())
config_host_data.set('CONFIG_RDMA', rdma.found())
config_host_data.set('CONFIG_RELOCATABLE', get_option('relocatable'))
config_host_data.set('CONFIG_SAFESTACK', get_option('safe_stack'))
@@ -4948,6 +4969,7 @@ summary_info += {'fdt support': fdt_opt == 'internal' ? 'internal' : fdt}
summary_info += {'libcap-ng support': libcap_ng}
summary_info += {'bpf support': libbpf}
summary_info += {'rbd support': rbd}
+summary_info += {'vitastor support': vitastor}
summary_info += {'smartcard support': cacard}
summary_info += {'U2F support': u2f}
summary_info += {'libusb': libusb}
diff --git a/meson_options.txt b/meson_options.txt
index 2836156257..148086cc6f 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -206,6 +206,8 @@ option('pvg', type: 'feature', value: 'auto',
description: 'macOS paravirtualized graphics support')
option('rbd', type : 'feature', value : 'auto',
description: 'Ceph block device driver')
+option('vitastor', type : 'feature', value : 'auto',
+ description: 'Vitastor block device driver')
option('opengl', type : 'feature', value : 'auto',
description: 'OpenGL support')
option('rdma', type : 'feature', value : 'auto',
diff --git a/qapi/block-core.json b/qapi/block-core.json
index b82af74256..f25a6f5ce8 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -3351,7 +3351,7 @@
'parallels', 'preallocate', 'qcow', 'qcow2', 'qed', 'quorum',
'raw', 'rbd',
{ 'name': 'replication', 'if': 'CONFIG_REPLICATION' },
- 'ssh', 'throttle', 'vdi', 'vhdx',
+ 'ssh', 'throttle', 'vdi', 'vhdx', 'vitastor',
{ 'name': 'virtio-blk-vfio-pci', 'if': 'CONFIG_BLKIO' },
{ 'name': 'virtio-blk-vhost-user', 'if': 'CONFIG_BLKIO' },
{ 'name': 'virtio-blk-vhost-vdpa', 'if': 'CONFIG_BLKIO' },
@@ -4434,6 +4434,28 @@
'*key-secret': 'str',
'*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:
#
@@ -4902,6 +4924,7 @@
'throttle': 'BlockdevOptionsThrottle',
'vdi': 'BlockdevOptionsGenericFormat',
'vhdx': 'BlockdevOptionsGenericFormat',
+ 'vitastor': 'BlockdevOptionsVitastor',
'virtio-blk-vfio-pci':
{ 'type': 'BlockdevOptionsVirtioBlkVfioPci',
'if': 'CONFIG_BLKIO' },
@@ -5376,6 +5399,20 @@
'*cluster-size' : 'size',
'*encrypt' : 'RbdEncryptionCreateOptions' } }
+##
+# @BlockdevCreateOptionsVitastor:
+#
+# Driver specific image creation options for Vitastor.
+#
+# @location: Where to store the new image file. This location cannot
+# point to a snapshot.
+#
+# @size: Size of the virtual disk in bytes
+##
+{ 'struct': 'BlockdevCreateOptionsVitastor',
+ 'data': { 'location': 'BlockdevOptionsVitastor',
+ 'size': 'size' } }
+
##
# @BlockdevVmdkSubformat:
#
@@ -5598,6 +5635,7 @@
'ssh': 'BlockdevCreateOptionsSsh',
'vdi': 'BlockdevCreateOptionsVdi',
'vhdx': 'BlockdevCreateOptionsVhdx',
+ 'vitastor': 'BlockdevCreateOptionsVitastor',
'vmdk': 'BlockdevCreateOptionsVmdk',
'vpc': 'BlockdevCreateOptionsVpc'
} }
diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh
index 3d0d132344..65ee8c855e 100644
--- a/scripts/meson-buildoptions.sh
+++ b/scripts/meson-buildoptions.sh
@@ -177,6 +177,7 @@ meson_options_help() {
printf "%s\n" ' qga-vss build QGA VSS support (broken with MinGW)'
printf "%s\n" ' qpl Query Processing Library support'
printf "%s\n" ' rbd Ceph block device driver'
+ printf "%s\n" ' vitastor Vitastor block device driver'
printf "%s\n" ' rdma Enable RDMA-based migration'
printf "%s\n" ' replication replication support'
printf "%s\n" ' rust Rust support'
@@ -464,6 +465,8 @@ _meson_option_parse() {
--disable-qpl) printf "%s" -Dqpl=disabled ;;
--enable-rbd) printf "%s" -Drbd=enabled ;;
--disable-rbd) printf "%s" -Drbd=disabled ;;
+ --enable-vitastor) printf "%s" -Dvitastor=enabled ;;
+ --disable-vitastor) printf "%s" -Dvitastor=disabled ;;
--enable-rdma) printf "%s" -Drdma=enabled ;;
--disable-rdma) printf "%s" -Drdma=disabled ;;
--enable-relocatable) printf "%s" -Drelocatable=true ;;

View File

@@ -11,10 +11,7 @@ WORKDIR /root
RUN sed -i 's/enabled=0/enabled=1/' /etc/yum.repos.d/*.repo
RUN dnf -y install epel-release dnf-plugins-core
RUN dnf -y install https://vitastor.io/rpms/centos/10/vitastor-release-1.0-1.el10.noarch.rpm
RUN sed -i 's/metalink=/#metalink=/' /etc/yum.repos.d/epel.repo
RUN sed -i 's!#baseurl=.*epel/!baseurl=https://mirror.yandex.ru/epel/!' /etc/yum.repos.d/epel.repo
RUN dnf -y install gcc-c++ gperftools-devel fio nodejs rpm-build jerasure-devel isa-l-devel openssl-devel libisa-l_crypto-devel \
gf-complete-devel rdma-core-devel cmake libnl3-devel c-ares-devel
RUN dnf -y install gcc-c++ gperftools-devel fio nodejs rpm-build jerasure-devel isa-l-devel gf-complete-devel rdma-core-devel cmake libnl3-devel c-ares-devel
RUN dnf download --source fio
RUN rpm --nomd5 -i fio*.src.rpm
RUN cd ~/rpmbuild/SPECS && dnf builddep -y --spec fio.spec

View File

@@ -1,18 +1,17 @@
Name: vitastor
Version: 3.1.0
Version: 3.0.5
Release: 1%{?dist}
Summary: Vitastor, a fast software-defined clustered block storage
License: Vitastor Network Public License 1.1
URL: https://vitastor.io/
Source0: vitastor-3.1.0.el10.tar.gz
Source0: vitastor-3.0.5.el10.tar.gz
BuildRequires: gperftools-devel
BuildRequires: gcc-c++
BuildRequires: nodejs >= 10
BuildRequires: jerasure-devel
BuildRequires: isa-l-devel
BuildRequires: libisa-l_crypto-devel
BuildRequires: gf-complete-devel
BuildRequires: rdma-core-devel
BuildRequires: cmake
@@ -162,8 +161,6 @@ chown vitastor:vitastor /var/lib/vitastor
%_bindir/vitastor-nbd
%_bindir/vitastor-ublk
%_bindir/vitastor-nfs
%_sbindir/mount.vitastorfs
%_sbindir/mount.vitastorblk
%_bindir/vitastor-cli
%_bindir/vitastor-rm
%_bindir/vitastor-kv

View File

@@ -13,11 +13,9 @@ RUN rm -f /etc/yum.repos.d/CentOS-Media.repo
RUN sed -i 's/^mirrorlist=/#mirrorlist=/; s!#baseurl=http://mirror.centos.org/centos/\$releasever!baseurl=http://vault.centos.org/7.9.2009!' /etc/yum.repos.d/*.repo
RUN yum -y --enablerepo=extras install centos-release-scl epel-release yum-utils rpm-build
RUN perl -i -pe 's!mirrorlist=!#mirrorlist=!s; s!#\s*baseurl=http://mirror.centos.org!baseurl=http://vault.centos.org!' /etc/yum.repos.d/CentOS-SCLo-scl*.repo
RUN yum -y install https://vitastor.io/rpms/centos/7/vitastor-release-1.0-2.el7.noarch.rpm
RUN sed -i 's/metalink=/#metalink=/' /etc/yum.repos.d/epel.repo
RUN sed -i 's!#baseurl=.*epel/!baseurl=https://archives.fedoraproject.org/pub/archive/epel/!' /etc/yum.repos.d/epel.repo
RUN yum -y install https://vitastor.io/rpms/centos/7/vitastor-release-1.0-1.el7.noarch.rpm
RUN yum -y install devtoolset-9-gcc-c++ devtoolset-9-libatomic-devel gcc make cmake gperftools-devel \
fio rh-nodejs12 jerasure-devel libisa-l-devel openssl3-devel libisa-l_crypto-devel gf-complete-devel rdma-core-devel libnl3-devel c-ares-devel
fio rh-nodejs12 jerasure-devel libisa-l-devel gf-complete-devel rdma-core-devel libnl3-devel c-ares-devel
RUN yumdownloader --disablerepo=centos-sclo-rh --source fio
RUN rpm --nomd5 -i fio*.src.rpm
RUN rm -f /etc/yum.repos.d/CentOS-Media.repo

View File

@@ -1,25 +1,23 @@
Name: vitastor
Version: 3.1.0
Version: 3.0.5
Release: 1%{?dist}
Summary: Vitastor, a fast software-defined clustered block storage
License: Vitastor Network Public License 1.1
URL: https://vitastor.io/
Source0: vitastor-3.1.0.el7.tar.gz
Source0: vitastor-3.0.5.el7.tar.gz
BuildRequires: gperftools-devel
BuildRequires: devtoolset-9-gcc-c++
BuildRequires: rh-nodejs12
BuildRequires: rh-nodejs12-npm
BuildRequires: jerasure-devel
BuildRequires: openssl3-devel
BuildRequires: libisa-l-devel
BuildRequires: libisa-l_crypto-devel
BuildRequires: gf-complete-devel
BuildRequires: rdma-core-devel
BuildRequires: cmake3
BuildRequires: libnl3-devel
BuildRequires: c-ares-devel >= 1.16
BuildRequires: c-ares-devel
Requires: vitastor-osd = %{version}-%{release}
Requires: vitastor-mon = %{version}-%{release}
Requires: vitastor-client = %{version}-%{release}
@@ -107,7 +105,7 @@ Vitastor storage plugin for OpenNebula.
%build
. /opt/rh/devtoolset-9/enable
%cmake3 . -DOPENSSL_CRYPTO_LIBRARY=%{_libdir}/openssl3/libcrypto.so -DOPENSSL_SSL_LIBRARY=%{_libdir}/openssl3/libssl.so -DOPENSSL_INCLUDE_DIR=%{_includedir}/openssl3
%cmake3 .
%make_build
@@ -169,8 +167,6 @@ chown vitastor:vitastor /var/lib/vitastor
%_bindir/vitastor-nbd
%_bindir/vitastor-ublk
%_bindir/vitastor-nfs
%_sbindir/mount.vitastorfs
%_sbindir/mount.vitastorblk
%_bindir/vitastor-cli
%_bindir/vitastor-rm
%_bindir/vitastor-kv

View File

@@ -11,11 +11,9 @@ RUN rm -f /etc/yum.repos.d/CentOS-Media.repo
RUN sed -i 's/^mirrorlist=/#mirrorlist=/; s!#baseurl=http://mirror.centos.org/!baseurl=http://vault.centos.org/!' /etc/yum.repos.d/*.repo
RUN dnf -y install centos-release-advanced-virtualization epel-release dnf-plugins-core
RUN sed -i 's/^mirrorlist=/#mirrorlist=/; s!#baseurl=.*!baseurl=http://vault.centos.org/centos/8.4.2105/virt/$basearch/$avdir/!; s!^baseurl=.*Source/.*!baseurl=http://vault.centos.org/centos/8.4.2105/virt/Source/advanced-virtualization/!' /etc/yum.repos.d/CentOS-Advanced-Virtualization.repo
RUN yum -y install https://vitastor.io/rpms/centos/8/vitastor-release-1.0-2.el8.noarch.rpm
RUN sed -i 's/metalink=/#metalink=/' /etc/yum.repos.d/epel.repo
RUN sed -i 's!#baseurl=.*epel/!baseurl=https://mirror.yandex.ru/epel/!' /etc/yum.repos.d/epel.repo
RUN dnf -y install gcc-toolset-9 gcc-toolset-9-gcc-c++ gperftools-devel fio nodejs rpm-build jerasure-devel \
libisa-l-devel openssl3-devel libisa-l_crypto-devel gf-complete-devel libibverbs-devel libarchive cmake libnl3-devel c-ares-devel
RUN yum -y install https://vitastor.io/rpms/centos/8/vitastor-release-1.0-1.el8.noarch.rpm
RUN dnf -y install gcc-toolset-9 gcc-toolset-9-gcc-c++ gperftools-devel \
fio nodejs rpm-build jerasure-devel libisa-l-devel gf-complete-devel libibverbs-devel libarchive cmake libnl3-devel c-ares-devel
RUN dnf download --source fio
RUN rpm --nomd5 -i fio*.src.rpm
RUN cd ~/rpmbuild/SPECS && dnf builddep -y --enablerepo=powertools --spec fio.spec

View File

@@ -1,24 +1,22 @@
Name: vitastor
Version: 3.1.0
Version: 3.0.5
Release: 1%{?dist}
Summary: Vitastor, a fast software-defined clustered block storage
License: Vitastor Network Public License 1.1
URL: https://vitastor.io/
Source0: vitastor-3.1.0.el8.tar.gz
Source0: vitastor-3.0.5.el8.tar.gz
BuildRequires: gperftools-devel
BuildRequires: gcc-toolset-9-gcc-c++
BuildRequires: nodejs >= 10
BuildRequires: jerasure-devel
BuildRequires: openssl3-devel
BuildRequires: libisa-l-devel
BuildRequires: libisa-l_crypto-devel
BuildRequires: gf-complete-devel
BuildRequires: rdma-core-devel
BuildRequires: cmake
BuildRequires: libnl3-devel
BuildRequires: c-ares-devel >= 1.16
BuildRequires: c-ares-devel
Requires: vitastor-osd = %{version}-%{release}
Requires: vitastor-mon = %{version}-%{release}
Requires: vitastor-client = %{version}-%{release}
@@ -105,7 +103,7 @@ Vitastor storage plugin for OpenNebula.
%build
. /opt/rh/gcc-toolset-9/enable
%cmake . -DOPENSSL_CRYPTO_LIBRARY=%{_libdir}/openssl3/libcrypto.so -DOPENSSL_SSL_LIBRARY=%{_libdir}/openssl3/libssl.so -DOPENSSL_INCLUDE_DIR=%{_includedir}/openssl3
%cmake .
%make_build
@@ -166,8 +164,6 @@ chown vitastor:vitastor /var/lib/vitastor
%_bindir/vitastor-nbd
%_bindir/vitastor-ublk
%_bindir/vitastor-nfs
%_sbindir/mount.vitastorfs
%_sbindir/mount.vitastorblk
%_bindir/vitastor-cli
%_bindir/vitastor-rm
%_bindir/vitastor-kv

View File

@@ -10,10 +10,7 @@ WORKDIR /root
RUN sed -i 's/enabled=0/enabled=1/' /etc/yum.repos.d/*.repo
RUN dnf -y install epel-release dnf-plugins-core
RUN dnf -y install https://vitastor.io/rpms/centos/9/vitastor-release-1.0-1.el9.noarch.rpm
RUN sed -i 's/metalink=/#metalink=/' /etc/yum.repos.d/epel.repo
RUN sed -i 's!#baseurl=.*epel/!baseurl=https://mirror.yandex.ru/epel/!' /etc/yum.repos.d/epel.repo
RUN dnf -y install gcc-c++ gperftools-devel fio nodejs rpm-build jerasure-devel libisa-l-devel \
openssl-devel libisa-l_crypto-devel gf-complete-devel rdma-core-devel libarchive cmake libnl3-devel c-ares-devel
RUN dnf -y install gcc-c++ gperftools-devel fio nodejs rpm-build jerasure-devel libisa-l-devel gf-complete-devel rdma-core-devel libarchive cmake libnl3-devel c-ares-devel
RUN dnf download --source fio
RUN rpm --nomd5 -i fio*.src.rpm
RUN cd ~/rpmbuild/SPECS && dnf builddep -y --spec fio.spec

View File

@@ -1,24 +1,22 @@
Name: vitastor
Version: 3.1.0
Version: 3.0.5
Release: 1%{?dist}
Summary: Vitastor, a fast software-defined clustered block storage
License: Vitastor Network Public License 1.1
URL: https://vitastor.io/
Source0: vitastor-3.1.0.el9.tar.gz
Source0: vitastor-3.0.5.el9.tar.gz
BuildRequires: gperftools-devel
BuildRequires: gcc-c++
BuildRequires: nodejs >= 10
BuildRequires: jerasure-devel
BuildRequires: openssl-devel
BuildRequires: libisa-l-devel
BuildRequires: libisa-l_crypto-devel
BuildRequires: gf-complete-devel
BuildRequires: rdma-core-devel
BuildRequires: cmake
BuildRequires: libnl3-devel
BuildRequires: c-ares-devel >= 1.16
BuildRequires: c-ares-devel
Requires: vitastor-osd = %{version}-%{release}
Requires: vitastor-mon = %{version}-%{release}
Requires: vitastor-client = %{version}-%{release}
@@ -163,8 +161,6 @@ chown vitastor:vitastor /var/lib/vitastor
%_bindir/vitastor-nbd
%_bindir/vitastor-ublk
%_bindir/vitastor-nfs
%_sbindir/mount.vitastorfs
%_sbindir/mount.vitastorblk
%_bindir/vitastor-cli
%_bindir/vitastor-rm
%_bindir/vitastor-kv

View File

@@ -1,8 +1,9 @@
cmake_minimum_required(VERSION 2.8...3.30)
cmake_minimum_required(VERSION 2.8.12)
project(vitastor)
include(GNUInstallDirs)
include(CTest)
include(CheckIncludeFile)
find_package(PkgConfig)
@@ -11,7 +12,6 @@ set(WITH_QEMU false CACHE BOOL "Build QEMU driver inside Vitastor source tree")
set(WITH_FIO true CACHE BOOL "Build FIO driver")
set(QEMU_PLUGINDIR qemu CACHE STRING "QEMU plugin directory suffix (qemu-kvm on RHEL)")
set(WITH_ASAN false CACHE BOOL "Build with AddressSanitizer")
set(WITH_UBSAN false CACHE BOOL "Build with UBSanitizer")
set(WITH_SYSTEM_LIBURING false CACHE BOOL "Use system liburing")
if("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/local/?$")
if(EXISTS "/etc/debian_version")
@@ -21,17 +21,13 @@ if("${CMAKE_INSTALL_PREFIX}" MATCHES "^/usr/local/?$")
endif()
set(ENABLE_COVERAGE false CACHE BOOL "Enable code coverage")
add_definitions(-DVITASTOR_VERSION="3.1.0")
add_definitions(-DVITASTOR_VERSION="3.0.5")
add_definitions(-D_GNU_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Wall -Wno-sign-compare -Wno-comment -Wno-parentheses -Wno-pointer-arith -fdiagnostics-color=always -fno-omit-frame-pointer -fvisibility=hidden -I ${CMAKE_SOURCE_DIR}/src)
add_link_options(-fno-omit-frame-pointer)
if (${WITH_ASAN})
add_definitions(-fsanitize=address)
add_link_options(-fsanitize=address -fno-omit-frame-pointer)
endif (${WITH_ASAN})
if (${WITH_UBSAN})
add_definitions(-fsanitize=undefined -fno-sanitize=alignment)
add_link_options(-fsanitize=undefined -fno-sanitize=alignment)
endif (${WITH_UBSAN})
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fvisibility-inlines-hidden")
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -fvisibility-inlines-hidden")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -fvisibility-inlines-hidden")
@@ -55,13 +51,9 @@ string(REGEX REPLACE "([\\/\\-]D) *NDEBUG" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_F
string(REGEX REPLACE "([\\/\\-]D) *NDEBUG" "" CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}")
string(REGEX REPLACE "([\\/\\-]D) *NDEBUG" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
macro(install_symlink _filepath _sympath)
get_filename_component(_symname ${_sympath} NAME)
get_filename_component(_installdir ${_sympath} PATH)
install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink
${_filepath} ${CMAKE_CURRENT_BINARY_DIR}/${_symname}.link)")
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${_symname}.link DESTINATION ${_installdir} RENAME ${_symname})
install(CODE "file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/${_symname}.link)")
macro(install_symlink filepath sympath)
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${filepath} \$ENV{DESTDIR}${sympath})")
install(CODE "message(\"-- Created symlink: ${sympath} -> ${filepath}\")")
endmacro(install_symlink)
check_include_file("linux/nbd-netlink.h" HAVE_NBD_NETLINK_H)
@@ -78,20 +70,17 @@ pkg_check_modules(ISAL libisal)
if (ISAL_LIBRARIES)
add_definitions(-DWITH_ISAL)
endif (ISAL_LIBRARIES)
pkg_check_modules(ISAL_CRYPTO libisal_crypto)
if (ISAL_CRYPTO_LIBRARIES)
add_definitions(-DWITH_ISAL_CRYPTO)
endif (ISAL_CRYPTO_LIBRARIES)
pkg_check_modules(RDMACM librdmacm)
if (RDMACM_LIBRARIES)
add_definitions(-DWITH_RDMACM)
endif (RDMACM_LIBRARIES)
find_package(OpenSSL 3.0 REQUIRED)
add_definitions(-DWITH_OPENSSL)
include_directories(${OPENSSL_INCLUDE_DIR})
find_package(OpenSSL REQUIRED)
if (OPENSSL_FOUND)
add_definitions(-DWITH_OPENSSL)
endif (OPENSSL_FOUND)
pkg_check_modules(CARES REQUIRED libcares>=1.16)
pkg_check_modules(CARES REQUIRED libcares)
include_directories(${CARES_INCLUDE_DIRS})
if (${WITH_SYSTEM_LIBURING})
@@ -126,7 +115,6 @@ add_subdirectory(kv)
add_subdirectory(nfs)
add_subdirectory(osd)
add_subdirectory(test)
add_subdirectory(util)
### Install
@@ -134,8 +122,6 @@ install(TARGETS vitastor-osd vitastor-disk vitastor-nbd vitastor-ublk vitastor-n
install_symlink(vitastor-disk ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/vitastor-dump-journal)
install_symlink(vitastor-cli ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/vitastor-rm)
install_symlink(vitastor-cli ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/vita)
install_symlink(../${CMAKE_INSTALL_BINDIR}/vitastor-nfs ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_SBINDIR}/mount.vitastorfs)
install_symlink(../${CMAKE_INSTALL_BINDIR}/vitastor-nfs ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_SBINDIR}/mount.vitastorblk)
install(
TARGETS vitastor_client vitastor_kv
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}

View File

@@ -1,9 +1,10 @@
cmake_minimum_required(VERSION 2.8...3.30)
cmake_minimum_required(VERSION 2.8.12)
project(vitastor)
# libvitastor_blk.a
add_library(vitastor_blk STATIC
../util/allocator.cpp ../util/crc32c.c ../util/xxhash.c ../util/ringloop.cpp
multilist.cpp blockstore_heap.cpp blockstore_disk.cpp
blockstore.cpp blockstore_impl.cpp blockstore_init.cpp blockstore_open.cpp
blockstore_flush.cpp blockstore_read.cpp blockstore_stable.cpp blockstore_sync.cpp blockstore_write.cpp
@@ -11,8 +12,10 @@ add_library(vitastor_blk STATIC
)
target_compile_options(vitastor_blk PUBLIC -fPIC)
target_link_libraries(vitastor_blk
util
crc32c
${LIBURING_LIBRARIES}
${ISAL_LIBRARIES}
# for timerfd_manager
vitastor_common
)
set_target_properties(vitastor_blk PROPERTIES VERSION ${VITASTOR_VERSION} SOVERSION 0)
@@ -20,9 +23,9 @@ if (${WITH_FIO})
# libfio_vitastor_blk.so
add_library(fio_vitastor_blk SHARED
fio_engine.cpp
../../json11/json11.cpp
)
target_link_libraries(fio_vitastor_blk
vitastor_blk
util
)
endif (${WITH_FIO})

View File

@@ -77,11 +77,8 @@ Output:
- retval = number of bytes actually read/written or negative error number
-EINVAL = invalid input parameters
-ENOENT = requested object/version does not exist for reads
-EEXIST = requested version to write already exists
-ENOSPC = no space left in the store for writes
-EAGAIN = temporary failure, operation should be retried (notably, writes may be retried
after stabilizing some journaled entries and freeing up journal space)
-EDOM = checksum error
-EDOM = checksum error.
- version = the version actually read or written
## BS_OP_DELETE
@@ -93,7 +90,7 @@ Input:
- version = requested version. Treated the same as with BS_OP_WRITE
Output:
- retval = 0 or negative error number (-EINVAL, -EAGAIN)
- retval = 0 or negative error number (-EINVAL)
- version = the version actually written (delete is initially written as an object version)
## BS_OP_SYNC
@@ -102,7 +99,7 @@ Make sure all previously issued modifications reach physical media.
Input: Nothing except opcode
Output:
- retval = 0 or negative error number (-EINVAL, -EAGAIN)
- retval = 0 or negative error number (-EINVAL)
## BS_OP_STABLE / BS_OP_ROLLBACK
@@ -132,9 +129,7 @@ Input:
- max_oid = max inode/stripe or 0 to list all objects
Output:
- retval = total obj_ver_id count or negative error number
-ENOMEM = could not allocate memory for the listing
-EAGAIN = pool sharding settings are incorrect, please reshard and retry
- retval = total obj_ver_id count
- version = stable obj_ver_id count
- buf = obj_ver_id array allocated by the blockstore. Stable versions come first.
You must free it yourself after usage with free().
@@ -171,7 +166,7 @@ struct __attribute__ ((visibility("default"))) blockstore_op_t
};
uint8_t *buf = NULL;
uint8_t *bitmap = NULL;
int64_t retval = 0;
int retval = 0;
uint8_t private_data[BS_OP_PRIVATE_DATA_SIZE];
};
@@ -233,9 +228,4 @@ public:
virtual uint64_t get_journal_size() = 0;
virtual uint32_t get_bitmap_granularity() = 0;
virtual uint64_t get_live_entries() = 0;
virtual uint64_t get_live_memory() = 0;
virtual uint64_t get_garbage_entries() = 0;
virtual uint64_t get_garbage_memory() = 0;
};

View File

@@ -12,9 +12,23 @@
#include "blockstore_disk.h"
#include "blockstore_heap.h"
#include "str_util.h"
#include "disk_util.h"
#include "allocator.h"
static uint32_t is_power_of_two(uint64_t value)
{
uint32_t l = 0;
while (value > 1)
{
if (value & 1)
{
return 64;
}
value = value >> 1;
l++;
}
return l;
}
void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config)
{
// Parse
@@ -84,9 +98,6 @@ void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config
csum_block_size = parse_size(config["csum_block_size"]);
discard_on_start = config.find("discard_on_start") != config.end() &&
(config["discard_on_start"] == "true" || config["discard_on_start"] == "1" || config["discard_on_start"] == "yes");
gc_on_start = config.find("gc_on_start") == config.end() ||
(config["gc_on_start"] == "true" || config["gc_on_start"] == "1" || config["gc_on_start"] == "yes");
skip_double_claim = (config["skip_double_claim"] == "true" || config["skip_double_claim"] == "1" || config["skip_double_claim"] == "yes");
min_discard_size = parse_size(config["min_discard_size"]);
if (!min_discard_size)
min_discard_size = 1024*1024;
@@ -112,7 +123,7 @@ void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config
{
data_block_size = (1 << DEFAULT_DATA_BLOCK_ORDER);
}
if ((data_block_size & (data_block_size-1)) || data_block_size < MIN_DATA_BLOCK_SIZE || data_block_size >= MAX_DATA_BLOCK_SIZE)
if (is_power_of_two(data_block_size) >= 64 || data_block_size < MIN_DATA_BLOCK_SIZE || data_block_size >= MAX_DATA_BLOCK_SIZE)
{
throw std::runtime_error("Bad block size");
}
@@ -210,32 +221,6 @@ void blockstore_disk_t::parse_config(std::map<std::string, std::string> & config
}
}
// A metadata entry must fit into one metadata block; a large block_size with a small
// csum_block_size otherwise inflates the entry past it and makes the callers divide by zero.
static uint64_t entries_per_meta_block(uint64_t entry_size, uint64_t meta_block_size,
uint64_t data_block_size, uint32_t bitmap_granularity, uint32_t csum_block_size)
{
if (entry_size && entry_size <= meta_block_size)
{
return meta_block_size / entry_size;
}
std::string msg = "Metadata entry size ("+std::to_string(entry_size)+" bytes) exceeds meta_block_size ("+
std::to_string(meta_block_size)+" bytes) with block_size="+std::to_string(data_block_size)+
", bitmap_granularity="+std::to_string(bitmap_granularity);
if (csum_block_size)
{
msg += ", csum_block_size="+std::to_string(csum_block_size)+" ("+
std::to_string(data_block_size/csum_block_size)+" checksums per block)";
}
msg += ". Decrease block_size";
if (csum_block_size)
{
msg += ", increase csum_block_size";
}
msg += " or increase meta_block_size";
throw std::runtime_error(msg);
}
void blockstore_disk_t::calc_lengths(bool skip_meta_check)
{
// data
@@ -285,20 +270,17 @@ void blockstore_disk_t::calc_lengths(bool skip_meta_check)
clean_entry_bitmap_size = (data_block_size / bitmap_granularity + 7) / 8;
clean_dyn_size = clean_entry_bitmap_size*2 + (csum_block_size
? data_block_size/csum_block_size*(data_csum_type & 0xFF) : 0);
uint64_t entries_per_block = 0;
recalc:
if (meta_format == BLOCKSTORE_META_FORMAT_HEAP)
{
entries_per_block = entries_per_meta_block(sizeof(heap_big_write_t) + clean_dyn_size,
meta_block_size, data_block_size, bitmap_granularity, csum_block_size);
uint32_t entries_per_block = meta_block_size / (sizeof(heap_big_write_t) + clean_dyn_size);
min_meta_len = (block_count+entries_per_block-1) / entries_per_block * meta_block_size;
}
else if (meta_format == BLOCKSTORE_META_FORMAT_V1)
{
clean_entry_size = 24 /*sizeof(clean_disk_entry)*/ + 2*clean_entry_bitmap_size;
entries_per_block = entries_per_meta_block(clean_entry_size, meta_block_size,
data_block_size, bitmap_granularity, 0);
min_meta_len = (1 + (block_count - 1 + entries_per_block) / entries_per_block) * meta_block_size;
min_meta_len = (1 + (block_count - 1 + meta_block_size / clean_entry_size)
/ (meta_block_size / clean_entry_size)) * meta_block_size;
if (!skip_meta_check && meta_area_size < min_meta_len)
{
too_small:
@@ -310,9 +292,7 @@ too_small:
{
meta_format = BLOCKSTORE_META_FORMAT_V2;
clean_entry_size = 24 /*sizeof(clean_disk_entry)*/ + clean_dyn_size + 4 /*entry_csum*/;
entries_per_block = entries_per_meta_block(clean_entry_size, meta_block_size,
data_block_size, bitmap_granularity, csum_block_size);
min_meta_len = (1 + (block_count - 1 + entries_per_block) / entries_per_block) * meta_block_size;
min_meta_len = (1 + (block_count - 1 + meta_block_size / clean_entry_size) / (meta_block_size / clean_entry_size)) * meta_block_size;
if (!skip_meta_check && meta_area_size < min_meta_len)
{
if (!data_csum_type)
@@ -355,6 +335,41 @@ void blockstore_disk_t::check_lengths()
}
}
// FIXME: Move to utils
static void check_size(int fd, uint64_t *size, uint64_t *sectsize, std::string name)
{
int sect;
struct stat st;
if (fstat(fd, &st) < 0)
{
throw std::runtime_error("Failed to stat "+name);
}
if (S_ISREG(st.st_mode))
{
*size = st.st_size;
if (sectsize)
{
*sectsize = st.st_blksize;
}
}
else if (S_ISBLK(st.st_mode))
{
if (ioctl(fd, BLKGETSIZE64, size) < 0 ||
ioctl(fd, BLKSSZGET, &sect) < 0)
{
throw std::runtime_error("Failed to get "+name+" size or block size: "+strerror(errno));
}
if (sectsize)
{
*sectsize = sect;
}
}
else
{
throw std::runtime_error(name+" is neither a file nor a block device");
}
}
static int bs_openmode(const std::string & mode)
{
if (mode == "directsync")
@@ -503,7 +518,7 @@ void blockstore_disk_t::close_all()
// Sadly DISCARD only works through ioctl(), but it seems to always block the device queue,
// so it's not a big deal that we can only run it synchronously.
int blockstore_disk_t::trim_data(std::function<bool(uint64_t)> is_used)
int blockstore_disk_t::trim_data(std::function<bool(uint64_t)> is_free)
{
if (mock_mode)
{
@@ -514,7 +529,7 @@ int blockstore_disk_t::trim_data(std::function<bool(uint64_t)> is_used)
uint64_t discarded = 0;
for (; i <= block_count; i++)
{
if (i >= block_count || is_used(i))
if (i >= block_count || is_free(i))
{
if (i > j && (i-j)*data_block_size >= min_discard_size)
{
@@ -527,21 +542,17 @@ int blockstore_disk_t::trim_data(std::function<bool(uint64_t)> is_used)
if (range[0] % discard_granularity)
range[0] = range[0] + discard_granularity - (range[0] % discard_granularity);
if (range[0] >= range[1])
range[1] = 0;
else
range[1] -= range[0];
continue;
range[1] -= range[0];
}
if (range[1] > 0)
r = ioctl(data_fd, BLKDISCARD, &range);
if (r != 0)
{
r = ioctl(data_fd, BLKDISCARD, &range);
if (r != 0)
{
fprintf(stderr, "Failed to execute BLKDISCARD %ju+%ju on %s: %s (code %d)\n",
range[0], range[1], data_device.c_str(), strerror(-r), r);
return -errno;
}
discarded += range[1];
fprintf(stderr, "Failed to execute BLKDISCARD %ju+%ju on %s: %s (code %d)\n",
range[0], range[1], data_device.c_str(), strerror(-r), r);
return -errno;
}
discarded += range[1];
}
j = i+1;
}

View File

@@ -58,10 +58,6 @@ struct blockstore_disk_t
bool inmemory_journal = true;
// Data discard granularity and minimum size (for the sake of performance)
bool discard_on_start = false;
// GC on start (new store)
bool gc_on_start = true;
// Skip double claim conflicts on start (new store, temporary until the bug is found)
bool skip_double_claim = false;
uint64_t min_discard_size = 1024*1024;
uint64_t discard_granularity = 0;
@@ -84,7 +80,7 @@ struct blockstore_disk_t
void calc_lengths(bool skip_meta_check = false);
void check_lengths();
void close_all();
int trim_data(std::function<bool(uint64_t)> is_used);
int trim_data(std::function<bool(uint64_t)> is_free);
inline uint64_t dirty_dyn_size(uint64_t offset, uint64_t len)
{

View File

@@ -174,18 +174,14 @@ bool journal_flusher_co::loop()
else if (wait_state == 19) goto resume_19;
else if (wait_state == 20) goto resume_20;
else if (wait_state == 21) goto resume_21;
else if (wait_state == 22) goto resume_22;
else if (wait_state == 23) goto resume_23;
else if (wait_state == 24) goto resume_24;
else if (wait_state == 25) goto resume_25;
resume_0:
wait_state = 0;
wait_count = 0;
cur_oid = {};
res = bs->heap->get_next_compact(cur_oid);
// Advance fsynced_lsn every <journal_trim_interval> intent writes
if ((bs->intent_write_counter >= bs->journal_trim_interval) && co_id == 0)
{
// Advance fsynced_lsn every <journal_trim_interval> intent writes
bs->intent_write_counter = 0;
resume_17:
resume_18:
@@ -200,7 +196,6 @@ resume_21:
if (res == ENOENT && flusher->force_start > 0 && co_id == 0 &&
(!bs->dsk.disable_journal_fsync || !bs->dsk.disable_meta_fsync || !bs->dsk.disable_data_fsync))
{
// When under pressure, do an additional fsync to force entries to be marked compactable
flusher->active_flushers++;
resume_14:
resume_15:
@@ -264,9 +259,11 @@ resume_1:
if (wr->type() == BS_HEAP_SMALL_WRITE ||
wr->type() == BS_HEAP_INTENT_WRITE && bs->dsk.csum_block_size > bs->dsk.bitmap_granularity)
{
bs->prepare_read(read_vec, cur_obj, wr, 0, bs->dsk.data_block_size,
auto res = bs->prepare_read(read_vec, cur_obj, wr, 0, bs->dsk.data_block_size,
wr->type() == BS_HEAP_INTENT_WRITE && bs->dsk.csum_block_size > bs->dsk.bitmap_granularity && !bs->perfect_csum_update
? COPY_BUF_SKIP_CSUM : 0);
if (res > 0)
copy_count++;
}
});
if (!compact_info.compact_lsn)
@@ -276,53 +273,30 @@ resume_1:
bs->heap->unlock_entry(cur_oid);
goto resume_0;
}
mem_or(new_bmp, compact_info.clean_wr->get_int_bitmap(bs->heap), bs->dsk.clean_entry_bitmap_size);
if (!bitmap_copied)
{
memcpy(new_ext_bmp, compact_info.clean_wr->get_ext_bitmap(bs->heap), bs->dsk.clean_entry_bitmap_size);
bitmap_copied = true;
}
if (bs->dsk.csum_block_size && bs->dsk.csum_block_size <= bs->dsk.bitmap_granularity)
{
memcpy(new_csums, compact_info.clean_wr->get_checksums(bs->heap), bs->dsk.data_block_size/bs->dsk.csum_block_size * (bs->dsk.data_csum_type & 0xFF));
for (size_t i = csum_copy.size(); i > 0; i--)
{
auto wr = csum_copy[i-1];
memcpy(new_csums + wr->small().offset/bs->dsk.csum_block_size*(bs->dsk.data_csum_type & 0xFF),
wr->get_checksums(bs->heap), wr->small().len/bs->dsk.csum_block_size*(bs->dsk.data_csum_type & 0xFF));
}
csum_copy.clear();
}
clean_loc = compact_info.clean_wr->big_location(bs->heap);
flusher->active_flushers++;
for (i = 0; i < read_vec.size(); i++)
if (bs->log_level > 10)
{
if ((read_vec[i].copy_flags & COPY_BUF_JOURNAL) &&
!(read_vec[i].copy_flags & COPY_BUF_COALESCED))
{
copy_count++;
}
}
if (copy_count > 0 && !bs->dsk.disable_data_fsync)
{
init_fsync_data();
}
if (compact_info.do_delete)
{
if (bs->log_level > 10)
{
printf("Compacting %jx:%jx up to l%ju (delete)\n", cur_oid.inode, cur_oid.stripe, compact_info.compact_lsn);
}
clean_loc = UINT64_MAX;
}
else
{
if (bs->log_level > 10)
{
printf("Compacting %jx:%jx v%ju..v%ju / l%ju..l%ju (%d writes)\n", cur_oid.inode, cur_oid.stripe,
compact_info.clean_wr->version, compact_info.compact_version,
compact_info.clean_wr->lsn, compact_info.compact_lsn, copy_count);
}
mem_or(new_bmp, compact_info.clean_wr->get_int_bitmap(bs->heap), bs->dsk.clean_entry_bitmap_size);
if (!bitmap_copied)
{
memcpy(new_ext_bmp, compact_info.clean_wr->get_ext_bitmap(bs->heap), bs->dsk.clean_entry_bitmap_size);
bitmap_copied = true;
}
if (bs->dsk.csum_block_size && bs->dsk.csum_block_size <= bs->dsk.bitmap_granularity)
{
memcpy(new_csums, compact_info.clean_wr->get_checksums(bs->heap), bs->dsk.data_block_size/bs->dsk.csum_block_size * (bs->dsk.data_csum_type & 0xFF));
for (size_t i = csum_copy.size(); i > 0; i--)
{
auto wr = csum_copy[i-1];
memcpy(new_csums + wr->small().offset/bs->dsk.csum_block_size*(bs->dsk.data_csum_type & 0xFF),
wr->get_checksums(bs->heap), wr->small().len/bs->dsk.csum_block_size*(bs->dsk.data_csum_type & 0xFF));
}
csum_copy.clear();
}
clean_loc = compact_info.clean_wr->big_location(bs->heap);
printf("Compacting %jx:%jx v%ju..v%ju / l%ju..l%ju (%d writes)\n", cur_oid.inode, cur_oid.stripe,
compact_info.clean_wr->version, compact_info.compact_version,
compact_info.clean_wr->lsn, compact_info.compact_lsn, copy_count);
}
overwrite_start = overwrite_end = 0;
if (read_vec.size() > 0)
@@ -362,13 +336,6 @@ resume_3:
if (res == ENOENT || res == EDOM)
{
// Abort compaction
abort_compact:
if (copy_count > 0 && !bs->dsk.disable_data_fsync)
{
cur_sync->member_count--;
if (cur_sync->member_count > 0)
bs->ringloop->wakeup();
}
flusher->flushing.erase(cur_oid);
bs->heap->unlock_entry(cur_oid);
flusher->active_flushers--;
@@ -382,7 +349,10 @@ resume_4:
if (res == ENOENT)
{
// Abort compaction
goto abort_compact;
flusher->flushing.erase(cur_oid);
bs->heap->unlock_entry(cur_oid);
flusher->active_flushers--;
goto resume_0;
}
if (res == EAGAIN)
{
@@ -411,14 +381,14 @@ resume_9:
for (i = 0; i < read_vec.size(); i++)
{
if ((read_vec[i].copy_flags & COPY_BUF_JOURNAL) &&
!(read_vec[i].copy_flags & COPY_BUF_COALESCED))
!(read_vec[i].copy_flags & COPY_BUF_COALESCED) ||
(read_vec[i].copy_flags & COPY_BUF_PADDED)) // FIXME Shit, simplify these flags
{
assert(read_vec[i].buf);
await_sqe(10);
data->iov = (struct iovec){ read_vec[i].buf + (read_vec[i].copy_flags & COPY_BUF_PADDED
? read_vec[i].offset - read_vec[i].disk_offset : 0), (size_t)read_vec[i].len };
data->callback = simple_callback_w;
assert(clean_loc + read_vec[i].offset + data->iov.iov_len <= bs->dsk.block_count*bs->dsk.data_block_size);
io_uring_prep_writev(sqe, bs->dsk.data_fd, &data->iov, 1, bs->dsk.data_offset + clean_loc + read_vec[i].offset);
wait_count++;
}
@@ -429,17 +399,6 @@ resume_11:
wait_state = 11;
return false;
}
if (copy_count > 0 && !bs->dsk.disable_data_fsync)
{
resume_22:
resume_23:
resume_24:
resume_25:
if (!fsync_data(22))
{
return false;
}
}
// Lock is only needed to prevent freeing the big_write because we overwrite it...
bs->heap->unlock_entry(cur_oid);
// Mark the object compacted, but don't free and remove small_writes
@@ -449,14 +408,12 @@ resume_25:
if (!cur_obj)
{
// Abort compaction
flusher->active_flushers--;
flusher->flushing.erase(cur_oid);
goto resume_0;
}
if (!calc_block_checksums())
{
// Abort compaction
flusher->active_flushers--;
flusher->flushing.erase(cur_oid);
goto resume_0;
}
@@ -465,7 +422,6 @@ resume_25:
if (res == EBUSY)
{
// Abort compaction, object is already overwritten by something else
flusher->active_flushers--;
flusher->flushing.erase(cur_oid);
goto resume_0;
}
@@ -630,13 +586,13 @@ int journal_flusher_co::check_and_punch_checksums()
bs->heap->calc_block_checksums((uint32_t*)(new_csums+csum_off), vec.buf, punch_bmp, vec.offset, vec.offset+vec.len, true, NULL);
}
}
// Modified, we should punch_holes and then write the block to disk
// Modified, we should add_punch_holes and then write the block to disk
return EBUSY;
}
bool journal_flusher_co::calc_block_checksums()
{
if (bs->dsk.csum_block_size <= bs->dsk.bitmap_granularity || compact_info.do_delete)
if (bs->dsk.csum_block_size <= bs->dsk.bitmap_granularity)
{
return true;
}
@@ -743,67 +699,6 @@ resume_1:
return true;
}
void journal_flusher_co::init_fsync_data()
{
cur_sync = flusher->data_syncs.begin();
if (cur_sync == flusher->data_syncs.end() || cur_sync->ready_count > 0)
{
cur_sync = flusher->data_syncs.emplace(cur_sync);
}
cur_sync->member_count++;
}
bool journal_flusher_co::fsync_data(int wait_base)
{
if (wait_state == wait_base)
goto resume_0;
else if (wait_state == wait_base+1)
goto resume_1;
else if (wait_state == wait_base+2)
goto resume_2;
else if (wait_state == wait_base+3)
goto resume_3;
cur_sync->ready_count++;
resume_0:
if (cur_sync->ready_count < cur_sync->member_count)
{
wait_state = wait_base;
return false;
}
if (!cur_sync->sent)
{
// Sync batch is ready. Do it.
await_sqe(1);
data->iov = { 0 };
data->callback = simple_callback_w;
io_uring_prep_fsync(sqe, bs->dsk.data_fd, IORING_FSYNC_DATASYNC);
cur_sync->sent = true;
wait_count++;
resume_2:
if (wait_count > 0)
{
wait_state = wait_base+2;
return false;
}
cur_sync->done = true;
// Wake up other flushers
bs->ringloop->wakeup();
}
resume_3:
if (!cur_sync->done)
{
wait_state = wait_base+3;
return false;
}
cur_sync->done_count++;
if (cur_sync->done_count >= cur_sync->member_count)
{
flusher->data_syncs.erase(cur_sync);
cur_sync = flusher->data_syncs.end();
}
return true;
}
bool journal_flusher_co::fsync_meta(int wait_base)
{
if (wait_state == wait_base) goto resume_0;

View File

@@ -25,15 +25,6 @@ struct flusher_meta_write_t
std::map<uint64_t, meta_sector_t>::iterator it;
};
struct flusher_data_sync_t
{
int member_count = 0;
int ready_count = 0;
int done_count = 0;
bool sent = false;
bool done = false;
};
class journal_flusher_t;
// Journal flusher coroutine
@@ -67,7 +58,6 @@ class journal_flusher_co
int i, res;
bool read_to_fill_incomplete;
int copy_count;
std::list<flusher_data_sync_t>::iterator cur_sync;
friend class journal_flusher_t;
@@ -78,8 +68,6 @@ class journal_flusher_co
bool calc_block_checksums();
bool write_meta_block(int wait_base);
bool read_buffered(int wait_base);
void init_fsync_data();
bool fsync_data(int wait_base);
bool fsync_meta(int wait_base);
bool fsync_buffer(int wait_base);
bool trim_lsn(int wait_base);
@@ -100,7 +88,6 @@ class journal_flusher_t
robin_hood::unordered_flat_set<object_id> flushing;
int active_flushers = 0;
std::list<flusher_data_sync_t> data_syncs;
int wanting_meta_fsync = 0;
bool fsyncing_meta = false;
int syncing_buffer = 0;

File diff suppressed because it is too large Load Diff

View File

@@ -57,11 +57,11 @@ struct __attribute__((__packed__)) heap_entry_t
inline heap_small_write_t& small() { return *(heap_small_write_t*)this; }
inline heap_big_write_t& big() { return *(heap_big_write_t*)this; }
inline heap_big_intent_t& big_intent() { return *(heap_big_intent_t*)this; }
bool is_garbage() const;
bool is_garbage();
void set_garbage();
bool is_overwrite() const;
bool is_compactable() const;
bool is_before(const heap_entry_t *other) const;
bool is_overwrite();
bool is_compactable();
bool is_before(heap_entry_t *other);
uint32_t get_size(blockstore_heap_t *heap);
uint8_t *get_ext_bitmap(blockstore_heap_t *heap);
uint8_t *get_int_bitmap(blockstore_heap_t *heap);
@@ -118,13 +118,10 @@ struct heap_object_mvcc_t
struct heap_block_info_t
{
struct __attribute__((__packed__))
{
uint32_t used_space = 0;
uint32_t garbage_space = 0;
};
uint32_t used_space = 0;
uint64_t mod_lsn = 0, mod_lsn_to = 0; // only 1 block write of LSN sequence is allowed at a moment
bool is_writing = false;
bool is_writing: 1;
bool has_garbage: 1;
std::vector<heap_list_item_t*> entries;
};
@@ -159,16 +156,6 @@ struct heap_li_equal
}
};
struct heap_recheck_state_t
{
heap_entry_t *obj = NULL;
heap_entry_t *next_wr = NULL;
size_t total_reads = 0;
size_t sent_reads = 0;
size_t checked_reads = 0;
heap_entry_t *bad_wr = NULL;
};
using i64hash_t = robin_hood::hash<uint64_t>;
using heap_inode_map_t = robin_hood::unordered_flat_set<heap_list_item_t*, heap_li_hash, heap_li_equal, 88>;
using heap_block_index_t = robin_hood::unordered_flat_map<uint64_t,
@@ -198,11 +185,6 @@ class blockstore_heap_t
uint64_t buffer_area_used_space = 0;
uint64_t data_used_space = 0;
uint64_t live_entries = 0;
uint64_t live_memory = 0;
uint64_t garbage_entries = 0;
uint64_t garbage_memory = 0;
uint64_t next_lsn = 0;
uint32_t last_allocated_block = UINT32_MAX;
heap_mvcc_map_t object_mvcc;
@@ -219,35 +201,25 @@ class blockstore_heap_t
bool marked_used_blocks = false;
bool recheck_queue_filled = false;
std::vector<heap_list_item_t*> postponed_items;
std::vector<heap_list_item_t*> init_erase_items;
std::vector<heap_list_item_t*> loaded_list_items;
std::set<uint32_t> recheck_modified_blocks;
std::deque<heap_entry_t*> recheck_queue;
std::map<heap_entry_t*, heap_recheck_state_t> recheck_states;
size_t recheck_pending_reads = 0;
int recheck_in_progress = 0;
bool in_recheck = false;
std::function<void(bool is_data, uint64_t offset, uint64_t len, std::function<void(uint8_t*)>)> recheck_cb;
std::function<void(bool is_data, uint64_t offset, uint64_t len, uint8_t* buf, std::function<void()>)> recheck_cb;
int recheck_queue_depth = 0;
uint64_t get_pg_id(inode_t inode, uint64_t stripe);
bool validate_object(heap_entry_t *obj);
void fill_recheck_queue();
void recheck_drop_entries(heap_entry_t *obj, heap_entry_t *bad_wr);
void recheck_start_reads(heap_recheck_state_t *st);
int mark_used_blocks();
void init_free_bad_entry(heap_entry_t *wr);
void init_erase_bad_entry(heap_list_item_t *li);
bool init_erase_double_claim(heap_list_item_t *prev_li, heap_list_item_t *cur_li);
void recheck_full_gc();
void recheck_buffer(heap_entry_t *cwr, uint8_t *buf);
void defragment_block(uint32_t block_num);
void reshard_add(heap_reshard_state_t *st, heap_list_item_t *li);
void gc_block(heap_block_info_t & inf);
int allocate_entry(uint32_t entry_size, uint32_t *block_num, bool allow_last_free);
void insert_list_items(heap_list_item_t** v, size_t count, bool postpone);
void unlink_list_item(heap_list_item_t *li);
void insert_list_item(heap_list_item_t *li);
int add_entry(uint32_t wr_size, uint32_t *modified_block, bool allow_last_free,
bool explicit_complete, std::function<void(heap_entry_t *wr)> fill_entry);
int add_simple(heap_entry_t *obj, uint64_t version, uint32_t *modified_block, uint32_t entry_type);
@@ -274,7 +246,7 @@ public:
// before finishing initialization if not R/O
std::vector<uint32_t> get_recheck_modified_blocks();
// recheck small write data after reading the database from disk
bool recheck_small_writes(std::function<void(bool is_data, uint64_t offset, uint64_t len, std::function<void(uint8_t* buf)>)> read_buffer, int queue_depth);
bool recheck_small_writes(std::function<void(bool is_data, uint64_t offset, uint64_t len, uint8_t* buf, std::function<void()>)> read_buffer, int queue_depth);
int finish_recheck();
// reshard database according to the pool's PG count
void* reshard_start(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size, uint64_t chunk_limit);
@@ -362,7 +334,7 @@ public:
// get metadata block data buffer and used space
void get_meta_block(uint32_t block_num, uint8_t *buffer);
void fill_block_empty_space(uint8_t *buffer, uint64_t pos);
void fill_block_empty_space(uint8_t *buffer, uint32_t pos);
uint32_t get_meta_block_used_space(uint32_t block_num);
// get space usage statistics
@@ -374,10 +346,6 @@ public:
uint32_t get_compact_queue_size();
uint32_t get_to_compact_count();
uint64_t get_compacted_count();
uint64_t get_live_entries();
uint64_t get_live_memory();
uint64_t get_garbage_entries();
uint64_t get_garbage_memory();
uint64_t entry_pos(uint32_t block_num, uint32_t offset);
heap_entry_t *entry_from_pos(uint64_t entry_pos, bool allow_unallocated = false);

View File

@@ -101,7 +101,6 @@ void blockstore_impl_t::loop()
unsigned initial_ring_space = ringloop->space_left();
int op_idx = 0, new_idx = 0;
bool has_unfinished_writes = false;
bool has_unfinished_sync = false;
for (; op_idx < submit_queue.size(); op_idx++, new_idx++)
{
auto op = submit_queue[op_idx];
@@ -139,13 +138,7 @@ void blockstore_impl_t::loop()
else if (op->opcode == BS_OP_SYNC)
{
// syncs only completed writes, so doesn't have to be blocked by anything
if (!has_unfinished_sync)
{
wr_st = continue_sync(op);
has_unfinished_sync = (wr_st != 2);
}
else
wr_st = 0;
wr_st = continue_sync(op);
}
else if (op->opcode == BS_OP_STABLE || op->opcode == BS_OP_ROLLBACK)
{
@@ -161,7 +154,9 @@ void blockstore_impl_t::loop()
wr_st = 2;
}
else
{
wr_st = 0;
}
}
if (wr_st == 2)
{
@@ -345,7 +340,7 @@ void blockstore_impl_t::process_list(blockstore_op_t *op)
std::sort(result, result + stable_count);
if (stable_count > op->list_stable_limit)
{
memmove(result + op->list_stable_limit, result + stable_count, unstable_count*sizeof(obj_ver_id));
memmove(result + op->list_stable_limit, result + stable_count, unstable_count);
stable_count = op->list_stable_limit;
}
}

View File

@@ -9,9 +9,12 @@
#include "ondisk_formats.h"
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <linux/fs.h>
#include <vector>
#include <list>
@@ -114,12 +117,9 @@ public:
journal_flusher_t *flusher;
int write_iodepth = 0;
int inflight_big = 0;
int intent_write_counter = 0;
uint64_t data_fsync_next = 0;
uint64_t data_fsync_cur = 0;
uint64_t data_fsync_sent = 0;
uint64_t data_fsync_done = 0;
std::deque<bool> data_fsyncs;
bool fsyncing_data = false;
bool live = false, queue_stall = false;
ring_loop_i *ringloop = NULL;
@@ -229,9 +229,4 @@ public:
uint64_t get_free_block_count();
inline uint32_t get_bitmap_granularity() { return dsk.bitmap_granularity; }
inline uint64_t get_journal_size() { return dsk.journal_len; }
inline uint64_t get_live_entries() { return heap->get_live_entries(); }
inline uint64_t get_live_memory() { return heap->get_live_memory(); }
inline uint64_t get_garbage_entries() { return heap->get_garbage_entries(); }
inline uint64_t get_garbage_memory() { return heap->get_garbage_memory(); }
};

View File

@@ -10,6 +10,7 @@
#define INIT_META_EMPTY 0
#define INIT_META_READING 1
#define INIT_META_READ_DONE 2
#define INIT_META_WRITING 3
#define GET_SQE() \
sqe = bs->get_sqe();\
@@ -17,34 +18,19 @@
throw std::runtime_error("io_uring is full during initialization");\
data = ((ring_data_t*)sqe->user_data)
struct blockstore_recheck_io_t
{
bool is_data;
uint64_t offset;
uint64_t len;
uint8_t *buf;
std::function<void(uint8_t *buf)> cb;
blockstore_recheck_io_t(bool is_data, uint64_t offset, uint64_t len, std::function<void(uint8_t *buf)>&& cb):
is_data(is_data), offset(offset), len(len), buf(NULL), cb(std::move(cb))
{
}
};
blockstore_init_meta::blockstore_init_meta(blockstore_impl_t *bs)
{
this->bs = bs;
}
void blockstore_init_meta::handle_event(ring_data_t *data, int buf_num, const char *op)
void blockstore_init_meta::handle_event(ring_data_t *data, int buf_num)
{
if (data->res != data->iov.iov_len)
if (data->res < 0)
{
throw std::runtime_error(strprintf(
"%s failed at offset %ju: got %s (code %d), but expected %zu",
op, (buf_num >= 0 ? bufs[buf_num].offset : last_read_offset), strerror(-data->res),
data->res, data->iov.iov_len
));
throw std::runtime_error(
std::string("read metadata failed at offset ") + std::to_string(buf_num >= 0 ? bufs[buf_num].offset : last_read_offset) +
std::string(": ") + strerror(-data->res)
);
}
if (buf_num >= 0)
{
@@ -74,7 +60,7 @@ int blockstore_init_meta::loop()
GET_SQE();
last_read_offset = 0;
data->iov = { bs->meta_superblock, (size_t)bs->dsk.meta_block_size };
data->callback = [this](ring_data_t *data) { handle_event(data, -1, "read metadata header"); };
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
io_uring_prep_readv(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset);
bs->ringloop->submit();
submitted++;
@@ -86,19 +72,25 @@ resume_1:
}
if (is_zero((uint64_t*)bs->meta_superblock, bs->dsk.meta_block_size))
{
assert(bs->dsk.meta_format == BLOCKSTORE_META_FORMAT_HEAP);
blockstore_meta_header_v3_t *hdr = (blockstore_meta_header_v3_t *)bs->meta_superblock;
hdr->zero = 0;
hdr->magic = BLOCKSTORE_META_MAGIC_V1;
hdr->version = bs->dsk.meta_format;
hdr->meta_block_size = bs->dsk.meta_block_size;
hdr->data_block_size = bs->dsk.data_block_size;
hdr->bitmap_granularity = bs->dsk.bitmap_granularity;
hdr->completed_lsn = 0;
hdr->data_csum_type = bs->dsk.data_csum_type;
hdr->csum_block_size = bs->dsk.csum_block_size;
hdr->meta_area_size = bs->dsk.meta_area_size;
hdr->set_crc32c();
{
blockstore_meta_header_v3_t *hdr = (blockstore_meta_header_v3_t *)bs->meta_superblock;
hdr->zero = 0;
hdr->magic = BLOCKSTORE_META_MAGIC_V1;
hdr->version = bs->dsk.meta_format;
hdr->meta_block_size = bs->dsk.meta_block_size;
hdr->data_block_size = bs->dsk.data_block_size;
hdr->bitmap_granularity = bs->dsk.bitmap_granularity;
if (bs->dsk.meta_format >= BLOCKSTORE_META_FORMAT_V2)
{
hdr->data_csum_type = bs->dsk.data_csum_type;
hdr->csum_block_size = bs->dsk.csum_block_size;
}
if (bs->dsk.meta_format >= BLOCKSTORE_META_FORMAT_HEAP)
{
hdr->meta_area_size = bs->dsk.meta_area_size;
}
hdr->set_crc32c();
}
if (bs->readonly)
{
printf("Skipping metadata initialization because blockstore is readonly\n");
@@ -106,8 +98,21 @@ resume_1:
else
{
printf("Initializing metadata area\n");
GET_SQE();
last_read_offset = 0;
data->iov = (struct iovec){ bs->meta_superblock, (size_t)bs->dsk.meta_block_size };
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
io_uring_prep_writev(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset);
bs->ringloop->submit();
submitted++;
resume_2:
if (submitted > 0)
{
wait_state = 2;
return 1;
}
zero_on_init = true;
}
zero_on_init = true;
}
else
{
@@ -148,17 +153,9 @@ resume_1:
);
exit(1);
}
uint32_t csum = hdr->header_csum;
hdr->header_csum = 0;
if (crc32c(0, hdr, sizeof(*hdr)) != csum)
{
printf("Metadata header is corrupt (checksum mismatch).\n");
exit(1);
}
hdr->header_csum = csum;
}
bs->heap->start_load(((blockstore_meta_header_v3_t *)bs->meta_superblock)->completed_lsn);
if (bs->dsk.inmemory_journal && !zero_on_init)
if (bs->dsk.inmemory_journal)
{
// Read buffer area
printf("Reading buffered data\n");
@@ -170,7 +167,7 @@ resume_1:
bs->buffer_area + md_offset,
(size_t)(bs->dsk.journal_len - md_offset < bs->metadata_buf_size ? bs->dsk.journal_len - md_offset : bs->metadata_buf_size),
};
data->callback = [this](ring_data_t *data) { handle_event(data, -1, "read buffer area"); };
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
io_uring_prep_readv(sqe, bs->dsk.journal_fd, &data->iov, 1, bs->dsk.journal_offset + md_offset);
md_offset += data->iov.iov_len;
submitted++;
@@ -189,7 +186,7 @@ resume_3:
next_offset = md_offset;
// Read the rest of the metadata
resume_4:
if (next_offset < bs->dsk.meta_area_size && submitted == 0 && (!zero_on_init || !bs->readonly))
if (next_offset < bs->dsk.meta_area_size && submitted == 0)
{
// Submit one read
for (int i = 0; i < 2; i++)
@@ -206,15 +203,12 @@ resume_4:
GET_SQE();
assert(bufs[i].size <= 0x7fffffff);
data->iov = { bufs[i].buf, (size_t)bufs[i].size };
data->callback = [this, i](ring_data_t *data) { handle_event(data, i); };
if (!zero_on_init)
{
data->callback = [this, i](ring_data_t *data) { handle_event(data, i, "read metadata"); };
io_uring_prep_readv(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset + bufs[i].offset);
}
else
{
// Fill metadata with empty block pattern
data->callback = [this, i](ring_data_t *data) { handle_event(data, i, "clear metadata"); };
memset(bufs[i].buf, 0, bufs[i].size);
for (uint64_t o = 0; o < bufs[i].size; o += bs->dsk.meta_block_size)
bs->heap->fill_block_empty_space(bufs[i].buf + o, 0);
@@ -230,14 +224,11 @@ resume_4:
if (bufs[i].state == INIT_META_READ_DONE)
{
// Handle result
if (!zero_on_init)
{
uint64_t loaded = 0;
int r = bs->heap->load_blocks(bufs[i].offset-bs->dsk.meta_block_size, bufs[i].size, bufs[i].buf, bs->skip_corrupted_meta_entries, loaded);
if (r != 0)
exit(1);
entries_loaded += loaded;
}
uint64_t loaded = 0;
int r = bs->heap->load_blocks(bufs[i].offset-bs->dsk.meta_block_size, bufs[i].size, bufs[i].buf, bs->skip_corrupted_meta_entries, loaded);
if (r != 0)
exit(1);
entries_loaded += loaded;
bufs[i].state = 0;
bs->ringloop->wakeup();
}
@@ -247,47 +238,51 @@ resume_4:
wait_state = 4;
return 1;
}
// metadata read/clear finished
// metadata read finished
bs->heap->finish_load();
printf("Metadata entries loaded: %ju, rechecking unfinished writes and garbage entries\n", entries_loaded);
printf("Metadata entries loaded: %ju, used blocks: %ju / %ju\n", entries_loaded, bs->heap->get_data_used_space() / bs->dsk.data_block_size, bs->dsk.block_count);
if (zero_on_init && !bs->dsk.disable_meta_fsync)
{
GET_SQE();
io_uring_prep_fsync(sqe, bs->dsk.meta_fd, IORING_FSYNC_DATASYNC);
last_read_offset = 0;
data->iov = { 0 };
data->callback = [this](ring_data_t *data) { handle_event(data, -1); };
submitted++;
bs->ringloop->submit();
resume_5:
if (submitted > 0)
{
wait_state = 5;
return 1;
}
}
// asynchronous recheck
resume_6:
wait_state = 6;
bs->heap->recheck_small_writes([this](bool is_data, uint64_t offset, uint64_t len, std::function<void(uint8_t*)> cb)
bs->heap->recheck_small_writes([this](bool is_data, uint64_t offset, uint64_t len, uint8_t *buf, std::function<void()> cb)
{
if (!len)
if (!buf)
{
wait_state = 7;
bs->ringloop->wakeup();
return;
}
recheck_ios.push_back(new blockstore_recheck_io_t(is_data, offset, len, std::move(cb)));
}, bs->meta_write_recheck_parallelism);
while (recheck_ios.size())
{
blockstore_recheck_io_t* rc = recheck_ios.back();
sqe = bs->get_sqe();
if (!sqe)
break;
if (!rc->buf)
rc->buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, rc->len);
data = ((ring_data_t*)sqe->user_data);
data->iov = (iovec){ rc->buf, rc->len };
data->callback = [this, rc](ring_data_t *data)
GET_SQE();
data->iov = (iovec){ buf, len };
data->callback = [offset, cb](ring_data_t *data)
{
if (data->res < 0)
{
fprintf(stderr, "Buffer area read failed at offset %ju: %d\n", rc->offset, data->res);
fprintf(stderr, "Buffer area read failed at offset %ju: %d\n", offset, data->res);
exit(1);
}
rc->cb(rc->buf);
free(rc->buf);
delete rc;
cb();
};
io_uring_prep_readv(sqe, (rc->is_data ? bs->dsk.data_fd : bs->dsk.journal_fd), &data->iov, 1,
(rc->is_data ? bs->dsk.data_offset : bs->dsk.journal_offset) + rc->offset);
recheck_ios.pop_back();
}
io_uring_prep_readv(sqe, (is_data ? bs->dsk.data_fd : bs->dsk.journal_fd), &data->iov, 1,
(is_data ? bs->dsk.data_offset : bs->dsk.journal_offset) + offset);
bs->ringloop->submit();
}, bs->meta_write_recheck_parallelism);
return 1;
resume_7:
if (bs->heap->finish_recheck() != 0)
@@ -298,11 +293,6 @@ resume_7:
if (bs->readonly)
{
recheck_mod.clear();
printf("Actual metadata entries: %ju\n", bs->heap->get_live_entries());
}
else
{
printf("Actual metadata entries: %ju, clearing garbage in %zu metadata blocks\n", bs->heap->get_live_entries(), recheck_mod.size());
}
for (i = 0; i < recheck_mod.size(); i++)
{
@@ -316,7 +306,7 @@ resume_8:
uint32_t block_num = recheck_mod[i];
uint64_t block_offset = bs->dsk.meta_offset + (uint64_t)(block_num+1) * bs->dsk.meta_block_size;
data = ((ring_data_t*)sqe->user_data);
uint8_t *buf = (uint8_t*)memalign_or_die(MEM_ALIGNMENT, bs->dsk.meta_block_size);
uint8_t *buf = (uint8_t*)malloc_or_die(bs->dsk.meta_block_size);
bs->heap->get_meta_block(block_num, buf);
data->iov = { buf, bs->dsk.meta_block_size };
data->callback = [this, buf, block_offset](ring_data_t *data)
@@ -342,47 +332,5 @@ resume_9:
}
free(metadata_buffer);
metadata_buffer = NULL;
do_fsync:
if (!bs->dsk.disable_meta_fsync && !bs->readonly)
{
GET_SQE();
io_uring_prep_fsync(sqe, bs->dsk.meta_fd, IORING_FSYNC_DATASYNC);
last_read_offset = 0;
data->iov = { 0 };
data->callback = [this](ring_data_t *data) { handle_event(data, -1, "fsync metadata"); };
submitted++;
bs->ringloop->submit();
resume_5:
if (submitted > 0)
{
wait_state = 5;
return 1;
}
}
if (zero_on_init && !header_written && !bs->readonly)
{
GET_SQE();
header_written = true;
last_read_offset = 0;
data->iov = (struct iovec){ bs->meta_superblock, (size_t)bs->dsk.meta_block_size };
data->callback = [this](ring_data_t *data) { handle_event(data, -1, "write metadata header"); };
io_uring_prep_writev(sqe, bs->dsk.meta_fd, &data->iov, 1, bs->dsk.meta_offset);
bs->ringloop->submit();
submitted++;
resume_2:
if (submitted > 0)
{
wait_state = 2;
return 1;
}
if (!bs->dsk.disable_meta_fsync)
{
goto do_fsync;
}
}
printf("Loading finished. Data used: %ju / %ju bytes (%s / %s)\n",
bs->heap->get_data_used_space(), bs->dsk.block_count * bs->dsk.data_block_size,
format_size(bs->heap->get_data_used_space()).c_str(),
format_size(bs->dsk.block_count * bs->dsk.data_block_size).c_str());
return 0;
}

View File

@@ -11,15 +11,12 @@ struct blockstore_init_meta_buf
int state = 0;
};
struct blockstore_recheck_io_t;
class blockstore_init_meta
{
blockstore_impl_t *bs;
int wait_state = 0;
int wait_count = 0;
bool zero_on_init = false;
bool header_written = false;
void *metadata_buffer = NULL;
blockstore_init_meta_buf bufs[2] = {};
int submitted = 0;
@@ -30,10 +27,9 @@ class blockstore_init_meta
uint64_t last_read_offset = 0;
uint64_t entries_loaded = 0;
std::vector<uint32_t> recheck_mod;
std::vector<blockstore_recheck_io_t*> recheck_ios;
int i = 0, j = 0;
bool handle_meta_block(uint8_t *buf, uint64_t count, uint64_t done_cnt);
void handle_event(ring_data_t *data, int buf_num, const char *op);
void handle_event(ring_data_t *data, int buf_num);
public:
blockstore_init_meta(blockstore_impl_t *bs);
int loop();

View File

@@ -1,113 +0,0 @@
// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 (see README.md for details)
#include "blockstore_mock.h"
blockstore_mock_t::blockstore_mock_t(const blockstore_config_t & config)
{
}
void blockstore_mock_t::parse_config(blockstore_config_t & config)
{
}
void* blockstore_mock_t::reshard_start(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size, uint64_t chunk_limit)
{
return NULL;
}
bool blockstore_mock_t::reshard_continue(void *reshard_state, uint64_t chunk_limit)
{
return true;
}
void blockstore_mock_t::loop()
{
}
bool blockstore_mock_t::is_started()
{
return true;
}
bool blockstore_mock_t::is_stalled()
{
return false;
}
bool blockstore_mock_t::is_safe_to_stop()
{
return true;
}
void blockstore_mock_t::enqueue_op(blockstore_op_t *op)
{
}
int blockstore_mock_t::read_bitmap(object_id oid, uint64_t target_version, void *bitmap, uint64_t *result_version)
{
return -EIO;
}
const std::map<uint64_t, uint64_t> & blockstore_mock_t::get_inode_space_stats()
{
return inode_space;
}
void blockstore_mock_t::set_no_inode_stats(const std::vector<uint64_t> & pool_ids)
{
}
void blockstore_mock_t::dump_diagnostics()
{
}
std::string blockstore_mock_t::get_op_diag(blockstore_op_t *op)
{
return "";
}
uint32_t blockstore_mock_t::get_block_size()
{
return block_size;
}
uint64_t blockstore_mock_t::get_block_count()
{
return block_count;
}
uint64_t blockstore_mock_t::get_free_block_count()
{
return block_count;
}
uint64_t blockstore_mock_t::get_journal_size()
{
return 32*1024*1024;
}
uint32_t blockstore_mock_t::get_bitmap_granularity()
{
return bitmap_granularity;
}
uint64_t blockstore_mock_t::get_live_entries()
{
return 0;
}
uint64_t blockstore_mock_t::get_live_memory()
{
return 0;
}
uint64_t blockstore_mock_t::get_garbage_entries()
{
return 0;
}
uint64_t blockstore_mock_t::get_garbage_memory()
{
return 0;
}

View File

@@ -1,39 +0,0 @@
// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 (see README.md for details)
#pragma once
#include "blockstore.h"
class blockstore_mock_t: public blockstore_i
{
public:
uint32_t block_size = 128*1024;
uint32_t bitmap_granularity = 4096;
uint64_t block_count = 100*1024*8;
std::map<uint64_t, uint64_t> inode_space;
blockstore_mock_t(const blockstore_config_t & config);
void parse_config(blockstore_config_t & config) override;
void* reshard_start(pool_id_t pool, uint32_t pg_count, uint32_t pg_stripe_size, uint64_t chunk_limit) override;
bool reshard_continue(void *reshard_state, uint64_t chunk_limit) override;
void loop() override;
bool is_started() override;
bool is_stalled() override;
bool is_safe_to_stop() override;
void enqueue_op(blockstore_op_t *op) override;
int read_bitmap(object_id oid, uint64_t target_version, void *bitmap, uint64_t *result_version = NULL) override;
const std::map<uint64_t, uint64_t> & get_inode_space_stats() override;
void set_no_inode_stats(const std::vector<uint64_t> & pool_ids) override;
void dump_diagnostics() override;
std::string get_op_diag(blockstore_op_t *op) override;
uint32_t get_block_size() override;
uint64_t get_block_count() override;
uint64_t get_free_block_count() override;
uint64_t get_journal_size() override;
uint32_t get_bitmap_granularity() override;
uint64_t get_live_entries() override;
uint64_t get_live_memory() override;
uint64_t get_garbage_entries() override;
uint64_t get_garbage_memory() override;
};

View File

@@ -105,12 +105,12 @@ void blockstore_impl_t::parse_config(blockstore_config_t & config, bool init)
{
meta_write_recheck_parallelism = 16;
}
if (immediate_commit != IMMEDIATE_NONE && (!dsk.disable_journal_fsync || !dsk.disable_meta_fsync))
if (immediate_commit != IMMEDIATE_NONE && !dsk.disable_journal_fsync)
{
throw std::runtime_error("immediate_commit requires disable_journal_fsync and disable_meta_fsync");
throw std::runtime_error("immediate_commit requires disable_journal_fsync");
}
if (immediate_commit == IMMEDIATE_ALL && !dsk.disable_data_fsync)
{
throw std::runtime_error("immediate_commit=all requires disable_data_fsync");
throw std::runtime_error("immediate_commit=all requires disable_journal_fsync and disable_data_fsync");
}
}

View File

@@ -134,8 +134,7 @@ int blockstore_impl_t::fulfill_read(blockstore_op_t *op)
}
else if ((vec.copy_flags & COPY_BUF_JOURNAL) && dsk.inmemory_journal)
{
// disk_loc may be negative - make ubsan happy
memcpy(op->buf + vec.offset - op->offset, buffer_area + (vec.disk_loc + vec.disk_offset), vec.len);
memcpy(op->buf + vec.offset - op->offset, buffer_area + vec.disk_loc + vec.disk_offset, vec.len);
}
else
{
@@ -463,10 +462,6 @@ int blockstore_impl_t::read_bitmap(object_id oid, uint64_t target_version, void
{
if (target_version >= wr->version)
{
if (wr->type() == BS_HEAP_DELETE)
{
return false;
}
found = true;
if (result_version)
{

View File

@@ -16,7 +16,6 @@ int blockstore_impl_t::dequeue_stable(blockstore_op_t *op)
else if (priv->op_state == 5) goto resume_5;
assert(!priv->op_state);
op->retval = 0;
PRIV(op)->lsn = 0;
priv->modified_block = priv->modified_block2 = UINT32_MAX;
for (priv->stab_pos = 0; priv->stab_pos < op->len; priv->stab_pos++)
{
@@ -28,7 +27,6 @@ int blockstore_impl_t::dequeue_stable(blockstore_op_t *op)
FINISH_OP(op);
return 2;
}
priv->modified_block2 = UINT32_MAX;
int res = op->opcode == BS_OP_STABLE
? heap->add_commit(obj, v[priv->stab_pos].version, &priv->modified_block2)
: heap->add_rollback(obj, v[priv->stab_pos].version, &priv->modified_block2);
@@ -38,12 +36,6 @@ int blockstore_impl_t::dequeue_stable(blockstore_op_t *op)
FINISH_OP(op);
return 2;
}
if (res == ENOENT)
{
op->retval = -ENOENT;
FINISH_OP(op);
return 2;
}
if (res == ENOSPC)
{
if (!heap->get_to_compact_count())
@@ -53,6 +45,11 @@ int blockstore_impl_t::dequeue_stable(blockstore_op_t *op)
FINISH_OP(op);
return 2;
}
if (priv->modified_block2 != UINT32_MAX)
{
priv->stab_pos--;
goto resume_1;
}
priv->wait_for = WAIT_COMPACTION;
priv->wait_detail = heap->get_compacted_count();
flusher->request_trim();

View File

@@ -9,7 +9,6 @@ int blockstore_impl_t::continue_sync(blockstore_op_t *op)
if (!PRIV(op)->op_state)
{
op->retval = 0;
PRIV(op)->lsn = 0;
}
int res = do_sync(op, 0);
if (res == 2)
@@ -29,12 +28,9 @@ bool blockstore_impl_t::has_unsynced()
bool blockstore_impl_t::submit_fsyncs(int & wait_count)
{
int n = (unsynced_meta_write_count > 0 && !dsk.disable_meta_fsync ? 1 : 0) +
(unsynced_buffer_write_count > 0 && !dsk.disable_journal_fsync &&
(!unsynced_meta_write_count || dsk.journal_fd != dsk.meta_fd) ? 1 : 0) +
(unsynced_data_write_count > 0 && !dsk.disable_data_fsync &&
(!unsynced_meta_write_count || dsk.data_fd != dsk.meta_fd) &&
(!unsynced_buffer_write_count || dsk.data_fd != dsk.journal_fd) ? 1 : 0);
int n = (unsynced_meta_write_count > 0 && !dsk.disable_meta_fsync) +
(unsynced_buffer_write_count > 0 && !dsk.disable_journal_fsync && dsk.journal_fd != dsk.meta_fd) +
(unsynced_data_write_count > 0 && !dsk.disable_data_fsync && dsk.data_fd != dsk.meta_fd && dsk.data_fd != dsk.journal_fd);
if (ringloop->space_left() < n)
{
return false;
@@ -63,8 +59,7 @@ bool blockstore_impl_t::submit_fsyncs(int & wait_count)
data->callback = cb;
wait_count++;
}
if (unsynced_buffer_write_count > 0 && !dsk.disable_journal_fsync &&
(!unsynced_meta_write_count || dsk.journal_fd != dsk.meta_fd))
if (unsynced_buffer_write_count > 0 && !dsk.disable_journal_fsync && dsk.meta_fd != dsk.journal_fd)
{
// fsync buffer
io_uring_sqe *sqe = get_sqe();
@@ -75,9 +70,7 @@ bool blockstore_impl_t::submit_fsyncs(int & wait_count)
data->callback = cb;
wait_count++;
}
if (unsynced_data_write_count > 0 && !dsk.disable_data_fsync &&
(!unsynced_meta_write_count || dsk.data_fd != dsk.meta_fd) &&
(!unsynced_buffer_write_count || dsk.data_fd != dsk.journal_fd))
if (unsynced_data_write_count > 0 && !dsk.disable_data_fsync && dsk.data_fd != dsk.meta_fd && dsk.data_fd != dsk.journal_fd)
{
// fsync data
io_uring_sqe *sqe = get_sqe();
@@ -111,11 +104,9 @@ int blockstore_impl_t::do_sync(blockstore_op_t *op, int base_state)
unsynced_data_write_count = unsynced_buffer_write_count = unsynced_meta_write_count = 0;
return 2;
}
assert(!PRIV(op)->lsn);
PRIV(op)->lsn = heap->get_completed_lsn();
PRIV(op)->modified_block = heap->get_completed_lsn();
if (!submit_fsyncs(PRIV(op)->pending_ops))
{
PRIV(op)->lsn = 0;
PRIV(op)->wait_detail = 1;
PRIV(op)->wait_for = WAIT_SQE;
return 0;
@@ -127,6 +118,6 @@ resume_1:
return 1;
}
resume_2:
heap->mark_lsn_fsynced(PRIV(op)->lsn);
heap->mark_lsn_fsynced(PRIV(op)->modified_block);
return 2;
}

View File

@@ -37,7 +37,6 @@ void blockstore_impl_t::prepare_meta_block_write(uint32_t modified_block)
heap->complete_block_write(modified_block);
ringloop->wakeup();
};
assert(((uint64_t)modified_block+2)*dsk.meta_block_size <= dsk.meta_area_size);
io_uring_prep_writev(
sqe, dsk.meta_fd, &data->iov, 1, dsk.meta_offset + ((uint64_t)modified_block+1)*dsk.meta_block_size
);
@@ -178,18 +177,14 @@ enospc:
ring_data_t *data = ((ring_data_t*)sqe->user_data);
data->iov = (struct iovec){ op->buf, op->len };
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
assert(loc+op->offset+op->len <= dsk.block_count*dsk.data_block_size);
io_uring_prep_writev(sqe, dsk.data_fd, &data->iov, 1, dsk.data_offset + loc + op->offset);
if (!dsk.disable_data_fsync)
{
// use PRIV->lsn for fsync_data_id
PRIV(op)->lsn = ++data_fsync_next;
data_fsyncs.push_back(false);
}
PRIV(op)->pending_ops++;
write_iodepth++;
if (PRIV(op)->write_type == BS_HEAP_BIG_WRITE)
{
PRIV(op)->op_state = 1;
inflight_big++;
}
else
PRIV(op)->op_state = 3;
}
@@ -243,12 +238,6 @@ enospc:
uint64_t loc = !op->len ? 0 : heap->find_free_buffer_area(op->len);
if (loc == UINT64_MAX)
{
if (!heap->get_to_compact_count())
{
op->retval = -EAGAIN;
FINISH_OP(op);
return 2;
}
PRIV(op)->wait_for = WAIT_COMPACTION;
PRIV(op)->wait_detail = heap->get_compacted_count();
flusher->request_trim();
@@ -275,7 +264,6 @@ enospc:
BS_SUBMIT_GET_SQE(sqe2, data2);
data2->iov = (struct iovec){ op->buf, op->len };
data2->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
assert(loc+op->len <= dsk.journal_len);
io_uring_prep_writev(sqe2, dsk.journal_fd, &data2->iov, 1, dsk.journal_offset + loc);
PRIV(op)->pending_ops++;
}
@@ -306,6 +294,8 @@ again:
goto resume_10;
else if (op_state == 11)
goto resume_11;
else if (op_state == 12)
goto resume_12;
else
{
// In progress
@@ -324,47 +314,38 @@ again:
resume_2:
// We must fsync all big writes to avoid complex write workflows
// It's OK for all HDDs and for server SSDs, but slightly worse for desktop SSDs
inflight_big--;
if (!dsk.disable_data_fsync)
{
// Mark our data write as completed and advance data_fsync_cur
data_fsyncs[PRIV(op)->lsn - data_fsync_cur - 1] = true;
while (data_fsyncs.size() > 0 && data_fsyncs.front())
{
data_fsyncs.pop_front();
data_fsync_cur++;
}
PRIV(op)->op_state = 11;
// Then wait for all other data writes currently in progress to do less fsync calls
// I.e. to fsync data in batches
PRIV(op)->lsn = data_fsync_cur + data_fsyncs.size();
// fsync data in a batch
resume_11:
if (data_fsync_cur < PRIV(op)->lsn)
if (inflight_big > 0)
{
PRIV(op)->op_state = 11;
return 1;
}
if (PRIV(op)->lsn > data_fsync_sent)
if (fsyncing_data)
{
BS_SUBMIT_GET_SQE(sqe, data);
io_uring_prep_fsync(sqe, dsk.data_fd, IORING_FSYNC_DATASYNC);
data->iov = { 0 };
data->callback = [this, op, fs = data_fsync_cur](ring_data_t *data)
resume_12:
if (fsyncing_data)
{
live = true;
if (data->res != 0)
disk_error_abort("sync data", data->res, 0);
if (fs > data_fsync_done)
{
data_fsync_done = fs;
ringloop->wakeup();
}
};
data_fsync_sent = data_fsync_cur;
PRIV(op)->op_state = 12;
return 1;
}
goto resume_4;
}
if (PRIV(op)->lsn > data_fsync_done)
fsyncing_data = true;
BS_SUBMIT_GET_SQE(sqe, data);
io_uring_prep_fsync(sqe, dsk.data_fd, IORING_FSYNC_DATASYNC);
data->iov = { 0 };
data->callback = [this, op](ring_data_t *data)
{
return 1;
}
PRIV(op)->lsn = 0;
fsyncing_data = false;
handle_write_event(data, op);
};
PRIV(op)->pending_ops++;
PRIV(op)->op_state = 3;
return 1;
}
resume_4:
{
@@ -472,7 +453,6 @@ resume_10:
BS_SUBMIT_GET_SQE(sqe, data);
data->iov = (struct iovec){ op->buf, op->len };
data->callback = [this, op](ring_data_t *data) { handle_write_event(data, op); };
assert(PRIV(op)->location + op->offset <= dsk.block_count*dsk.data_block_size);
io_uring_prep_writev(sqe, dsk.data_fd, &data->iov, 1, dsk.data_offset + PRIV(op)->location + op->offset);
if (dsk.use_atomic_flag)
sqe->rw_flags = RWF_ATOMIC;

View File

@@ -262,7 +262,7 @@ static enum fio_q_status _bs_queue(struct thread_data *td, struct io_u *io, bool
bsd->inflight--;
bsd->completed.push_back(io);
if (bsd->trace)
printf("--- OP_READ %zx n=%d retval=%jd\n", (size_t)op, n, op->retval);
printf("--- OP_READ %zx n=%d retval=%d\n", (size_t)op, n, op->retval);
delete op;
};
break;
@@ -283,7 +283,7 @@ static enum fio_q_status _bs_queue(struct thread_data *td, struct io_u *io, bool
{
bs_data *bsd = ((bs_data*)((thread_data*)io->engine_data)->io_ops_data);
if (bsd->trace)
printf("--- OP_WRITE %zx n=%d retval=%jd\n", (size_t)op, n, op->retval);
printf("--- OP_WRITE %zx n=%d retval=%d\n", (size_t)op, n, op->retval);
if (op->retval < 0)
{
io->error = op->retval < 0 ? -op->retval : 0;
@@ -305,7 +305,7 @@ static enum fio_q_status _bs_queue(struct thread_data *td, struct io_u *io, bool
{
bs_data *bsd = ((bs_data*)((thread_data*)io->engine_data)->io_ops_data);
if (bsd->trace)
printf("--- OP_STABLE %zx n=%d retval=%jd\n", (size_t)op, n, op->retval);
printf("--- OP_STABLE %zx n=%d retval=%d\n", (size_t)op, n, op->retval);
io->error = op->retval < 0 ? -op->retval : 0;
bsd->inflight--;
bsd->completed.push_back(io);
@@ -323,7 +323,7 @@ static enum fio_q_status _bs_queue(struct thread_data *td, struct io_u *io, bool
{
bs_data *bsd = ((bs_data*)((thread_data*)io->engine_data)->io_ops_data);
if (bsd->trace)
printf("--- OP_WRITE_STABLE %zx n=%d retval=%jd\n", (size_t)op, n, op->retval);
printf("--- OP_WRITE_STABLE %zx n=%d retval=%d\n", (size_t)op, n, op->retval);
io->error = op->retval < 0 ? -op->retval : 0;
bsd->inflight--;
bsd->completed.push_back(io);
@@ -342,7 +342,7 @@ static enum fio_q_status _bs_queue(struct thread_data *td, struct io_u *io, bool
bsd->completed.push_back(io);
bsd->inflight--;
if (bsd->trace)
printf("--- OP_SYNC %zx n=%d retval=%jd\n", (size_t)op, n, op->retval);
printf("--- OP_SYNC %zx n=%d retval=%d\n", (size_t)op, n, op->retval);
delete op;
};
bsd->last_sync = true;

Some files were not shown because too many files have changed in this diff Show More