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:
Executable
+105
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env bash
|
||||
# Collect the command outputs this driver parses, from a real QNAP NAS.
|
||||
#
|
||||
# Run once against a QTS 4 box and once against a QTS 5 box; the resulting
|
||||
# files become the test fixtures. Writing parsers against guessed output is how
|
||||
# a driver ends up passing its own tests and failing on hardware, so this runs
|
||||
# before any parser code.
|
||||
#
|
||||
# ./tools/harvest.sh admin@nas1.example.lan qts4
|
||||
# ./tools/harvest.sh admin@nas2.example.lan qts5
|
||||
#
|
||||
# Output lands in tools/harvest-out/<label>/ (gitignored). Sanitise with
|
||||
# ./tools/sanitize.py before pasting anything into a test file.
|
||||
set -uo pipefail
|
||||
|
||||
TARGET="${1:?usage: harvest.sh <user@host> <label> [ssh-port]}"
|
||||
LABEL="${2:?usage: harvest.sh <user@host> <label> [ssh-port]}"
|
||||
PORT="${3:-22}"
|
||||
OUT="$(dirname "$0")/harvest-out/$LABEL"
|
||||
mkdir -p "$OUT"
|
||||
|
||||
# One SSH session for the whole run: QTS is slow to authenticate, and opening a
|
||||
# connection per command turns a 30-second harvest into several minutes.
|
||||
CTL="$(mktemp -u /tmp/qnap-harvest-XXXXXX)"
|
||||
cleanup() { ssh -O exit -o ControlPath="$CTL" "$TARGET" 2>/dev/null || true; }
|
||||
trap cleanup EXIT
|
||||
|
||||
ssh -M -o ControlPath="$CTL" -o ControlPersist=120 -p "$PORT" -N "$TARGET" &
|
||||
sleep 3
|
||||
|
||||
run() {
|
||||
local name="$1"; shift
|
||||
local cmd="$*"
|
||||
printf ' %-28s %s\n' "$name" "$cmd"
|
||||
{
|
||||
echo "### command: $cmd"
|
||||
ssh -o ControlPath="$CTL" -p "$PORT" "$TARGET" "$cmd" 2>&1
|
||||
echo "### exit: $?"
|
||||
} > "$OUT/$name.txt"
|
||||
}
|
||||
|
||||
echo "Harvesting $TARGET -> $OUT"
|
||||
|
||||
# ── Identity / facts ──────────────────────────────────────────────────────────
|
||||
run version 'getcfg System Version'
|
||||
run build 'getcfg System "Build Number"'
|
||||
run model 'getcfg System Model'
|
||||
run servername 'getcfg System "Server Name"'
|
||||
run getsysinfo_model 'getsysinfo model'
|
||||
run getsysinfo_serial 'getsysinfo serial'
|
||||
run uptime 'cat /proc/uptime'
|
||||
run uname 'uname -a'
|
||||
run ulinux_system 'sed -n "/^\[System\]/,/^\[/p" /etc/config/uLinux.conf'
|
||||
|
||||
# ── Interfaces ────────────────────────────────────────────────────────────────
|
||||
run ip_link 'ip -o link show'
|
||||
run ip_addr 'ip -o addr show'
|
||||
run ifconfig 'ifconfig -a'
|
||||
run ulinux_network 'sed -n "/^\[Network\]/,/^\[/p" /etc/config/uLinux.conf'
|
||||
|
||||
# ── Storage ───────────────────────────────────────────────────────────────────
|
||||
run qcli_storage 'qcli_storage'
|
||||
run qcli_storage_d 'qcli_storage -d'
|
||||
run qcli_storage_v 'qcli_storage -v'
|
||||
run qcli_storage_p 'qcli_storage -p'
|
||||
run qcli_storage_t 'qcli_storage -t'
|
||||
run mdstat 'cat /proc/mdstat'
|
||||
run volume_conf 'cat /etc/volume.conf'
|
||||
run enclosure_conf 'cat /etc/enclosure_0.conf'
|
||||
run df 'df -B1 -P'
|
||||
run hdnum 'getsysinfo hdnum'
|
||||
run hdtmp_all 'for i in $(seq 1 8); do echo "== slot $i"; getsysinfo hdtmp $i; done'
|
||||
run hdstatus_all 'for i in $(seq 1 8); do echo "== slot $i"; getsysinfo hdstatus $i; done'
|
||||
run smartinfo_1 'get_hd_smartinfo -d 1'
|
||||
run smartinfo_2 'get_hd_smartinfo -d 2'
|
||||
run lsblk 'lsblk -b -P -o NAME,MODEL,SERIAL,SIZE,ROTA,TYPE'
|
||||
|
||||
# ── Shares & services ─────────────────────────────────────────────────────────
|
||||
run smb_conf 'cat /etc/config/smb.conf'
|
||||
run exports 'cat /etc/exports'
|
||||
run ss_listen 'ss -lntup'
|
||||
run svc_samba 'getcfg Samba Enable'
|
||||
run svc_nfs 'getcfg NFS Enable'
|
||||
run svc_ftp 'getcfg FTP Enable'
|
||||
run svc_afp 'getcfg AFP Enable'
|
||||
run svc_iscsi 'getcfg iSCSI Enable'
|
||||
run init_d 'ls /etc/init.d/'
|
||||
|
||||
# ── Packages (QPKG) ───────────────────────────────────────────────────────────
|
||||
run qpkg_conf 'cat /etc/config/qpkg.conf'
|
||||
run qpkg_cli 'qpkg_cli -L'
|
||||
run qpkg_dir 'ls -d /share/*/.qpkg/*'
|
||||
|
||||
# ── Virtualisation & containers ───────────────────────────────────────────────
|
||||
run virsh_which 'ls /share/*/.qpkg/QKVM/usr/bin/virsh /share/*/.qpkg/*/bin/virsh 2>&1'
|
||||
run virsh_list 'virsh list --all'
|
||||
run docker_which 'ls /share/*/.qpkg/container-station/bin/docker /share/*/.qpkg/container-station/usr/bin/docker 2>&1'
|
||||
run docker_ps 'docker ps -a --no-trunc --format "{{json .}}"'
|
||||
|
||||
# ── Firmware / updates ────────────────────────────────────────────────────────
|
||||
run fw_update_cfg 'sed -n "/^\[FIRMWARE UPDATE\]/,/^\[/p" /etc/config/uLinux.conf'
|
||||
|
||||
echo
|
||||
echo "Done. $(ls "$OUT" | wc -l) files in $OUT"
|
||||
echo "Next: ./tools/sanitize.py $OUT"
|
||||
Reference in New Issue
Block a user