fix(checkmk): per-host state lookup via /objects/host/{name}, remove batch collection call
This commit is contained in:
73
server.ts
73
server.ts
@ -740,51 +740,13 @@ async function startServer() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2: fetch live monitoring state for all hosts in one call.
|
// Step 2: for each device, look up its CheckMK hostname by IP, then query the
|
||||||
let hostnameToState: Map<string, number>;
|
// individual monitoring object at /objects/host/{name} for the live state.
|
||||||
try {
|
// The collection endpoint (/domain-types/host/collections/all) only returns
|
||||||
const monRes = await fetch(
|
// minimal fields without state, so per-host calls are required.
|
||||||
`${CHECKMK_API_URL}/domain-types/host/collections/all`,
|
|
||||||
{ headers }
|
|
||||||
);
|
|
||||||
if (!monRes.ok) throw new Error(checkmkHttpHint(monRes.status));
|
|
||||||
const monData = await monRes.json();
|
|
||||||
|
|
||||||
// Log raw response so we can see the actual structure (first 500 chars).
|
|
||||||
db.prepare('INSERT INTO logs (id, timestamp, type, message) VALUES (?, ?, ?, ?)')
|
|
||||||
.run(uid('log'), now, 'system',
|
|
||||||
`CheckMK monitoring raw response: ${JSON.stringify(monData).substring(0, 500)}`);
|
|
||||||
|
|
||||||
hostnameToState = new Map<string, number>();
|
|
||||||
// Try multiple possible container keys used by different CheckMK versions.
|
|
||||||
const candidates: any[] = Array.isArray(monData?.value)
|
|
||||||
? monData.value
|
|
||||||
: Array.isArray(monData?.members)
|
|
||||||
? monData.members.map((m: any) => m?.value ?? m)
|
|
||||||
: [];
|
|
||||||
for (const host of candidates) {
|
|
||||||
const name: string | undefined = host?.id ?? host?.extensions?.name;
|
|
||||||
const state: number | undefined = host?.extensions?.state;
|
|
||||||
if (name !== undefined && state !== undefined) hostnameToState.set(name, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hostnameToState.size === 0 && ipToHostname.size > 0) {
|
|
||||||
db.prepare('INSERT INTO logs (id, timestamp, type, message) VALUES (?, ?, ?, ?)')
|
|
||||||
.run(uid('log'), now, 'system',
|
|
||||||
`CheckMK: parsed 0 monitoring hosts from response (candidates array length: ${candidates.length}). ` +
|
|
||||||
'Check the raw response entry above to identify the correct JSON path.');
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
const msg = `CheckMK sync failed — could not fetch monitoring states: ${err?.message ?? err}`;
|
|
||||||
console.error('[CheckMK]', msg);
|
|
||||||
db.prepare('INSERT INTO logs (id, timestamp, type, message) VALUES (?, ?, ?, ?)')
|
|
||||||
.run(uid('log'), now, 'system', msg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 3: update each device based on IP lookup + live state
|
|
||||||
const rows = db.prepare('SELECT id, hostname, ip, status FROM devices').all() as { id: string; hostname: string; ip: string; status: string }[];
|
const rows = db.prepare('SELECT id, hostname, ip, status FROM devices').all() as { id: string; hostname: string; ip: string; status: string }[];
|
||||||
const counts = { online: 0, offline: 0, unknown: 0 };
|
const counts = { online: 0, offline: 0, unknown: 0 };
|
||||||
|
let loggedFirstExtensions = false;
|
||||||
|
|
||||||
for (const dev of rows) {
|
for (const dev of rows) {
|
||||||
const cmkHost = ipToHostname.get(dev.ip);
|
const cmkHost = ipToHostname.get(dev.ip);
|
||||||
@ -797,7 +759,23 @@ async function startServer() {
|
|||||||
counts.unknown++;
|
counts.unknown++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const state = hostnameToState.get(cmkHost);
|
try {
|
||||||
|
const hostRes = await fetch(
|
||||||
|
`${CHECKMK_API_URL}/objects/host/${encodeURIComponent(cmkHost)}`,
|
||||||
|
{ headers }
|
||||||
|
);
|
||||||
|
if (!hostRes.ok) throw new Error(checkmkHttpHint(hostRes.status));
|
||||||
|
const hostData = await hostRes.json();
|
||||||
|
|
||||||
|
// Log raw extensions once so the state field path can be verified.
|
||||||
|
if (!loggedFirstExtensions) {
|
||||||
|
loggedFirstExtensions = true;
|
||||||
|
db.prepare('INSERT INTO logs (id, timestamp, type, message) VALUES (?, ?, ?, ?)')
|
||||||
|
.run(uid('log'), now, 'system',
|
||||||
|
`CheckMK per-host raw extensions for "${cmkHost}": ${JSON.stringify(hostData?.extensions ?? hostData).substring(0, 400)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const state: number = hostData?.extensions?.state ?? -1;
|
||||||
const newStatus = state === 0 ? 'online' : state === 1 || state === 2 ? 'offline' : 'unknown';
|
const newStatus = state === 0 ? 'online' : state === 1 || state === 2 ? 'offline' : 'unknown';
|
||||||
db.prepare('UPDATE devices SET status = ?, lastCheckedAt = ? WHERE id = ?').run(newStatus, now, dev.id);
|
db.prepare('UPDATE devices SET status = ?, lastCheckedAt = ? WHERE id = ?').run(newStatus, now, dev.id);
|
||||||
if (dev.status !== newStatus) {
|
if (dev.status !== newStatus) {
|
||||||
@ -805,6 +783,13 @@ async function startServer() {
|
|||||||
.run(uid('log'), now, 'status', `CheckMK: ${dev.hostname} (${dev.ip}) status changed to ${newStatus} (was: ${dev.status}).`, dev.id);
|
.run(uid('log'), now, 'status', `CheckMK: ${dev.hostname} (${dev.ip}) status changed to ${newStatus} (was: ${dev.status}).`, dev.id);
|
||||||
}
|
}
|
||||||
counts[newStatus as 'online' | 'offline' | 'unknown']++;
|
counts[newStatus as 'online' | 'offline' | 'unknown']++;
|
||||||
|
} catch (err: any) {
|
||||||
|
const msg = `CheckMK: status sync failed for ${dev.hostname} (${dev.ip}) — ${err?.message ?? err}`;
|
||||||
|
console.error('[CheckMK]', msg);
|
||||||
|
db.prepare('INSERT INTO logs (id, timestamp, type, message, deviceId) VALUES (?, ?, ?, ?, ?)')
|
||||||
|
.run(uid('log'), now, 'system', msg, dev.id);
|
||||||
|
counts.unknown++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
db.prepare('INSERT INTO logs (id, timestamp, type, message) VALUES (?, ?, ?, ?)')
|
db.prepare('INSERT INTO logs (id, timestamp, type, message) VALUES (?, ?, ?, ?)')
|
||||||
|
|||||||
Reference in New Issue
Block a user