fix(checkmk): per-host state lookup via /objects/host/{name}, remove batch collection call

This commit is contained in:
Brückner
2026-06-04 15:27:21 +02:00
parent 15c4e3f6ac
commit 985178ea84

View File

@ -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,14 +759,37 @@ async function startServer() {
counts.unknown++; counts.unknown++;
continue; continue;
} }
const state = hostnameToState.get(cmkHost); try {
const newStatus = state === 0 ? 'online' : state === 1 || state === 2 ? 'offline' : 'unknown'; const hostRes = await fetch(
db.prepare('UPDATE devices SET status = ?, lastCheckedAt = ? WHERE id = ?').run(newStatus, now, dev.id); `${CHECKMK_API_URL}/objects/host/${encodeURIComponent(cmkHost)}`,
if (dev.status !== newStatus) { { 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';
db.prepare('UPDATE devices SET status = ?, lastCheckedAt = ? WHERE id = ?').run(newStatus, now, dev.id);
if (dev.status !== newStatus) {
db.prepare('INSERT INTO logs (id, timestamp, type, message, deviceId) VALUES (?, ?, ?, ?, ?)')
.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']++;
} 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 (?, ?, ?, ?, ?)') db.prepare('INSERT INTO logs (id, timestamp, type, message, deviceId) VALUES (?, ?, ?, ?, ?)')
.run(uid('log'), now, 'status', `CheckMK: ${dev.hostname} (${dev.ip}) status changed to ${newStatus} (was: ${dev.status}).`, dev.id); .run(uid('log'), now, 'system', msg, dev.id);
counts.unknown++;
} }
counts[newStatus as 'online' | 'offline' | 'unknown']++;
} }
db.prepare('INSERT INTO logs (id, timestamp, type, message) VALUES (?, ?, ?, ?)') db.prepare('INSERT INTO logs (id, timestamp, type, message) VALUES (?, ?, ?, ?)')