Add nbdssl support

This commit is contained in:
Lucian Petrut
2026-09-07 12:46:00 +00:00
parent c3e3fa5769
commit b7131ec0af
14 changed files with 375 additions and 99 deletions
+37
View File
@@ -0,0 +1,37 @@
# Copyright 2026 Cloudbase Solutions Srl
# All Rights Reserved.
"""Session-scoped lab fixtures for live vSphere tests."""
from __future__ import annotations
import os
from collections.abc import Iterator
import pytest
from tests.integration.base import (
LabEnv,
create_lab_vm,
destroy_lab_vm,
ensure_vddk_library_path,
require_vddk,
)
@pytest.fixture(scope="session")
def lab() -> Iterator[LabEnv]:
"""Create one temporary lab VM for the whole pytest session."""
os.environ.pop("LD_PRELOAD", None)
ensure_vddk_library_path()
env = create_lab_vm()
try:
yield env
finally:
destroy_lab_vm(env)
@pytest.fixture(scope="session")
def vddk() -> None:
"""Skip VDDK-backed tests when ``libvixDiskLib`` cannot be loaded."""
require_vddk()
+7 -3
View File
@@ -60,7 +60,10 @@ class LabEnv:
vmx_spec: str
disk_path: str
def authenticate(self, read_only: bool = True) -> NfcAuthSession:
def authenticate(
self,
read_only: bool = True,
nfc_ssl: bool = True) -> NfcAuthSession:
"""Login to the lab vCenter and complete NFC authd for the temp VM."""
return nfc_auth.authenticate(
host=self.host,
@@ -70,7 +73,8 @@ class LabEnv:
thumbprint=self.thumbprint,
allow_untrusted=self.allow_untrusted,
disk_path=None if read_only else self.disk_path,
read_only=read_only)
read_only=read_only,
nfc_ssl=nfc_ssl)
def vixdisklib_connect_kwargs(
self, extra: Optional[dict[str, Any]] = None) -> dict[str, Any]:
@@ -82,7 +86,7 @@ class LabEnv:
"username": self.username,
"password": self.password,
"vmx_spec": self.vmx_spec,
"transport_modes": "nbd",
"transport_modes": "nbdssl",
"read_only": False,
}
if extra:
+9
View File
@@ -14,5 +14,14 @@ class TestNfcAuth:
assert ticket.host
assert ticket.port
assert ticket.sessionId
assert session.nfc_ssl is True
assert session.authd_sock.version()
assert session.authd_sock.cipher()
def test_authd_nbd_handshake_completes(self, lab: LabEnv) -> None:
"""Complete authd with plaintext NFC after PROXY (nbd)."""
with lab.authenticate(nfc_ssl=False) as session:
assert session.nfc_ssl is False
assert session.ticket.sessionId
assert session.authd_sock.version()
assert session.authd_sock.cipher()
+7 -2
View File
@@ -3,15 +3,20 @@
"""Exercise NFC disk open and a one-sector write/read against the lab."""
import pytest
from openvixdisklib import nfc_open
from tests.integration.base import LabEnv, SECTOR_SIZE, pattern_bytes
class TestNfcOpen:
def test_open_disk_and_read_first_sector(self, lab: LabEnv) -> None:
@pytest.mark.parametrize(
"nfc_ssl", [True, False], ids=["nbdssl", "nbd"])
def test_open_disk_and_read_first_sector(
self, lab: LabEnv, nfc_ssl: bool) -> None:
"""Open the temp VMDK, write sector 0, and read it back."""
expected = pattern_bytes(SECTOR_SIZE, b"NFC-OPEN-S0")
with lab.authenticate(read_only=False) as session:
with lab.authenticate(read_only=False, nfc_ssl=nfc_ssl) as session:
with nfc_open.open_disk(
session, lab.disk_path, read_only=False) as disk:
assert disk.path == lab.disk_path
+8 -1
View File
@@ -3,13 +3,17 @@
"""Exercise the VDDK-compatible openvixdisklib handle against the lab."""
import pytest
from openvixdisklib import openvixdisklib as vixdisklib
from tests.integration.base import (
LabEnv, SECTOR_AT_1GB, SECTOR_SIZE, pattern_bytes)
class TestOpenvixdisklib:
def test_write_and_read_sector_zero_and_one_gib(self, lab: LabEnv) -> None:
@pytest.mark.parametrize("transport_mode", ["nbdssl", "nbd"])
def test_write_and_read_sector_zero_and_one_gib(
self, lab: LabEnv, transport_mode: str) -> None:
"""Write then read sector 0 and the sector at a 1 GiB offset."""
handle = vixdisklib.VixDiskLibHandle(
vixdisklib_compatibility_version="8.0",
@@ -18,13 +22,16 @@ class TestOpenvixdisklib:
read_buf = vixdisklib.get_buffer(SECTOR_SIZE)
connect_kwargs = lab.vixdisklib_connect_kwargs({
"allow_untrusted": lab.allow_untrusted,
"transport_modes": transport_mode,
})
patterns = {
0: pattern_bytes(SECTOR_SIZE, b"OVDL-S0"),
SECTOR_AT_1GB: pattern_bytes(SECTOR_SIZE, b"OVDL-1GB"),
}
assert handle.get_transport_modes() == ["nbdssl", "nbd"]
with handle.connect(**connect_kwargs) as conn:
with handle.open(conn, lab.disk_path, flags=0) as disk:
assert handle.get_transport_mode(disk) == transport_mode
for start, expected in patterns.items():
write_buf[:SECTOR_SIZE] = expected
handle.write(disk, start, 1, write_buf)
View File
+88
View File
@@ -0,0 +1,88 @@
# Copyright 2026 Cloudbase Solutions Srl
# All Rights Reserved.
"""Compare openvixdisklib and native VDDK I/O throughput against the lab."""
from __future__ import annotations
import time
from typing import Any, Optional
from openvixdisklib import openvixdisklib as open_vix
from tests.integration import vixdisklib
from tests.integration.base import LabEnv, SECTOR_SIZE, pattern_bytes
_SIZES = (
("64KiB", 64 * 1024),
("129 sectors", 129 * SECTOR_SIZE),
("32MiB", 32 * 1024 * 1024),
)
def _connect_extra(lab: LabEnv, module: Any) -> Optional[dict[str, Any]]:
"""Return extra ``connect`` kwargs needed by ``module``."""
if module is open_vix:
return {"allow_untrusted": lab.allow_untrusted}
return None
def _time_write_read(
lab: LabEnv,
module: Any,
payload: bytes) -> tuple[float, float]:
"""Write ``payload`` at sector 0, read it back, and return durations."""
n_sectors = len(payload) // SECTOR_SIZE
handle = module.VixDiskLibHandle(
vixdisklib_compatibility_version="8.0",
config_path=None)
write_buf = module.get_buffer(len(payload))
read_buf = module.get_buffer(len(payload))
write_buf[:len(payload)] = payload
kwargs = lab.vixdisklib_connect_kwargs(_connect_extra(lab, module))
with handle.connect(**kwargs) as conn:
with handle.open(conn, lab.disk_path, flags=0) as disk:
started = time.perf_counter()
handle.write(disk, 0, n_sectors, write_buf)
write_s = time.perf_counter() - started
read_buf[:len(payload)] = b"\xa5" * len(payload)
started = time.perf_counter()
handle.read(disk, 0, n_sectors, read_buf)
read_s = time.perf_counter() - started
assert read_buf.raw[:len(payload)] == payload
return write_s, read_s
def _mib_per_s(nbytes: int, seconds: float) -> float:
if seconds <= 0:
return float("inf")
return (nbytes / (1024 * 1024)) / seconds
class TestCompare:
def test_write_read_throughput(self, lab: LabEnv, vddk: None) -> None:
"""Time matching write/read sizes on VDDK and openvixdisklib."""
libraries = (
("vddk", vixdisklib),
("openvixdisklib", open_vix),
)
rows: list[tuple[str, str, float, float, float, float]] = []
for label, nbytes in _SIZES:
payload = pattern_bytes(nbytes, f"PERF-{label}-".encode())
for name, module in libraries:
write_s, read_s = _time_write_read(lab, module, payload)
rows.append((
label,
name,
write_s,
read_s,
_mib_per_s(nbytes, write_s),
_mib_per_s(nbytes, read_s),
))
print()
print(
f"{'size':<14} {'library':<16} {'write_s':>10} {'read_s':>10} "
f"{'write_MiB/s':>12} {'read_MiB/s':>12}")
for label, name, write_s, read_s, write_r, read_r in rows:
print(
f"{label:<14} {name:<16} {write_s:10.3f} {read_s:10.3f} "
f"{write_r:12.1f} {read_r:12.1f}")