From 27eb24bd4b878f3e29dddc544a7232c6140bd3fe Mon Sep 17 00:00:00 2001 From: Gustavo de Andrade Rosa Date: Tue, 21 Jul 2026 14:43:59 -0300 Subject: [PATCH] fix drive name normalization (#74) --- drive-health/collector.luau | 16 +++++++++++++--- drive-health/plugin.toml | 2 +- drive-health/scripts/collect_raw.sh | 2 +- drive-health/tests/collector_harness.lua | 16 +++++++++------- drive-health/tests/fixtures/bin/lsblk | 7 ++++--- drive-health/tests/test_collect_raw.sh | 2 +- 6 files changed, 29 insertions(+), 16 deletions(-) diff --git a/drive-health/collector.luau b/drive-health/collector.luau index c4d133f..c6ce183 100644 --- a/drive-health/collector.luau +++ b/drive-health/collector.luau @@ -118,6 +118,11 @@ local function smartDeviceFor(path) return controller ~= nil and "/dev/" .. controller or tostring(path) end +local function deviceBasename(value) + local normalized = tostring(value or ""):gsub("/+$", "") + return normalized:match("([^/]+)$") or "" +end + local function listDirectory(path) local entries = noctalia.listDir(path) return type(entries) == "table" and entries or {} @@ -765,8 +770,13 @@ local function normalizeRaw(raw, source) local disks = {} for _, block in ipairs(lsblk.blockdevices or {}) do - local device = tostring(block.path or ("/dev/" .. tostring(block.name or ""))) - local name = tostring(block.name or "") + local device = tostring(block.path or "") + local name = deviceBasename(block.name) + if name == "" then name = deviceBasename(block.kname) end + if name == "" then name = deviceBasename(device) end + if not device:match("^/dev/") and name ~= "" then device = "/dev/" .. name end + local kernelName = deviceBasename(block.kname) + if kernelName == "" then kernelName = name end local virtual = name:match("^zram") or name:match("^loop") or name:match("^ram") or name:match("^sr") if block.type == "disk" and device:match("^/dev/") and not virtual then local smartDevice = smartDeviceFor(device) @@ -789,7 +799,7 @@ local function normalizeRaw(raw, source) local model = noctalia.string.trim(tostring(block.model or "")) local namespace = name:match("^nvme%d+(n%d+)$") local id = serial ~= "" and serial .. (namespace ~= nil and ":" .. namespace or "") - or tostring(block.kname or device) + or (kernelName ~= "" and kernelName or device) local preferences = preferenceFor(id) local removable = block.rm == true or tonumber(block.rm) == 1 or block.hotplug == true or tonumber(block.hotplug) == 1 local transport = tostring(block.tran or "unknown") diff --git a/drive-health/plugin.toml b/drive-health/plugin.toml index 8ed7f41..58a3335 100644 --- a/drive-health/plugin.toml +++ b/drive-health/plugin.toml @@ -1,6 +1,6 @@ id = "gustav0ar/drive-health" name = "Drive Health" -version = "1.2.0" +version = "1.2.1" plugin_api = 3 author = "Drive Health contributors" license = "MIT" diff --git a/drive-health/scripts/collect_raw.sh b/drive-health/scripts/collect_raw.sh index c6bfae3..6fea26c 100755 --- a/drive-health/scripts/collect_raw.sh +++ b/drive-health/scripts/collect_raw.sh @@ -59,7 +59,7 @@ lsblk --nodeps --noheadings --paths --output PATH,TYPE,ROTA >"$devices_tmp" { printf '{"schema":2,"collector_version":"%s","collection_id":"%s","generated_at_epoch":%s,"lsblk":' \ "$collector_version" "$collection_id" "$generated_at_epoch" - lsblk --json --bytes --output \ + lsblk --json --bytes --paths --output \ NAME,KNAME,PATH,PKNAME,TYPE,TRAN,ROTA,RM,HOTPLUG,SIZE,LOG-SEC,PHY-SEC,MODEL,SERIAL,FSTYPE,FSSIZE,FSUSED,FSAVAIL,MOUNTPOINTS printf ',"smart":[' diff --git a/drive-health/tests/collector_harness.lua b/drive-health/tests/collector_harness.lua index f2678c4..c960e19 100644 --- a/drive-health/tests/collector_harness.lua +++ b/drive-health/tests/collector_harness.lua @@ -392,10 +392,10 @@ local normalized, normalizeError = normalizeRaw({ generated_at_epoch = 1700000000, lsblk = { blockdevices = { { - name = "nvme9n1", kname = "nvme9n1", path = "/dev/nvme9n1", type = "disk", + name = "/dev/nvme9n1", kname = "/dev/nvme9n1", path = "/dev/nvme9n1", type = "disk", tran = "nvme", rota = false, size = 2000000000, model = "Fixture NVMe", serial = "FIXTURE1", mountpoints = {}, children = { - { name = "nvme9n1p1", kname = "nvme9n1p1", path = "/dev/nvme9n1p1", type = "part", + { name = "/dev/nvme9n1p1", kname = "/dev/nvme9n1p1", path = "/dev/nvme9n1p1", type = "part", mountpoints = { "/mnt/work" }, fsused = 250, fsavail = 750 }, }, }, @@ -485,15 +485,17 @@ assert(sleeping.summary.sleeping_count == 1 and sleeping.summary.smart_unavailab local namespaces = assert(normalizeRaw({ schema = 2, generated_at_epoch = 1700000000, lsblk = { blockdevices = { - { name = "nvme0n1", kname = "nvme0n1", path = "/dev/nvme0n1", type = "disk", + { name = "/dev/nvme0n1", kname = "/dev/nvme0n1", path = "/dev/nvme0n1", type = "disk", tran = "nvme", rota = false, serial = "SHARED", children = {} }, - { name = "nvme0n2", kname = "nvme0n2", path = "/dev/nvme0n2", type = "disk", + { name = "/dev/nvme0n2", kname = "/dev/nvme0n2", path = "/dev/nvme0n2", type = "disk", tran = "nvme", rota = false, serial = "SHARED", children = {} }, + { name = "/dev/zram0", kname = "/dev/zram0", path = "/dev/zram0", type = "disk", + rota = false, serial = "VIRTUAL", children = {} }, } }, smart = {}, }, "test")) -assert(namespaces.disks[1].id ~= namespaces.disks[2].id - and namespaces.disks[1].id:match(":n%d+$") and namespaces.disks[2].id:match(":n%d+$"), - "NVMe namespaces sharing a controller serial did not receive unique IDs") +assert(#namespaces.disks == 2, "absolute zram name was not excluded from physical drive inventory") +assert(namespaces.disks[1].id == "SHARED:n1" and namespaces.disks[2].id == "SHARED:n2", + "absolute NVMe names did not receive stable namespace-qualified IDs") local empty = assert(normalizeRaw({ schema = 2, collection_id = " ", generated_at_epoch = 1700000000, diff --git a/drive-health/tests/fixtures/bin/lsblk b/drive-health/tests/fixtures/bin/lsblk index 845dc11..50a9396 100755 --- a/drive-health/tests/fixtures/bin/lsblk +++ b/drive-health/tests/fixtures/bin/lsblk @@ -3,12 +3,13 @@ set -eu case " $* " in *" --nodeps "*) - printf '%s\n' "/dev/sda disk 1" "/dev/nvme0n1 disk 0" + printf '%s\n' "/dev/sda disk 1" "/dev/nvme0n1 disk 0" "/dev/zram0 disk 0" ;; *" --json "*) printf '%s\n' '{"blockdevices":[' \ - '{"name":"sda","kname":"sda","path":"/dev/sda","type":"disk","tran":"sata","rota":true,"size":1000000000,"model":"Fixture SATA","serial":"SATA1","mountpoints":[]},' \ - '{"name":"nvme0n1","kname":"nvme0n1","path":"/dev/nvme0n1","type":"disk","tran":"nvme","rota":false,"size":2000000000,"model":"Fixture NVMe","serial":"NVME1","mountpoints":[]}' \ + '{"name":"/dev/sda","kname":"/dev/sda","path":"/dev/sda","type":"disk","tran":"sata","rota":true,"size":1000000000,"model":"Fixture SATA","serial":"SATA1","mountpoints":[]},' \ + '{"name":"/dev/nvme0n1","kname":"/dev/nvme0n1","path":"/dev/nvme0n1","type":"disk","tran":"nvme","rota":false,"size":2000000000,"model":"Fixture NVMe","serial":"NVME1","mountpoints":[]},' \ + '{"name":"/dev/zram0","kname":"/dev/zram0","path":"/dev/zram0","type":"disk","rota":false,"size":4294967296,"mountpoints":[]}' \ ']}' ;; *) diff --git a/drive-health/tests/test_collect_raw.sh b/drive-health/tests/test_collect_raw.sh index b6e0126..10487b5 100755 --- a/drive-health/tests/test_collect_raw.sh +++ b/drive-health/tests/test_collect_raw.sh @@ -9,7 +9,7 @@ printf '%s\n' "$payload" | jq -e ' .schema == 2 and .collector_version == "2.0.0" and (.collection_id | type == "string" and length > 0) - and (.lsblk.blockdevices | length) == 2 + and ([.lsblk.blockdevices[].name] | sort) == ["/dev/nvme0n1", "/dev/sda", "/dev/zram0"] and (.smart | length) == 2 and ([.smart[].requested_device] | sort) == ["/dev/nvme0", "/dev/sda"] and (.smart[] | select(.requested_device == "/dev/sda") | .payload.test_standby) == true