@glassmkr/crucible 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +92 -0
- package/config/collector.example.yaml +43 -0
- package/dist/alerts/evaluator.d.ts +3 -0
- package/dist/alerts/evaluator.js +14 -0
- package/dist/alerts/evaluator.js.map +1 -0
- package/dist/alerts/rules.d.ts +7 -0
- package/dist/alerts/rules.js +203 -0
- package/dist/alerts/rules.js.map +1 -0
- package/dist/alerts/state.d.ts +6 -0
- package/dist/alerts/state.js +77 -0
- package/dist/alerts/state.js.map +1 -0
- package/dist/collect/cpu.d.ts +2 -0
- package/dist/collect/cpu.js +35 -0
- package/dist/collect/cpu.js.map +1 -0
- package/dist/collect/disks.d.ts +2 -0
- package/dist/collect/disks.js +33 -0
- package/dist/collect/disks.js.map +1 -0
- package/dist/collect/ipmi.d.ts +2 -0
- package/dist/collect/ipmi.js +55 -0
- package/dist/collect/ipmi.js.map +1 -0
- package/dist/collect/memory.d.ts +2 -0
- package/dist/collect/memory.js +27 -0
- package/dist/collect/memory.js.map +1 -0
- package/dist/collect/network.d.ts +2 -0
- package/dist/collect/network.js +54 -0
- package/dist/collect/network.js.map +1 -0
- package/dist/collect/os-alerts.d.ts +2 -0
- package/dist/collect/os-alerts.js +41 -0
- package/dist/collect/os-alerts.js.map +1 -0
- package/dist/collect/raid.d.ts +2 -0
- package/dist/collect/raid.js +34 -0
- package/dist/collect/raid.js.map +1 -0
- package/dist/collect/smart.d.ts +2 -0
- package/dist/collect/smart.js +56 -0
- package/dist/collect/smart.js.map +1 -0
- package/dist/collect/system.d.ts +2 -0
- package/dist/collect/system.js +19 -0
- package/dist/collect/system.js.map +1 -0
- package/dist/config.d.ts +208 -0
- package/dist/config.js +58 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +96 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/exec.d.ts +1 -0
- package/dist/lib/exec.js +19 -0
- package/dist/lib/exec.js.map +1 -0
- package/dist/lib/parse.d.ts +4 -0
- package/dist/lib/parse.js +29 -0
- package/dist/lib/parse.js.map +1 -0
- package/dist/lib/types.d.ts +103 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/notify/email.d.ts +4 -0
- package/dist/notify/email.js +55 -0
- package/dist/notify/email.js.map +1 -0
- package/dist/notify/slack.d.ts +2 -0
- package/dist/notify/slack.js +38 -0
- package/dist/notify/slack.js.map +1 -0
- package/dist/notify/telegram.d.ts +2 -0
- package/dist/notify/telegram.js +38 -0
- package/dist/notify/telegram.js.map +1 -0
- package/dist/push/forge.d.ts +2 -0
- package/dist/push/forge.js +26 -0
- package/dist/push/forge.js.map +1 -0
- package/package.json +29 -0
- package/src/alerts/evaluator.ts +15 -0
- package/src/alerts/rules.ts +184 -0
- package/src/alerts/state.ts +92 -0
- package/src/collect/cpu.ts +44 -0
- package/src/collect/disks.ts +36 -0
- package/src/collect/ipmi.ts +60 -0
- package/src/collect/memory.ts +30 -0
- package/src/collect/network.ts +61 -0
- package/src/collect/os-alerts.ts +43 -0
- package/src/collect/raid.ts +40 -0
- package/src/collect/smart.ts +60 -0
- package/src/collect/system.ts +21 -0
- package/src/config.ts +60 -0
- package/src/index.ts +112 -0
- package/src/lib/exec.ts +16 -0
- package/src/lib/parse.ts +29 -0
- package/src/lib/types.ts +110 -0
- package/src/notify/email.ts +68 -0
- package/src/notify/slack.ts +46 -0
- package/src/notify/telegram.ts +45 -0
- package/src/push/forge.ts +25 -0
- package/tsconfig.json +15 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Glassmkr
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Glassmkr Collector
|
|
2
|
+
|
|
3
|
+
Lightweight bare metal server monitoring. Collects hardware and OS health data, evaluates 15 opinionated alert rules, sends notifications directly (Telegram, Slack, email), and optionally pushes to [Forge](https://forge.glassmkr.com) for dashboard and history.
|
|
4
|
+
|
|
5
|
+
Runs as a systemd service. Wakes up every 5 minutes, collects everything, alerts if needed, sleeps.
|
|
6
|
+
|
|
7
|
+
## Quick Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
curl -sf https://forge.glassmkr.com/install | bash
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or manual:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g @glassmkr/collector
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## What It Monitors
|
|
20
|
+
|
|
21
|
+
**Hardware (IPMI):** CPU temperature, ECC memory errors, PSU redundancy
|
|
22
|
+
**Storage:** SMART health, NVMe wear, RAID status, disk space, I/O latency
|
|
23
|
+
**Network:** Interface errors/drops, link speed, saturation
|
|
24
|
+
**OS:** RAM pressure, swap usage, CPU iowait, OOM kills, disk space
|
|
25
|
+
|
|
26
|
+
## 15 Alert Rules
|
|
27
|
+
|
|
28
|
+
| # | Alert | Severity | Default Threshold |
|
|
29
|
+
|---|-------|----------|-------------------|
|
|
30
|
+
| 1 | RAM usage high | Warning/Critical | 90% / 95% |
|
|
31
|
+
| 2 | Swap active | Warning | Any usage |
|
|
32
|
+
| 3 | Disk space high | Warning/Critical | 85% / 95% |
|
|
33
|
+
| 4 | CPU iowait high | Warning | 20% |
|
|
34
|
+
| 5 | OOM kills detected | Critical | Any |
|
|
35
|
+
| 6 | SMART failure | Critical | Bad health/sectors |
|
|
36
|
+
| 7 | NVMe wear high | Warning/Critical | 85% / 95% |
|
|
37
|
+
| 8 | RAID degraded | Critical | Any degradation |
|
|
38
|
+
| 9 | Disk latency high | Warning | 50ms NVMe, 200ms HDD |
|
|
39
|
+
| 10 | Interface errors | Warning | Any errors/drops |
|
|
40
|
+
| 11 | Link speed mismatch | Warning | Below 1 Gbps |
|
|
41
|
+
| 12 | Interface saturation | Warning | 90% utilization |
|
|
42
|
+
| 13 | CPU temperature high | Warning/Critical | 80C / 90C |
|
|
43
|
+
| 14 | ECC memory errors | Warning/Critical | Correctable / Uncorrectable |
|
|
44
|
+
| 15 | PSU redundancy loss | Critical | Any PSU failed/absent |
|
|
45
|
+
|
|
46
|
+
## Configuration
|
|
47
|
+
|
|
48
|
+
Edit `/etc/glassmkr/collector.yaml`:
|
|
49
|
+
|
|
50
|
+
```yaml
|
|
51
|
+
server_name: "web-01"
|
|
52
|
+
collection:
|
|
53
|
+
interval_seconds: 300
|
|
54
|
+
ipmi: true
|
|
55
|
+
smart: true
|
|
56
|
+
forge:
|
|
57
|
+
enabled: true
|
|
58
|
+
url: "https://forge.glassmkr.com"
|
|
59
|
+
api_key: "col_xxx"
|
|
60
|
+
thresholds:
|
|
61
|
+
ram_percent: 90
|
|
62
|
+
disk_percent: 85
|
|
63
|
+
channels:
|
|
64
|
+
telegram:
|
|
65
|
+
enabled: true
|
|
66
|
+
bot_token: "123:abc"
|
|
67
|
+
chat_id: "123456"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
See [config/collector.example.yaml](config/collector.example.yaml) for all options.
|
|
71
|
+
|
|
72
|
+
## Requirements
|
|
73
|
+
|
|
74
|
+
- Linux (Ubuntu 22.04/24.04, Debian 11/12)
|
|
75
|
+
- Node.js 22+
|
|
76
|
+
- Root access
|
|
77
|
+
- Optional: smartmontools (for SMART monitoring)
|
|
78
|
+
- Optional: ipmitool (for IPMI/BMC monitoring)
|
|
79
|
+
|
|
80
|
+
## Notification Channels
|
|
81
|
+
|
|
82
|
+
**Telegram:** Provide bot token and chat ID. Create a bot via @BotFather.
|
|
83
|
+
**Email:** Uses local sendmail. Install postfix or msmtp.
|
|
84
|
+
**Slack:** Provide an incoming webhook URL.
|
|
85
|
+
|
|
86
|
+
## Forge Integration
|
|
87
|
+
|
|
88
|
+
Optional. Register at [forge.glassmkr.com](https://forge.glassmkr.com), add a server, get an API key. The collector pushes health snapshots for dashboard display and history.
|
|
89
|
+
|
|
90
|
+
## License
|
|
91
|
+
|
|
92
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Glassmkr Collector Configuration
|
|
2
|
+
# Copy to /etc/glassmkr/collector.yaml
|
|
3
|
+
|
|
4
|
+
# Server identity
|
|
5
|
+
server_name: "my-server"
|
|
6
|
+
|
|
7
|
+
# Collection settings
|
|
8
|
+
collection:
|
|
9
|
+
interval_seconds: 300 # How often to collect (default 5 minutes)
|
|
10
|
+
ipmi: true # Collect IPMI data (requires ipmitool)
|
|
11
|
+
smart: true # Collect SMART data (requires smartmontools)
|
|
12
|
+
|
|
13
|
+
# Forge integration (optional dashboard)
|
|
14
|
+
forge:
|
|
15
|
+
enabled: false
|
|
16
|
+
url: "https://forge.glassmkr.com"
|
|
17
|
+
api_key: "" # Get this from forge.glassmkr.com after registering a server
|
|
18
|
+
|
|
19
|
+
# Alert thresholds (all optional, sensible defaults used if omitted)
|
|
20
|
+
thresholds:
|
|
21
|
+
ram_percent: 90 # Alert when RAM usage exceeds this
|
|
22
|
+
swap_alert: true # Alert on any swap usage
|
|
23
|
+
disk_percent: 85 # Alert when any disk exceeds this
|
|
24
|
+
iowait_percent: 20 # Alert when CPU iowait exceeds this
|
|
25
|
+
nvme_wear_percent: 85 # Alert when NVMe lifetime wear exceeds this
|
|
26
|
+
disk_latency_nvme_ms: 50 # p99 latency threshold for NVMe
|
|
27
|
+
disk_latency_hdd_ms: 200 # p99 latency threshold for HDD
|
|
28
|
+
cpu_temp_warning_c: 80 # CPU temperature warning
|
|
29
|
+
cpu_temp_critical_c: 90 # CPU temperature critical
|
|
30
|
+
interface_utilization_percent: 90 # Network saturation threshold
|
|
31
|
+
|
|
32
|
+
# Notification channels (all optional)
|
|
33
|
+
channels:
|
|
34
|
+
telegram:
|
|
35
|
+
enabled: false
|
|
36
|
+
bot_token: ""
|
|
37
|
+
chat_id: ""
|
|
38
|
+
email:
|
|
39
|
+
enabled: false
|
|
40
|
+
to: ""
|
|
41
|
+
slack:
|
|
42
|
+
enabled: false
|
|
43
|
+
webhook_url: ""
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { allRules } from "./rules.js";
|
|
2
|
+
export function evaluateAlerts(snapshot, thresholds) {
|
|
3
|
+
const results = [];
|
|
4
|
+
for (const rule of allRules) {
|
|
5
|
+
try {
|
|
6
|
+
results.push(...rule.evaluate(snapshot, thresholds));
|
|
7
|
+
}
|
|
8
|
+
catch (err) {
|
|
9
|
+
console.error(`[alerts] Rule ${rule.type} error:`, err);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return results;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=evaluator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluator.js","sourceRoot":"","sources":["../../src/alerts/evaluator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAItC,MAAM,UAAU,cAAc,CAAC,QAAkB,EAAE,UAAgC;IACjF,MAAM,OAAO,GAAkB,EAAE,CAAC;IAClC,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,CAAC;YACH,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC;QACvD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,iBAAiB,IAAI,CAAC,IAAI,SAAS,EAAE,GAAG,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Snapshot, AlertResult } from "../lib/types.js";
|
|
2
|
+
import type { Config } from "../config.js";
|
|
3
|
+
export interface AlertRule {
|
|
4
|
+
type: string;
|
|
5
|
+
evaluate(snap: Snapshot, thresholds: Config["thresholds"]): AlertResult[];
|
|
6
|
+
}
|
|
7
|
+
export declare const allRules: AlertRule[];
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
// Alert rules for the collector are identical to the Forge evaluator.
|
|
2
|
+
// Re-export from a shared definition to avoid duplication.
|
|
3
|
+
// For the collector, we use the same 15 rules but with local thresholds from config.
|
|
4
|
+
export const allRules = [
|
|
5
|
+
// 1. RAM high
|
|
6
|
+
{ type: "ram_high", evaluate(snap, t) {
|
|
7
|
+
if (!snap.memory?.total_mb)
|
|
8
|
+
return [];
|
|
9
|
+
const pct = (snap.memory.used_mb / snap.memory.total_mb) * 100;
|
|
10
|
+
if (pct < (t.ram_percent ?? 90))
|
|
11
|
+
return [];
|
|
12
|
+
return [{ type: "ram_high", severity: pct >= 95 ? "critical" : "warning",
|
|
13
|
+
title: `RAM usage at ${pct.toFixed(1)}%`,
|
|
14
|
+
message: `Using ${snap.memory.used_mb}MB of ${snap.memory.total_mb}MB. ${snap.memory.available_mb}MB available.`,
|
|
15
|
+
evidence: { used_mb: snap.memory.used_mb, total_mb: snap.memory.total_mb, percent: Math.round(pct * 10) / 10 },
|
|
16
|
+
recommendation: "Check: ps aux --sort=-rss | head -20" }];
|
|
17
|
+
} },
|
|
18
|
+
// 2. Swap active
|
|
19
|
+
{ type: "swap_active", evaluate(snap, t) {
|
|
20
|
+
if (t.swap_alert === false || !snap.memory || snap.memory.swap_used_mb <= 0)
|
|
21
|
+
return [];
|
|
22
|
+
return [{ type: "swap_active", severity: "warning", title: `Swap in use: ${snap.memory.swap_used_mb}MB`,
|
|
23
|
+
message: "Server is using swap space, indicating memory pressure.",
|
|
24
|
+
evidence: { swap_used_mb: snap.memory.swap_used_mb },
|
|
25
|
+
recommendation: "Check: free -h && ps aux --sort=-rss | head -20" }];
|
|
26
|
+
} },
|
|
27
|
+
// 3. Disk space high
|
|
28
|
+
{ type: "disk_space_high", evaluate(snap, t) {
|
|
29
|
+
if (!snap.disks)
|
|
30
|
+
return [];
|
|
31
|
+
const threshold = t.disk_percent ?? 85;
|
|
32
|
+
return snap.disks.filter(d => d.percent_used >= threshold).map(d => ({
|
|
33
|
+
type: "disk_space_high", severity: d.percent_used >= 95 ? "critical" : "warning",
|
|
34
|
+
title: `Disk ${d.mount} at ${d.percent_used}%`,
|
|
35
|
+
message: `${d.device}: ${d.used_gb}GB of ${d.total_gb}GB used. ${d.available_gb}GB available.`,
|
|
36
|
+
evidence: { device: d.device, mount: d.mount, percent_used: d.percent_used },
|
|
37
|
+
recommendation: "Check: du -sh /* | sort -rh | head -20"
|
|
38
|
+
}));
|
|
39
|
+
} },
|
|
40
|
+
// 4. CPU iowait
|
|
41
|
+
{ type: "cpu_iowait_high", evaluate(snap, t) {
|
|
42
|
+
if (!snap.cpu || snap.cpu.iowait_percent < (t.iowait_percent ?? 20))
|
|
43
|
+
return [];
|
|
44
|
+
return [{ type: "cpu_iowait_high", severity: "warning", title: `CPU iowait at ${snap.cpu.iowait_percent.toFixed(1)}%`,
|
|
45
|
+
message: `High I/O wait: CPU spending ${snap.cpu.iowait_percent.toFixed(1)}% waiting for disk.`,
|
|
46
|
+
evidence: { iowait_percent: snap.cpu.iowait_percent },
|
|
47
|
+
recommendation: "Check: iotop -oP or iostat -x 1 5" }];
|
|
48
|
+
} },
|
|
49
|
+
// 5. OOM kills
|
|
50
|
+
{ type: "oom_kills", evaluate(snap) {
|
|
51
|
+
if (!snap.os_alerts || snap.os_alerts.oom_kills_recent <= 0)
|
|
52
|
+
return [];
|
|
53
|
+
return [{ type: "oom_kills", severity: "critical", title: `${snap.os_alerts.oom_kills_recent} OOM kill(s)`,
|
|
54
|
+
message: `Kernel OOM killer terminated ${snap.os_alerts.oom_kills_recent} process(es).`,
|
|
55
|
+
evidence: { oom_kills_recent: snap.os_alerts.oom_kills_recent },
|
|
56
|
+
recommendation: "Check: dmesg | grep -i 'out of memory'" }];
|
|
57
|
+
} },
|
|
58
|
+
// 6. SMART failing
|
|
59
|
+
{ type: "smart_failing", evaluate(snap) {
|
|
60
|
+
if (!snap.smart)
|
|
61
|
+
return [];
|
|
62
|
+
return snap.smart.filter(d => d.health !== "PASSED" || (d.reallocated_sectors && d.reallocated_sectors > 0) || (d.pending_sectors && d.pending_sectors > 0))
|
|
63
|
+
.map(d => ({ type: "smart_failing", severity: "critical",
|
|
64
|
+
title: `SMART failure: ${d.device}`, message: `${d.model}: drive showing signs of failure.`,
|
|
65
|
+
evidence: { device: d.device, health: d.health, reallocated_sectors: d.reallocated_sectors, pending_sectors: d.pending_sectors },
|
|
66
|
+
recommendation: `Back up data. Schedule replacement for ${d.device}.` }));
|
|
67
|
+
} },
|
|
68
|
+
// 7. NVMe wear
|
|
69
|
+
{ type: "nvme_wear_high", evaluate(snap, t) {
|
|
70
|
+
if (!snap.smart)
|
|
71
|
+
return [];
|
|
72
|
+
const threshold = t.nvme_wear_percent ?? 85;
|
|
73
|
+
return snap.smart.filter(d => d.percentage_used != null && d.percentage_used >= threshold)
|
|
74
|
+
.map(d => ({ type: "nvme_wear_high", severity: d.percentage_used >= 95 ? "critical" : "warning",
|
|
75
|
+
title: `NVMe ${d.device} wear at ${d.percentage_used}%`, message: `${d.model} at ${d.percentage_used}% lifetime wear.`,
|
|
76
|
+
evidence: { device: d.device, percentage_used: d.percentage_used },
|
|
77
|
+
recommendation: "Plan drive replacement." }));
|
|
78
|
+
} },
|
|
79
|
+
// 8. RAID degraded
|
|
80
|
+
{ type: "raid_degraded", evaluate(snap) {
|
|
81
|
+
if (!snap.raid)
|
|
82
|
+
return [];
|
|
83
|
+
return snap.raid.filter(r => r.degraded || r.failed_disks.length > 0)
|
|
84
|
+
.map(r => ({ type: "raid_degraded", severity: "critical",
|
|
85
|
+
title: `RAID ${r.device} degraded`, message: `${r.device} (${r.level}) degraded. Failed: ${r.failed_disks.join(", ") || "unknown"}.`,
|
|
86
|
+
evidence: { device: r.device, failed_disks: r.failed_disks },
|
|
87
|
+
recommendation: "Replace failed drive immediately." }));
|
|
88
|
+
} },
|
|
89
|
+
// 9. Disk latency
|
|
90
|
+
{ type: "disk_latency_high", evaluate(snap, t) {
|
|
91
|
+
if (!snap.disks)
|
|
92
|
+
return [];
|
|
93
|
+
return snap.disks.filter(d => {
|
|
94
|
+
if (d.latency_p99_ms == null)
|
|
95
|
+
return false;
|
|
96
|
+
const thresh = d.device.includes("nvme") ? (t.disk_latency_nvme_ms ?? 50) : (t.disk_latency_hdd_ms ?? 200);
|
|
97
|
+
return d.latency_p99_ms >= thresh;
|
|
98
|
+
}).map(d => ({ type: "disk_latency_high", severity: "warning",
|
|
99
|
+
title: `Disk ${d.device} latency ${d.latency_p99_ms.toFixed(1)}ms`,
|
|
100
|
+
message: `p99 I/O latency on ${d.device} is high.`,
|
|
101
|
+
evidence: { device: d.device, latency_p99_ms: d.latency_p99_ms },
|
|
102
|
+
recommendation: "Check: iotop -oP" }));
|
|
103
|
+
} },
|
|
104
|
+
// 10. Interface errors
|
|
105
|
+
{ type: "interface_errors", evaluate(snap) {
|
|
106
|
+
if (!snap.network)
|
|
107
|
+
return [];
|
|
108
|
+
return snap.network.filter(i => (i.rx_errors + i.tx_errors + i.rx_drops + i.tx_drops) > 0)
|
|
109
|
+
.map(i => ({ type: "interface_errors", severity: "warning",
|
|
110
|
+
title: `${i.interface}: errors/drops detected`,
|
|
111
|
+
message: `RX errors=${i.rx_errors}, TX errors=${i.tx_errors}, RX drops=${i.rx_drops}, TX drops=${i.tx_drops}.`,
|
|
112
|
+
evidence: { interface: i.interface, rx_errors: i.rx_errors, tx_errors: i.tx_errors, rx_drops: i.rx_drops, tx_drops: i.tx_drops },
|
|
113
|
+
recommendation: "Check cables and SFP/transceiver." }));
|
|
114
|
+
} },
|
|
115
|
+
// 11. Link speed mismatch
|
|
116
|
+
{ type: "link_speed_mismatch", evaluate(snap) {
|
|
117
|
+
if (!snap.network)
|
|
118
|
+
return [];
|
|
119
|
+
return snap.network.filter(i => i.speed_mbps > 0 && i.speed_mbps < 1000)
|
|
120
|
+
.map(i => ({ type: "link_speed_mismatch", severity: "warning",
|
|
121
|
+
title: `${i.interface} at ${i.speed_mbps} Mbps`,
|
|
122
|
+
message: `Interface negotiated below 1 Gbps.`,
|
|
123
|
+
evidence: { interface: i.interface, speed_mbps: i.speed_mbps },
|
|
124
|
+
recommendation: "Check cable, SFP, switch port config." }));
|
|
125
|
+
} },
|
|
126
|
+
// 12. Interface saturation
|
|
127
|
+
{ type: "interface_saturation", evaluate(snap, t) {
|
|
128
|
+
if (!snap.network)
|
|
129
|
+
return [];
|
|
130
|
+
const threshold = (t.interface_utilization_percent ?? 90) / 100;
|
|
131
|
+
return snap.network.filter(i => {
|
|
132
|
+
if (!i.speed_mbps)
|
|
133
|
+
return false;
|
|
134
|
+
const maxBps = (i.speed_mbps * 1_000_000) / 8;
|
|
135
|
+
return Math.max(i.rx_bytes_sec, i.tx_bytes_sec) / maxBps >= threshold;
|
|
136
|
+
}).map(i => {
|
|
137
|
+
const maxBps = (i.speed_mbps * 1_000_000) / 8;
|
|
138
|
+
const util = Math.max(i.rx_bytes_sec, i.tx_bytes_sec) / maxBps * 100;
|
|
139
|
+
return { type: "interface_saturation", severity: "warning",
|
|
140
|
+
title: `${i.interface} at ${util.toFixed(0)}% utilization`,
|
|
141
|
+
message: `Interface ${i.interface} (${i.speed_mbps} Mbps) near saturation.`,
|
|
142
|
+
evidence: { interface: i.interface, utilization_percent: Math.round(util * 10) / 10 },
|
|
143
|
+
recommendation: "Check: iftop or nload" };
|
|
144
|
+
});
|
|
145
|
+
} },
|
|
146
|
+
// 13. CPU temperature
|
|
147
|
+
{ type: "cpu_temperature_high", evaluate(snap, t) {
|
|
148
|
+
if (!snap.ipmi?.available || !snap.ipmi.sensors)
|
|
149
|
+
return [];
|
|
150
|
+
const warn = t.cpu_temp_warning_c ?? 80;
|
|
151
|
+
return snap.ipmi.sensors.filter(s => {
|
|
152
|
+
const n = s.name.toLowerCase();
|
|
153
|
+
if (!n.includes("cpu") && !n.includes("temp"))
|
|
154
|
+
return false;
|
|
155
|
+
const v = typeof s.value === "number" ? s.value : parseFloat(String(s.value));
|
|
156
|
+
return !isNaN(v) && v >= warn;
|
|
157
|
+
}).map(s => {
|
|
158
|
+
const v = typeof s.value === "number" ? s.value : parseFloat(String(s.value));
|
|
159
|
+
const crit = s.upper_critical ?? (t.cpu_temp_critical_c ?? 90);
|
|
160
|
+
return { type: "cpu_temperature_high", severity: v >= crit ? "critical" : "warning",
|
|
161
|
+
title: `${s.name}: ${v}${s.unit}`, message: `Temperature above warning threshold.`,
|
|
162
|
+
evidence: { sensor: s.name, value: v },
|
|
163
|
+
recommendation: "Check cooling, fans, airflow." };
|
|
164
|
+
});
|
|
165
|
+
} },
|
|
166
|
+
// 14. ECC errors
|
|
167
|
+
{ type: "ecc_errors", evaluate(snap) {
|
|
168
|
+
if (!snap.ipmi?.ecc_errors)
|
|
169
|
+
return [];
|
|
170
|
+
const { correctable, uncorrectable } = snap.ipmi.ecc_errors;
|
|
171
|
+
if (correctable <= 0 && uncorrectable <= 0)
|
|
172
|
+
return [];
|
|
173
|
+
if (uncorrectable > 0)
|
|
174
|
+
return [{ type: "ecc_errors", severity: "critical",
|
|
175
|
+
title: `${uncorrectable} uncorrectable ECC error(s)`, message: "Data corruption possible. DIMM failing.",
|
|
176
|
+
evidence: { correctable, uncorrectable },
|
|
177
|
+
recommendation: "Replace DIMM immediately. Run: ipmitool sdr type Memory" }];
|
|
178
|
+
return [{ type: "ecc_errors", severity: "warning",
|
|
179
|
+
title: `${correctable} correctable ECC error(s)`, message: "Early warning of DIMM failure.",
|
|
180
|
+
evidence: { correctable, uncorrectable },
|
|
181
|
+
recommendation: "Schedule DIMM replacement. Run: ipmitool sdr type Memory" }];
|
|
182
|
+
} },
|
|
183
|
+
// 15. PSU redundancy
|
|
184
|
+
{ type: "psu_redundancy_loss", evaluate(snap) {
|
|
185
|
+
if (!snap.ipmi?.available || !snap.ipmi.sensors)
|
|
186
|
+
return [];
|
|
187
|
+
const psus = snap.ipmi.sensors.filter(s => { const n = s.name.toLowerCase(); return n.includes("psu") || n.includes("power supply"); });
|
|
188
|
+
if (psus.length < 2)
|
|
189
|
+
return [];
|
|
190
|
+
const failed = psus.filter(s => {
|
|
191
|
+
const st = String(s.status).toLowerCase();
|
|
192
|
+
const v = String(s.value).toLowerCase();
|
|
193
|
+
return st.includes("fail") || st.includes("absent") || v.includes("fail") || v.includes("absent");
|
|
194
|
+
});
|
|
195
|
+
if (failed.length === 0)
|
|
196
|
+
return [];
|
|
197
|
+
return [{ type: "psu_redundancy_loss", severity: "critical",
|
|
198
|
+
title: "PSU redundancy lost", message: `${failed.length} PSU(s) failed/absent: ${failed.map(p => p.name).join(", ")}.`,
|
|
199
|
+
evidence: { failed: failed.map(p => ({ name: p.name, status: p.status })) },
|
|
200
|
+
recommendation: "Replace failed PSU. Check power connections." }];
|
|
201
|
+
} },
|
|
202
|
+
];
|
|
203
|
+
//# sourceMappingURL=rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/alerts/rules.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
2
|
+
const STATE_FILE = "/var/lib/glassmkr/alert-state.json";
|
|
3
|
+
let state = new Map();
|
|
4
|
+
function load() {
|
|
5
|
+
try {
|
|
6
|
+
const raw = readFileSync(STATE_FILE, "utf-8");
|
|
7
|
+
const data = JSON.parse(raw);
|
|
8
|
+
state = new Map(Object.entries(data));
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
state = new Map();
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function save() {
|
|
15
|
+
try {
|
|
16
|
+
mkdirSync("/var/lib/glassmkr", { recursive: true });
|
|
17
|
+
const obj = {};
|
|
18
|
+
for (const [k, v] of state)
|
|
19
|
+
obj[k] = v;
|
|
20
|
+
writeFileSync(STATE_FILE, JSON.stringify(obj, null, 2));
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
console.error("[state] Failed to save alert state:", err);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Initialize on import
|
|
27
|
+
load();
|
|
28
|
+
export function updateAlertState(currentAlerts) {
|
|
29
|
+
const now = new Date().toISOString();
|
|
30
|
+
const currentTypes = new Set(currentAlerts.map((a) => a.type));
|
|
31
|
+
const newAlerts = [];
|
|
32
|
+
const resolvedAlerts = [];
|
|
33
|
+
// Check for new alerts
|
|
34
|
+
for (const alert of currentAlerts) {
|
|
35
|
+
const existing = state.get(alert.type);
|
|
36
|
+
if (!existing) {
|
|
37
|
+
// New alert
|
|
38
|
+
state.set(alert.type, { type: alert.type, first_seen: now, last_seen: now, notified: false });
|
|
39
|
+
newAlerts.push(alert);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// Existing alert, update last_seen
|
|
43
|
+
existing.last_seen = now;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Check for resolved alerts
|
|
47
|
+
for (const [type, alertState] of state) {
|
|
48
|
+
if (!currentTypes.has(type)) {
|
|
49
|
+
resolvedAlerts.push({
|
|
50
|
+
type,
|
|
51
|
+
severity: "warning",
|
|
52
|
+
title: `Resolved: ${type}`,
|
|
53
|
+
message: `Condition cleared. Active for ${timeSince(alertState.first_seen)}.`,
|
|
54
|
+
evidence: {},
|
|
55
|
+
recommendation: "",
|
|
56
|
+
});
|
|
57
|
+
state.delete(type);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
save();
|
|
61
|
+
return { newAlerts, resolvedAlerts };
|
|
62
|
+
}
|
|
63
|
+
function timeSince(isoDate) {
|
|
64
|
+
const ms = Date.now() - new Date(isoDate).getTime();
|
|
65
|
+
const minutes = Math.floor(ms / 60000);
|
|
66
|
+
if (minutes < 60)
|
|
67
|
+
return `${minutes} minute(s)`;
|
|
68
|
+
const hours = Math.floor(minutes / 60);
|
|
69
|
+
if (hours < 24)
|
|
70
|
+
return `${hours} hour(s) ${minutes % 60} minute(s)`;
|
|
71
|
+
const days = Math.floor(hours / 24);
|
|
72
|
+
return `${days} day(s)`;
|
|
73
|
+
}
|
|
74
|
+
export function getActiveAlerts() {
|
|
75
|
+
return Array.from(state.keys());
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/alerts/state.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { readProcFile, sleep } from "../lib/parse.js";
|
|
2
|
+
function parseProcStat() {
|
|
3
|
+
const raw = readProcFile("/proc/stat") || "";
|
|
4
|
+
const line = raw.split("\n").find((l) => l.startsWith("cpu "));
|
|
5
|
+
if (!line)
|
|
6
|
+
return { user: 0, nice: 0, system: 0, idle: 0, iowait: 0, irq: 0, softirq: 0, steal: 0 };
|
|
7
|
+
const parts = line.split(/\s+/).slice(1).map(Number);
|
|
8
|
+
return {
|
|
9
|
+
user: parts[0] || 0, nice: parts[1] || 0, system: parts[2] || 0, idle: parts[3] || 0,
|
|
10
|
+
iowait: parts[4] || 0, irq: parts[5] || 0, softirq: parts[6] || 0, steal: parts[7] || 0,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export async function collectCpu() {
|
|
14
|
+
const stat1 = parseProcStat();
|
|
15
|
+
await sleep(1000);
|
|
16
|
+
const stat2 = parseProcStat();
|
|
17
|
+
const d = {
|
|
18
|
+
user: stat2.user - stat1.user, nice: stat2.nice - stat1.nice,
|
|
19
|
+
system: stat2.system - stat1.system, idle: stat2.idle - stat1.idle,
|
|
20
|
+
iowait: stat2.iowait - stat1.iowait, irq: stat2.irq - stat1.irq,
|
|
21
|
+
softirq: stat2.softirq - stat1.softirq, steal: stat2.steal - stat1.steal,
|
|
22
|
+
};
|
|
23
|
+
const total = Object.values(d).reduce((a, b) => a + b, 0) || 1;
|
|
24
|
+
const loadavg = (readProcFile("/proc/loadavg") || "0 0 0").trim().split(" ");
|
|
25
|
+
return {
|
|
26
|
+
user_percent: Math.round((d.user / total) * 10000) / 100,
|
|
27
|
+
system_percent: Math.round((d.system / total) * 10000) / 100,
|
|
28
|
+
iowait_percent: Math.round((d.iowait / total) * 10000) / 100,
|
|
29
|
+
idle_percent: Math.round((d.idle / total) * 10000) / 100,
|
|
30
|
+
load_1m: parseFloat(loadavg[0]) || 0,
|
|
31
|
+
load_5m: parseFloat(loadavg[1]) || 0,
|
|
32
|
+
load_15m: parseFloat(loadavg[2]) || 0,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=cpu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cpu.js","sourceRoot":"","sources":["../../src/collect/cpu.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { run } from "../lib/exec.js";
|
|
2
|
+
export async function collectDisks() {
|
|
3
|
+
const dfOutput = await run("df", ["-B1", "--output=source,target,size,used,avail,pcent", "-x", "tmpfs", "-x", "devtmpfs", "-x", "squashfs"]);
|
|
4
|
+
if (!dfOutput)
|
|
5
|
+
return [];
|
|
6
|
+
const lines = dfOutput.trim().split("\n").slice(1); // skip header
|
|
7
|
+
const disks = [];
|
|
8
|
+
for (const line of lines) {
|
|
9
|
+
const parts = line.trim().split(/\s+/);
|
|
10
|
+
if (parts.length < 6)
|
|
11
|
+
continue;
|
|
12
|
+
const device = parts[0];
|
|
13
|
+
const mount = parts[1];
|
|
14
|
+
const totalBytes = parseInt(parts[2]) || 0;
|
|
15
|
+
const usedBytes = parseInt(parts[3]) || 0;
|
|
16
|
+
const availBytes = parseInt(parts[4]) || 0;
|
|
17
|
+
const pctStr = parts[5].replace("%", "");
|
|
18
|
+
const percent = parseInt(pctStr) || 0;
|
|
19
|
+
// Skip pseudo-filesystems
|
|
20
|
+
if (!device.startsWith("/dev/"))
|
|
21
|
+
continue;
|
|
22
|
+
disks.push({
|
|
23
|
+
device,
|
|
24
|
+
mount,
|
|
25
|
+
total_gb: Math.round((totalBytes / 1073741824) * 100) / 100,
|
|
26
|
+
used_gb: Math.round((usedBytes / 1073741824) * 100) / 100,
|
|
27
|
+
available_gb: Math.round((availBytes / 1073741824) * 100) / 100,
|
|
28
|
+
percent_used: percent,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return disks;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=disks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disks.js","sourceRoot":"","sources":["../../src/collect/disks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAGrC,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,8CAA8C,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;IAC7I,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAC;IAEzB,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc;IAClE,MAAM,KAAK,GAAe,EAAE,CAAC;IAE7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACvC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,SAAS;QAC/B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACvB,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3C,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAEtC,0BAA0B;QAC1B,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC;YAAE,SAAS;QAE1C,KAAK,CAAC,IAAI,CAAC;YACT,MAAM;YACN,KAAK;YACL,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,UAAU,GAAG,UAAU,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG;YAC3D,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,UAAU,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG;YACzD,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,UAAU,GAAG,UAAU,CAAC,GAAG,GAAG,CAAC,GAAG,GAAG;YAC/D,YAAY,EAAE,OAAO;SACtB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|