@daemonitor/plugins 1.1.7 → 1.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -39,8 +39,13 @@ const QNAP = {
|
|
|
39
39
|
hdNumber: "1.3.6.1.4.1.24681.1.2.10.0", // installed disk count
|
|
40
40
|
hdDescr: "1.3.6.1.4.1.24681.1.2.11.1.2.", // + index
|
|
41
41
|
hdTemp: "1.3.6.1.4.1.24681.1.2.11.1.3.",
|
|
42
|
-
hdStatus: "1.3.6.1.4.1.24681.1.2.11.1.4.", // 0 = ready/ok
|
|
42
|
+
hdStatus: "1.3.6.1.4.1.24681.1.2.11.1.4.", // 0 = ready/ok, negative = fault/empty
|
|
43
|
+
hdModel: "1.3.6.1.4.1.24681.1.2.11.1.5.", // "--" only when the bay is empty
|
|
44
|
+
hdCapacity: "1.3.6.1.4.1.24681.1.2.11.1.6.", // "7.28 TB" / "--"
|
|
43
45
|
hdSmart: "1.3.6.1.4.1.24681.1.2.11.1.7.", // "GOOD" / "Warning"
|
|
46
|
+
memTotal: "1.3.6.1.4.1.24681.1.2.2.0", // "16013.4 MB"
|
|
47
|
+
memFree: "1.3.6.1.4.1.24681.1.2.3.0", // "11984.9 MB"
|
|
48
|
+
fanSpeed: "1.3.6.1.4.1.24681.1.2.15.1.3.", // + index, "1835 RPM"
|
|
44
49
|
};
|
|
45
50
|
function slug(s) {
|
|
46
51
|
return s.replace(/[^a-zA-Z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -88,32 +93,38 @@ async function snmpWalk(t, oid, timeout = 10000) {
|
|
|
88
93
|
}
|
|
89
94
|
async function collectPrinter(t) {
|
|
90
95
|
const warnPct = t.tonerWarnPct ?? 10;
|
|
91
|
-
|
|
96
|
+
// Walk LEVELS (clean numeric values, one per row) to get the supply indices,
|
|
97
|
+
// then fetch each supply's description/max by that index — a single get whose
|
|
98
|
+
// (possibly multi-line hex) value decodeSnmp handles, so a long description
|
|
99
|
+
// can't be split into phantom supplies and misalign the levels.
|
|
100
|
+
const [model, serial, pagesRaw, statusRaw, levelRows] = await Promise.all([
|
|
92
101
|
snmpGet(t, OID.model),
|
|
93
102
|
snmpGet(t, OID.serial),
|
|
94
103
|
snmpGet(t, OID.pages),
|
|
95
104
|
snmpGet(t, OID.status),
|
|
96
|
-
|
|
97
|
-
snmpWalk(t, OID.supplyMax),
|
|
98
|
-
snmpWalk(t, OID.supplyLevel),
|
|
105
|
+
snmpWalkIndexed(t, OID.supplyLevel),
|
|
99
106
|
]);
|
|
100
107
|
// No response at all → the printer is unreachable / SNMP off.
|
|
101
|
-
if (model == null && serial == null && pagesRaw == null && !
|
|
108
|
+
if (model == null && serial == null && pagesRaw == null && !levelRows.length) {
|
|
102
109
|
return { name: t.name, ip: t.ip, kind: "printer", reachable: false };
|
|
103
110
|
}
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
const supplies =
|
|
108
|
-
const
|
|
109
|
-
|
|
111
|
+
// prtMarkerSuppliesLevel semantics: -2 = unknown, -3 = "some remaining" (a
|
|
112
|
+
// level the device won't quantify); else a count out of MaxCapacity (which can
|
|
113
|
+
// itself be -2 = unknown).
|
|
114
|
+
const supplies = await Promise.all(levelRows.map(async (row) => {
|
|
115
|
+
const [descRaw, maxRaw] = await Promise.all([
|
|
116
|
+
snmpGet(t, `${OID.supplyDesc}.${row.idx}`),
|
|
117
|
+
snmpGet(t, `${OID.supplyMax}.${row.idx}`),
|
|
118
|
+
]);
|
|
119
|
+
const lvl = Number(row.val);
|
|
120
|
+
const max = Number(maxRaw);
|
|
110
121
|
let pct = null;
|
|
111
122
|
if (lvl === -3)
|
|
112
|
-
pct = 100;
|
|
123
|
+
pct = 100;
|
|
113
124
|
else if (lvl >= 0 && max > 0)
|
|
114
125
|
pct = Math.round((lvl / max) * 100);
|
|
115
|
-
return { name:
|
|
116
|
-
});
|
|
126
|
+
return { name: descRaw || "supply", level: lvl, max, pct };
|
|
127
|
+
}));
|
|
117
128
|
const low = supplies.filter((s) => s.pct != null && s.pct <= warnPct);
|
|
118
129
|
return {
|
|
119
130
|
name: t.name,
|
|
@@ -193,13 +204,16 @@ async function snmpWalkIndexed(t, oid, timeout = 10000) {
|
|
|
193
204
|
async function collectQnap(t) {
|
|
194
205
|
const tempWarn = t.tempWarnC ?? 70;
|
|
195
206
|
const volWarn = t.volumeWarnPct ?? 90;
|
|
196
|
-
const [model, cpuTemp, sysTemp, hdNumRaw, cpuLoads, storDescrs] = await Promise.all([
|
|
207
|
+
const [model, cpuTemp, sysTemp, hdNumRaw, cpuLoads, storDescrs, memTotal, memFree, fanSpeeds] = await Promise.all([
|
|
197
208
|
snmpGet(t, QNAP.model),
|
|
198
209
|
snmpGet(t, QNAP.cpuTemp),
|
|
199
210
|
snmpGet(t, QNAP.sysTemp),
|
|
200
211
|
snmpGet(t, QNAP.hdNumber),
|
|
201
212
|
snmpWalk(t, "1.3.6.1.2.1.25.3.3.1.2"), // hrProcessorLoad (per core)
|
|
202
213
|
snmpWalkIndexed(t, "1.3.6.1.2.1.25.2.3.1.3"), // hrStorageDescr
|
|
214
|
+
snmpGet(t, QNAP.memTotal),
|
|
215
|
+
snmpGet(t, QNAP.memFree),
|
|
216
|
+
snmpWalk(t, "1.3.6.1.4.1.24681.1.2.15.1.3"), // SystemFanSpeed table
|
|
203
217
|
]);
|
|
204
218
|
if (model == null && cpuTemp == null && !cpuLoads.length) {
|
|
205
219
|
return { name: t.name, ip: t.ip, kind: "qnap", reachable: false };
|
|
@@ -207,20 +221,32 @@ async function collectQnap(t) {
|
|
|
207
221
|
// CPU: average per-core load.
|
|
208
222
|
const cores = cpuLoads.map(Number).filter((n) => !isNaN(n));
|
|
209
223
|
const cpuPct = cores.length ? Math.round(cores.reduce((a, b) => a + b, 0) / cores.length) : undefined;
|
|
210
|
-
//
|
|
224
|
+
// RAM used %. Values look like "16013.4 MB".
|
|
225
|
+
const memTotalMB = parseNum(memTotal), memFreeMB = parseNum(memFree);
|
|
226
|
+
const memPct = memTotalMB && memTotalMB > 0 && memFreeMB != null
|
|
227
|
+
? Math.round(((memTotalMB - memFreeMB) / memTotalMB) * 100) : undefined;
|
|
228
|
+
// Fans — each entry looks like "1835 RPM".
|
|
229
|
+
const fans = fanSpeeds.map((f) => parseNum(f)).filter((n) => n != null && n > 0);
|
|
230
|
+
// Disks — iterate 1..hdNumber (bounded). Presence is judged by MODEL/CAPACITY:
|
|
231
|
+
// an empty bay reports model "--", but a FAILING disk can report an unreadable
|
|
232
|
+
// temp ("-- C/-- F") + negative status yet still advertise a real model. The old
|
|
233
|
+
// temp+status heuristic silently skipped those bad disks (e.g. an HDD in SMART
|
|
234
|
+
// "Warning" with status -9) — so the NAS looked healthy while QTS flagged it.
|
|
211
235
|
const hdN = Math.min(Number(hdNumRaw) || 0, 24);
|
|
212
236
|
const disks = [];
|
|
213
237
|
for (let i = 1; i <= hdN; i++) {
|
|
214
|
-
const [st, tp, sm, ds] = await Promise.all([
|
|
215
|
-
snmpGet(t, QNAP.hdStatus + i), snmpGet(t, QNAP.hdTemp + i), snmpGet(t, QNAP.hdSmart + i),
|
|
238
|
+
const [st, tp, sm, ds, md, cap] = await Promise.all([
|
|
239
|
+
snmpGet(t, QNAP.hdStatus + i), snmpGet(t, QNAP.hdTemp + i), snmpGet(t, QNAP.hdSmart + i),
|
|
240
|
+
snmpGet(t, QNAP.hdDescr + i), snmpGet(t, QNAP.hdModel + i), snmpGet(t, QNAP.hdCapacity + i),
|
|
216
241
|
]);
|
|
242
|
+
const model = md && md !== "--" ? md : undefined;
|
|
243
|
+
const capacity = cap && cap !== "--" ? cap : undefined;
|
|
244
|
+
// Only a truly empty bay (no model AND no capacity) is skipped.
|
|
245
|
+
if (!model && !capacity)
|
|
246
|
+
continue;
|
|
217
247
|
const status = st != null ? Number(st) : undefined;
|
|
218
248
|
const tempC = parseC(tp);
|
|
219
|
-
|
|
220
|
-
// status. Skip it — an empty slot isn't a failed disk.
|
|
221
|
-
if (tempC == null && (status == null || status < 0))
|
|
222
|
-
continue;
|
|
223
|
-
disks.push({ slot: i, descr: ds || undefined, tempC, status, smart: sm && sm !== "--" ? sm : undefined });
|
|
249
|
+
disks.push({ slot: i, descr: ds || undefined, model, capacity, tempC, status, smart: sm && sm !== "--" ? sm : undefined });
|
|
224
250
|
}
|
|
225
251
|
// A present disk is bad only on a real error status, an explicitly-bad SMART
|
|
226
252
|
// verdict, or overheating — not a blank "--" or an empty bay.
|
|
@@ -242,7 +268,10 @@ async function collectQnap(t) {
|
|
|
242
268
|
name: t.name, ip: t.ip, kind: "qnap", reachable: true,
|
|
243
269
|
model: model || undefined,
|
|
244
270
|
cpuPct, cpuTempC: parseC(cpuTemp), sysTempC: parseC(sysTemp),
|
|
245
|
-
|
|
271
|
+
memPct, memTotalMB: memTotalMB || undefined,
|
|
272
|
+
fans, fanRpm: fans.length ? Math.min(...fans) : undefined,
|
|
273
|
+
disks, diskCount: disks.length,
|
|
274
|
+
badDisks: badDisks.map((d) => ({ slot: d.slot, descr: d.descr, model: d.model, status: d.status, smart: d.smart, tempC: d.tempC })),
|
|
246
275
|
volumes, fullVolumes: fullVols,
|
|
247
276
|
tempWarnC: tempWarn, volumeWarnPct: volWarn,
|
|
248
277
|
};
|