@daemonitor/plugins 1.1.19 → 1.1.21

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.
@@ -92,7 +92,7 @@ function decodeSnmp(s) {
92
92
  }
93
93
  async function snmpGet(t, oid, timeout = 8000) {
94
94
  try {
95
- const { stdout } = await pexec("snmpget", ["-v", t.version || "2c", "-c", t.community || "public", "-Ovq", "-t", "2", "-r", "1", t.ip, oid], { timeout });
95
+ const { stdout } = await pexec("snmpget", ["-v", t.version || "2c", "-c", t.community || "public", "-Oevq", "-t", "2", "-r", "1", t.ip, oid], { timeout });
96
96
  const raw = stdout.trim().replace(/^"|"$/g, "");
97
97
  if (!raw || /No Such|no response|Timeout/i.test(raw))
98
98
  return null;
@@ -104,7 +104,7 @@ async function snmpGet(t, oid, timeout = 8000) {
104
104
  }
105
105
  async function snmpWalk(t, oid, timeout = 10000) {
106
106
  try {
107
- const { stdout } = await pexec("snmpwalk", ["-v", t.version || "2c", "-c", t.community || "public", "-Oqv", "-t", "2", "-r", "1", t.ip, oid], { timeout });
107
+ const { stdout } = await pexec("snmpwalk", ["-v", t.version || "2c", "-c", t.community || "public", "-Oeqv", "-t", "2", "-r", "1", t.ip, oid], { timeout });
108
108
  return stdout.split("\n").map((l) => decodeSnmp(l.trim().replace(/^"|"$/g, ""))).filter(Boolean);
109
109
  }
110
110
  catch {
@@ -168,20 +168,38 @@ async function collectDevice(t) {
168
168
  snmpGet(t, SYS.descr),
169
169
  snmpGet(t, SYS.name),
170
170
  snmpGet(t, SYS.uptime),
171
- snmpWalk(t, SYS.ifOper),
172
- snmpWalk(t, SYS.ifAdmin),
171
+ snmpWalkIndexed(t, SYS.ifOper),
172
+ snmpWalkIndexed(t, SYS.ifAdmin),
173
173
  ]);
174
174
  if (descr == null && name == null && !oper.length) {
175
175
  return { name: t.name, ip: t.ip, kind: t.kind || "device", reachable: false };
176
176
  }
177
+ // Index the link-state walks by ifIndex so each interface can carry its own
178
+ // up/down (positional zipping breaks whenever the two walks differ in length).
179
+ const byIndex = (rows) => {
180
+ const m = {};
181
+ for (const r of rows)
182
+ m[r.idx] = r.val;
183
+ return m;
184
+ };
185
+ const operByIdx = byIndex(oper);
186
+ const adminByIdx = byIndex(admin);
187
+ // Values arrive numeric (-Oe), but tolerate an enum rendering like "up(1)"
188
+ // from any agent/MIB combination that slips one through.
189
+ const enumNum = (v) => {
190
+ if (v == null)
191
+ return undefined;
192
+ const m = /\((\d+)\)\s*$/.exec(v) || /^\s*(-?\d+)\s*$/.exec(v);
193
+ return m ? Number(m[1]) : undefined;
194
+ };
177
195
  // Only count ADMIN-enabled ports; an enabled port that's operationally down is
178
196
  // a real link-down (ignore the many disabled ports on a big switch).
179
197
  let portsTotal = 0;
180
198
  let portsUp = 0;
181
- for (let i = 0; i < oper.length; i++) {
182
- if (Number(admin[i]) === 1) {
199
+ for (const idx of Object.keys(adminByIdx)) {
200
+ if (enumNum(adminByIdx[idx]) === 1) {
183
201
  portsTotal++;
184
- if (Number(oper[i]) === 1)
202
+ if (enumNum(operByIdx[idx]) === 1)
185
203
  portsUp++;
186
204
  }
187
205
  }
@@ -231,15 +249,9 @@ async function collectDevice(t) {
231
249
  // "internet traffic". Without one, the headline is left undefined rather than
232
250
  // reporting a number we know to be wrong.
233
251
  const PSEUDO = /^(lo|imq\d*|itf\d*|ifb\d*|sit\d*|gre\d*|teql\d*|tunl\d*)$/i;
234
- const idxOf = (rows) => {
235
- const m = {};
236
- for (const r of rows)
237
- m[r.idx] = r.val;
238
- return m;
239
- };
240
- const nameByIdx = idxOf(ifNames);
241
- const inByIdx = idxOf(hcInIdx);
242
- const outByIdx = idxOf(hcOutIdx);
252
+ const nameByIdx = byIndex(ifNames);
253
+ const inByIdx = byIndex(hcInIdx);
254
+ const outByIdx = byIndex(hcOutIdx);
243
255
  const now = Date.now();
244
256
  const prevIf = netPrevIf[t.ip] || {};
245
257
  const nextIf = {};
@@ -261,7 +273,11 @@ async function collectDevice(t) {
261
273
  outBps = Math.round((cout - p.out) / dt);
262
274
  }
263
275
  }
264
- interfaces.push({ name, inBps, outBps });
276
+ // ifOperStatus 1=up, ifAdminStatus 1=enabled. A link that's admin-enabled but
277
+ // operationally down is a real fault; an admin-disabled port is just off.
278
+ const adminUp = adminByIdx[idx] != null ? enumNum(adminByIdx[idx]) === 1 : undefined;
279
+ const linkUp = operByIdx[idx] != null ? enumNum(operByIdx[idx]) === 1 : undefined;
280
+ interfaces.push({ name, inBps, outBps, up: linkUp, adminUp });
265
281
  }
266
282
  netPrevIf[t.ip] = nextIf;
267
283
  // Headline = the WAN link when the target names one.
@@ -298,7 +314,7 @@ async function collectDevice(t) {
298
314
  // hrStorage table entries for the data volumes.
299
315
  async function snmpWalkIndexed(t, oid, timeout = 10000) {
300
316
  try {
301
- const { stdout } = await pexec("snmpwalk", ["-v", t.version || "2c", "-c", t.community || "public", "-On", "-t", "2", "-r", "1", t.ip, oid], { timeout });
317
+ const { stdout } = await pexec("snmpwalk", ["-v", t.version || "2c", "-c", t.community || "public", "-Oen", "-t", "2", "-r", "1", t.ip, oid], { timeout });
302
318
  return stdout.split("\n").map((l) => {
303
319
  const m = /^(\S+)\s*=\s*[^:]+:\s*(.*)$/.exec(l.trim());
304
320
  if (!m)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daemonitor/plugins",
3
- "version": "1.1.19",
3
+ "version": "1.1.21",
4
4
  "description": "daemonitor plugins",
5
5
  "types": "types/index.d.ts",
6
6
  "main": "dist/index.js",