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.
85 lines
3.0 KiB
Python
85 lines
3.0 KiB
Python
"""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"
|