feat: initial QNAP QTS driver scaffold
QTS is a Linux distribution, so the driver inherits the OS surface from LinuxDriver and adds QNAP's storage, QPKG and virtualisation layers. What is here: the class with its discovery fingerprints, per-session detection of the QTS major version and of the QPKG-local docker and virsh binaries, and explicit resolution of the MRO collisions that StorageDriver creates over LinuxDriver. What is not: the storage, QPKG and VM parsers. Those need real command output from QTS 4 and QTS 5 hardware to be written against, which is what tools/harvest.sh collects and tools/sanitize.py anonymises. Two tests are marked xfail(strict) as the specification for that work.
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
"""The harvest sanitiser (tools/sanitize.py).
|
||||
|
||||
Fixtures are committed forever, so a miss here puts a real NAS's serial numbers
|
||||
and addresses into git history. Worth testing even though it is a dev tool.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import pathlib
|
||||
|
||||
import pytest
|
||||
|
||||
_SPEC = importlib.util.spec_from_file_location(
|
||||
"qnap_sanitize", pathlib.Path(__file__).parent.parent / "tools" / "sanitize.py"
|
||||
)
|
||||
assert _SPEC and _SPEC.loader
|
||||
sanitize = importlib.util.module_from_spec(_SPEC)
|
||||
_SPEC.loader.exec_module(sanitize)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def scrubber():
|
||||
return sanitize.Scrubber()
|
||||
|
||||
|
||||
class TestSerials:
|
||||
def test_labelled_serial_is_replaced(self, scrubber):
|
||||
out = scrubber.scrub("Serial Number: WD-WCC4N7RTKZ9P")
|
||||
assert "WD-WCC4N7RTKZ9P" not in out
|
||||
assert "SERIAL001XXXX" in out
|
||||
|
||||
def test_lsblk_serial_field_is_replaced(self, scrubber):
|
||||
out = scrubber.scrub('NAME="sda" MODEL="WD40EFRX" SERIAL="WD-WCC4N7RTKZ9P"')
|
||||
assert "WD-WCC4N7RTKZ9P" not in out
|
||||
assert 'MODEL="WD40EFRX"' in out, "model names are not identifying and must survive"
|
||||
|
||||
def test_same_serial_maps_to_the_same_placeholder(self, scrubber):
|
||||
"""A disk serial appears in several harvested files; the cross-reference
|
||||
has to survive or the fixtures stop describing one coherent device."""
|
||||
first = scrubber.scrub("serial: ABC123456")
|
||||
second = scrubber.scrub('SERIAL="ABC123456"')
|
||||
assert "SERIAL001XXXX" in first
|
||||
assert "SERIAL001XXXX" in second
|
||||
|
||||
def test_distinct_serials_get_distinct_placeholders(self, scrubber):
|
||||
out = scrubber.scrub("sn: AAA111222\nsn: BBB333444")
|
||||
assert "SERIAL001XXXX" in out
|
||||
assert "SERIAL002XXXX" in out
|
||||
|
||||
|
||||
class TestNetworkIdentifiers:
|
||||
def test_mac_is_replaced(self, scrubber):
|
||||
out = scrubber.scrub("link/ether 24:5e:be:11:22:33 brd ff:ff:ff:ff:ff:ff")
|
||||
assert "24:5e:be:11:22:33" not in out
|
||||
assert "00:11:22:33:44:" in out
|
||||
|
||||
def test_ipv4_is_replaced(self, scrubber):
|
||||
out = scrubber.scrub("inet 10.7.224.12/24")
|
||||
assert "10.7.224.12" not in out
|
||||
assert "192.0.2.1" in out
|
||||
|
||||
def test_loopback_and_wildcard_survive(self, scrubber):
|
||||
"""Replacing these makes fixtures unreadable and reveals nothing."""
|
||||
out = scrubber.scrub("0.0.0.0:445 127.0.0.1:22")
|
||||
assert "0.0.0.0" in out
|
||||
assert "127.0.0.1" in out
|
||||
|
||||
def test_same_ip_maps_consistently(self, scrubber):
|
||||
out = scrubber.scrub("gw 10.0.0.1\nvia 10.0.0.1")
|
||||
assert out.count("192.0.2.1") == 2
|
||||
|
||||
|
||||
class TestHostnames:
|
||||
def test_extra_host_is_replaced_case_insensitively(self):
|
||||
s = sanitize.Scrubber(["MyNAS"])
|
||||
out = s.scrub("Server Name = mynas\nhost MYNAS ok")
|
||||
assert "mynas" not in out.lower().replace("testnas", "")
|
||||
assert out.count("testnas") == 2
|
||||
|
||||
def test_empty_host_entry_is_ignored(self):
|
||||
"""An unset --extra-host must not turn into a regex that matches everything."""
|
||||
s = sanitize.Scrubber(["", None])
|
||||
assert s.scrub("untouched text") == "untouched text"
|
||||
Reference in New Issue
Block a user