@glassmkr/crucible 0.5.0 → 0.6.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/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
4
4
  [![npm version](https://img.shields.io/npm/v/@glassmkr/crucible.svg)](https://www.npmjs.com/package/@glassmkr/crucible)
5
5
 
6
- Lightweight bare metal server monitoring agent. Collects hardware and OS health data, pushes snapshots to [Forge](https://forge.glassmkr.com) every 5 minutes. Forge evaluates 30 alert rules and sends notifications.
6
+ Lightweight bare metal server monitoring agent. Collects hardware and OS health data, pushes snapshots to [Forge](https://forge.glassmkr.com) every 5 minutes. Forge evaluates 36 alert rules and sends notifications.
7
7
 
8
8
  Open source. MIT licensed. Built by [Glassmkr](https://glassmkr.com).
9
9
 
@@ -34,9 +34,9 @@ npm install -g @glassmkr/crucible
34
34
 
35
35
  ## Alert Rules
36
36
 
37
- Crucible collects the data. Forge evaluates 30 alert rules server-side and sends notifications via Telegram and Slack.
37
+ Crucible collects the data. Forge evaluates 36 alert rules server-side and sends notifications via Telegram and Slack.
38
38
 
39
- **Categories:** OS (5), Storage (8), Network (3), Hardware/IPMI (6), ZFS (2), Security (6).
39
+ **Categories:** OS (9), Storage (8), Network (4), Hardware/IPMI (5), ZFS (2), Security (6), Service Health (2).
40
40
 
41
41
  **Priorities:** P1 Urgent, P2 High, P3 Medium, P4 Low.
42
42
 
@@ -79,7 +79,7 @@ Full configuration reference: [forge.glassmkr.com/docs/configuration](https://fo
79
79
 
80
80
  - [Getting Started](https://forge.glassmkr.com/docs/getting-started)
81
81
  - [Configuration Reference](https://forge.glassmkr.com/docs/configuration)
82
- - [Alert Rules (30)](https://forge.glassmkr.com/docs/alerts)
82
+ - [Alert Rules (36)](https://forge.glassmkr.com/docs/alerts)
83
83
  - [Troubleshooting](https://forge.glassmkr.com/docs/troubleshooting)
84
84
  - [API Reference](https://forge.glassmkr.com/docs/api)
85
85
 
@@ -0,0 +1,7 @@
1
+ export interface ConntrackData {
2
+ available: boolean;
3
+ count: number;
4
+ max: number;
5
+ percent: number;
6
+ }
7
+ export declare function collectConntrack(): ConntrackData;
@@ -0,0 +1,16 @@
1
+ import { readProcFile } from "../lib/parse.js";
2
+ export function collectConntrack() {
3
+ const countRaw = readProcFile("/proc/sys/net/netfilter/nf_conntrack_count");
4
+ const maxRaw = readProcFile("/proc/sys/net/netfilter/nf_conntrack_max");
5
+ if (!countRaw || !maxRaw) {
6
+ return { available: false, count: 0, max: 0, percent: 0 };
7
+ }
8
+ const count = parseInt(countRaw.trim(), 10);
9
+ const max = parseInt(maxRaw.trim(), 10);
10
+ if (isNaN(count) || isNaN(max) || max === 0) {
11
+ return { available: false, count: 0, max: 0, percent: 0 };
12
+ }
13
+ const percent = Math.round(((count / max) * 100) * 10) / 10;
14
+ return { available: true, count, max, percent };
15
+ }
16
+ //# sourceMappingURL=conntrack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conntrack.js","sourceRoot":"","sources":["../../src/collect/conntrack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAS/C,MAAM,UAAU,gBAAgB;IAC9B,MAAM,QAAQ,GAAG,YAAY,CAAC,4CAA4C,CAAC,CAAC;IAC5E,MAAM,MAAM,GAAG,YAAY,CAAC,0CAA0C,CAAC,CAAC;IAExE,IAAI,CAAC,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;QACzB,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAC5D,CAAC;IAED,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IAC5C,MAAM,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IAExC,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC;QAC5C,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAC5D,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;IAC5D,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;AAClD,CAAC"}
@@ -0,0 +1,7 @@
1
+ export interface FileDescriptorData {
2
+ allocated: number;
3
+ free: number;
4
+ max: number;
5
+ percent: number;
6
+ }
7
+ export declare function collectFileDescriptors(): FileDescriptorData;
@@ -0,0 +1,20 @@
1
+ import { readProcFile } from "../lib/parse.js";
2
+ export function collectFileDescriptors() {
3
+ const raw = readProcFile("/proc/sys/fs/file-nr");
4
+ if (!raw) {
5
+ return { allocated: 0, free: 0, max: 0, percent: 0 };
6
+ }
7
+ const parts = raw.trim().split(/\s+/);
8
+ if (parts.length < 3) {
9
+ return { allocated: 0, free: 0, max: 0, percent: 0 };
10
+ }
11
+ const allocated = parseInt(parts[0], 10);
12
+ const free = parseInt(parts[1], 10);
13
+ const max = parseInt(parts[2], 10);
14
+ if (isNaN(allocated) || isNaN(max) || max === 0) {
15
+ return { allocated: 0, free: 0, max: 0, percent: 0 };
16
+ }
17
+ const percent = Math.round(((allocated / max) * 100) * 10) / 10;
18
+ return { allocated, free: isNaN(free) ? 0 : free, max, percent };
19
+ }
20
+ //# sourceMappingURL=fd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fd.js","sourceRoot":"","sources":["../../src/collect/fd.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAS/C,MAAM,UAAU,sBAAsB;IACpC,MAAM,GAAG,GAAG,YAAY,CAAC,sBAAsB,CAAC,CAAC;IACjD,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IACvD,CAAC;IAED,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACtC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IACvD,CAAC;IAED,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACpC,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEnC,IAAI,KAAK,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC;QAChD,OAAO,EAAE,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IACvD,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC;IAChE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;AACnE,CAAC"}
@@ -0,0 +1,8 @@
1
+ export interface IoLatencyInfo {
2
+ device: string;
3
+ avg_read_latency_ms: number | null;
4
+ avg_write_latency_ms: number | null;
5
+ read_iops: number;
6
+ write_iops: number;
7
+ }
8
+ export declare function collectIoLatency(): IoLatencyInfo[];
@@ -0,0 +1,80 @@
1
+ import { readProcFile } from "../lib/parse.js";
2
+ // Previous cumulative counters for delta computation
3
+ const previousCounters = new Map();
4
+ // Match physical block devices, not partitions or virtual devices
5
+ function isPhysicalDevice(name) {
6
+ // sd*, vd*, xvd* without trailing partition number
7
+ if (/^(sd|vd|xvd)[a-z]+$/.test(name))
8
+ return true;
9
+ // nvme*n* without partition suffix (nvme0n1 yes, nvme0n1p1 no)
10
+ if (/^nvme\d+n\d+$/.test(name))
11
+ return true;
12
+ // md* (RAID arrays)
13
+ if (/^md\d+$/.test(name))
14
+ return true;
15
+ return false;
16
+ }
17
+ function parseDiskstats() {
18
+ const raw = readProcFile("/proc/diskstats") || "";
19
+ const result = {};
20
+ for (const line of raw.split("\n")) {
21
+ const parts = line.trim().split(/\s+/);
22
+ if (parts.length < 11)
23
+ continue;
24
+ const name = parts[2];
25
+ if (!isPhysicalDevice(name))
26
+ continue;
27
+ result[name] = {
28
+ reads_completed: Number(parts[3]) || 0,
29
+ read_time_ms: Number(parts[6]) || 0,
30
+ writes_completed: Number(parts[7]) || 0,
31
+ write_time_ms: Number(parts[10]) || 0,
32
+ };
33
+ }
34
+ return result;
35
+ }
36
+ function delta(current, previous) {
37
+ if (current >= previous)
38
+ return current - previous;
39
+ return current; // counter wrapped or reset
40
+ }
41
+ export function collectIoLatency() {
42
+ const current = parseDiskstats();
43
+ const results = [];
44
+ const currentDevices = new Set();
45
+ for (const [name, counters] of Object.entries(current)) {
46
+ currentDevices.add(name);
47
+ const prev = previousCounters.get(name);
48
+ // Store current for next cycle
49
+ previousCounters.set(name, { ...counters });
50
+ if (!prev) {
51
+ // First cycle: no delta, report null latency
52
+ results.push({
53
+ device: name,
54
+ avg_read_latency_ms: null,
55
+ avg_write_latency_ms: null,
56
+ read_iops: 0,
57
+ write_iops: 0,
58
+ });
59
+ continue;
60
+ }
61
+ const deltaReads = delta(counters.reads_completed, prev.reads_completed);
62
+ const deltaReadTime = delta(counters.read_time_ms, prev.read_time_ms);
63
+ const deltaWrites = delta(counters.writes_completed, prev.writes_completed);
64
+ const deltaWriteTime = delta(counters.write_time_ms, prev.write_time_ms);
65
+ results.push({
66
+ device: name,
67
+ avg_read_latency_ms: deltaReads > 0 ? Math.round((deltaReadTime / deltaReads) * 100) / 100 : null,
68
+ avg_write_latency_ms: deltaWrites > 0 ? Math.round((deltaWriteTime / deltaWrites) * 100) / 100 : null,
69
+ read_iops: deltaReads,
70
+ write_iops: deltaWrites,
71
+ });
72
+ }
73
+ // Remove stale devices
74
+ for (const name of previousCounters.keys()) {
75
+ if (!currentDevices.has(name))
76
+ previousCounters.delete(name);
77
+ }
78
+ return results;
79
+ }
80
+ //# sourceMappingURL=io-latency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"io-latency.js","sourceRoot":"","sources":["../../src/collect/io-latency.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export interface NtpData {
2
+ synced: boolean;
3
+ offset_seconds: number;
4
+ source: string;
5
+ daemon_running: boolean;
6
+ }
7
+ export declare function collectNtp(): Promise<NtpData>;
@@ -0,0 +1,84 @@
1
+ import { run } from "../lib/exec.js";
2
+ export async function collectNtp() {
3
+ // Try timedatectl first (systemd-timesyncd)
4
+ const tdctl = await run("timedatectl", ["show", "--property=NTPSynchronized", "--value"]);
5
+ if (tdctl !== null) {
6
+ const synced = tdctl.trim() === "yes";
7
+ // Get the source daemon name
8
+ const statusOut = await run("timedatectl", ["show", "--property=NTP", "--value"]);
9
+ const ntpEnabled = statusOut?.trim() === "yes";
10
+ // Try to get offset from timedatectl timesync-status
11
+ let offset = 0;
12
+ try {
13
+ const tsStatus = await run("timedatectl", ["timesync-status"]);
14
+ if (tsStatus) {
15
+ const match = tsStatus.match(/Offset:\s*([+-]?\d+\.?\d*)(us|ms|s)/);
16
+ if (match) {
17
+ const val = parseFloat(match[1]);
18
+ const unit = match[2];
19
+ if (unit === "us")
20
+ offset = val / 1_000_000;
21
+ else if (unit === "ms")
22
+ offset = val / 1000;
23
+ else
24
+ offset = val;
25
+ }
26
+ }
27
+ }
28
+ catch { /* offset stays 0 */ }
29
+ return {
30
+ synced,
31
+ offset_seconds: offset,
32
+ source: "systemd-timesyncd",
33
+ daemon_running: ntpEnabled || synced,
34
+ };
35
+ }
36
+ // Try chrony
37
+ const chronyOut = await run("chronyc", ["tracking"]);
38
+ if (chronyOut) {
39
+ const leapMatch = chronyOut.match(/Leap status\s*:\s*(.+)/);
40
+ const synced = leapMatch ? leapMatch[1].trim() === "Normal" : false;
41
+ let offset = 0;
42
+ const offsetMatch = chronyOut.match(/Last offset\s*:\s*([+-]?\d+\.?\d*)\s*seconds/);
43
+ if (offsetMatch)
44
+ offset = parseFloat(offsetMatch[1]);
45
+ return {
46
+ synced,
47
+ offset_seconds: Math.abs(offset),
48
+ source: "chrony",
49
+ daemon_running: true,
50
+ };
51
+ }
52
+ // Try ntpq
53
+ const ntpqOut = await run("ntpq", ["-pn"]);
54
+ if (ntpqOut) {
55
+ // A line starting with * means a selected peer (synced)
56
+ const synced = ntpqOut.split("\n").some((line) => line.startsWith("*"));
57
+ let offset = 0;
58
+ // Parse offset from the selected peer line
59
+ const selectedLine = ntpqOut.split("\n").find((line) => line.startsWith("*"));
60
+ if (selectedLine) {
61
+ const fields = selectedLine.trim().split(/\s+/);
62
+ // offset is typically field 8 (in ms)
63
+ if (fields.length >= 9) {
64
+ const rawOffset = parseFloat(fields[8]);
65
+ if (!isNaN(rawOffset))
66
+ offset = Math.abs(rawOffset) / 1000;
67
+ }
68
+ }
69
+ return {
70
+ synced,
71
+ offset_seconds: offset,
72
+ source: "ntpd",
73
+ daemon_running: true,
74
+ };
75
+ }
76
+ // No time sync daemon found
77
+ return {
78
+ synced: false,
79
+ offset_seconds: 0,
80
+ source: "none",
81
+ daemon_running: false,
82
+ };
83
+ }
84
+ //# sourceMappingURL=ntp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ntp.js","sourceRoot":"","sources":["../../src/collect/ntp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export interface SystemdData {
2
+ failed_units: string[];
3
+ failed_count: number;
4
+ }
5
+ export declare function collectSystemd(extraExcludes?: string[]): Promise<SystemdData>;
@@ -0,0 +1,23 @@
1
+ import { run } from "../lib/exec.js";
2
+ // Units commonly in failed state by design or misconfiguration
3
+ const DEFAULT_EXCLUDES = [
4
+ "systemd-networkd-wait-online.service",
5
+ ];
6
+ export async function collectSystemd(extraExcludes = []) {
7
+ const output = await run("systemctl", [
8
+ "list-units", "--type=service", "--state=failed", "--no-legend", "--plain",
9
+ ]);
10
+ if (!output || output.trim() === "") {
11
+ return { failed_units: [], failed_count: 0 };
12
+ }
13
+ const excludes = new Set([...DEFAULT_EXCLUDES, ...extraExcludes]);
14
+ const units = [];
15
+ for (const line of output.trim().split("\n")) {
16
+ const unit = line.trim().split(/\s+/)[0];
17
+ if (unit && unit.endsWith(".service") && !excludes.has(unit)) {
18
+ units.push(unit);
19
+ }
20
+ }
21
+ return { failed_units: units, failed_count: units.length };
22
+ }
23
+ //# sourceMappingURL=systemd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"systemd.js","sourceRoot":"","sources":["../../src/collect/systemd.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAOrC,+DAA+D;AAC/D,MAAM,gBAAgB,GAAG;IACvB,sCAAsC;CACvC,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,gBAA0B,EAAE;IAC/D,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,WAAW,EAAE;QACpC,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,aAAa,EAAE,SAAS;KAC3E,CAAC,CAAC;IAEH,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACpC,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;IAC/C,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,gBAAgB,EAAE,GAAG,aAAa,CAAC,CAAC,CAAC;IAClE,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACzC,IAAI,IAAI,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7D,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;AAC7D,CAAC"}
package/dist/index.js CHANGED
@@ -33,6 +33,11 @@ import { pushToForge, initForgeAgent } from "./push/forge.js";
33
33
  import { collectSecurity } from "./collect/security.js";
34
34
  import { collectZfs } from "./collect/zfs.js";
35
35
  import { collectIoErrors } from "./collect/io-errors.js";
36
+ import { collectIoLatency } from "./collect/io-latency.js";
37
+ import { collectConntrack } from "./collect/conntrack.js";
38
+ import { collectSystemd } from "./collect/systemd.js";
39
+ import { collectNtp } from "./collect/ntp.js";
40
+ import { collectFileDescriptors } from "./collect/fd.js";
36
41
  const configPath = process.argv[2] || "/etc/glassmkr/collector.yaml";
37
42
  const config = loadConfig(configPath);
38
43
  console.log(`[collector] Starting. Server: ${config.server_name}. Interval: ${config.collection.interval_seconds}s`);
@@ -92,6 +97,26 @@ async function collect() {
92
97
  snapshot.io_errors = await collectIoErrors() ?? undefined;
93
98
  }
94
99
  catch { /* skip on error */ }
100
+ try {
101
+ snapshot.io_latency = collectIoLatency();
102
+ }
103
+ catch { /* skip on error */ }
104
+ try {
105
+ snapshot.conntrack = collectConntrack();
106
+ }
107
+ catch { /* skip on error */ }
108
+ try {
109
+ snapshot.systemd = await collectSystemd();
110
+ }
111
+ catch { /* skip on error */ }
112
+ try {
113
+ snapshot.ntp = await collectNtp();
114
+ }
115
+ catch { /* skip on error */ }
116
+ try {
117
+ snapshot.file_descriptors = collectFileDescriptors();
118
+ }
119
+ catch { /* skip on error */ }
95
120
  // Update Prometheus metrics
96
121
  updateMetrics(snapshot);
97
122
  // Evaluate alerts
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -16,6 +16,39 @@ export interface Snapshot {
16
16
  count: number;
17
17
  devices: string[];
18
18
  };
19
+ io_latency?: Array<{
20
+ device: string;
21
+ avg_read_latency_ms: number | null;
22
+ avg_write_latency_ms: number | null;
23
+ read_iops: number;
24
+ write_iops: number;
25
+ }>;
26
+ conntrack?: ConntrackData;
27
+ systemd?: SystemdData;
28
+ ntp?: NtpData;
29
+ file_descriptors?: FileDescriptorData;
30
+ }
31
+ export interface ConntrackData {
32
+ available: boolean;
33
+ count: number;
34
+ max: number;
35
+ percent: number;
36
+ }
37
+ export interface SystemdData {
38
+ failed_units: string[];
39
+ failed_count: number;
40
+ }
41
+ export interface NtpData {
42
+ synced: boolean;
43
+ offset_seconds: number;
44
+ source: string;
45
+ daemon_running: boolean;
46
+ }
47
+ export interface FileDescriptorData {
48
+ allocated: number;
49
+ free: number;
50
+ max: number;
51
+ percent: number;
19
52
  }
20
53
  export interface ZfsPool {
21
54
  name: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glassmkr/crucible",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Lightweight bare metal server monitoring. IPMI, SMART, OS, network. Opinionated alerts.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -0,0 +1,27 @@
1
+ import { readProcFile } from "../lib/parse.js";
2
+
3
+ export interface ConntrackData {
4
+ available: boolean;
5
+ count: number;
6
+ max: number;
7
+ percent: number;
8
+ }
9
+
10
+ export function collectConntrack(): ConntrackData {
11
+ const countRaw = readProcFile("/proc/sys/net/netfilter/nf_conntrack_count");
12
+ const maxRaw = readProcFile("/proc/sys/net/netfilter/nf_conntrack_max");
13
+
14
+ if (!countRaw || !maxRaw) {
15
+ return { available: false, count: 0, max: 0, percent: 0 };
16
+ }
17
+
18
+ const count = parseInt(countRaw.trim(), 10);
19
+ const max = parseInt(maxRaw.trim(), 10);
20
+
21
+ if (isNaN(count) || isNaN(max) || max === 0) {
22
+ return { available: false, count: 0, max: 0, percent: 0 };
23
+ }
24
+
25
+ const percent = Math.round(((count / max) * 100) * 10) / 10;
26
+ return { available: true, count, max, percent };
27
+ }
@@ -0,0 +1,31 @@
1
+ import { readProcFile } from "../lib/parse.js";
2
+
3
+ export interface FileDescriptorData {
4
+ allocated: number;
5
+ free: number;
6
+ max: number;
7
+ percent: number;
8
+ }
9
+
10
+ export function collectFileDescriptors(): FileDescriptorData {
11
+ const raw = readProcFile("/proc/sys/fs/file-nr");
12
+ if (!raw) {
13
+ return { allocated: 0, free: 0, max: 0, percent: 0 };
14
+ }
15
+
16
+ const parts = raw.trim().split(/\s+/);
17
+ if (parts.length < 3) {
18
+ return { allocated: 0, free: 0, max: 0, percent: 0 };
19
+ }
20
+
21
+ const allocated = parseInt(parts[0], 10);
22
+ const free = parseInt(parts[1], 10);
23
+ const max = parseInt(parts[2], 10);
24
+
25
+ if (isNaN(allocated) || isNaN(max) || max === 0) {
26
+ return { allocated: 0, free: 0, max: 0, percent: 0 };
27
+ }
28
+
29
+ const percent = Math.round(((allocated / max) * 100) * 10) / 10;
30
+ return { allocated, free: isNaN(free) ? 0 : free, max, percent };
31
+ }
@@ -0,0 +1,103 @@
1
+ import { readProcFile } from "../lib/parse.js";
2
+
3
+ export interface IoLatencyInfo {
4
+ device: string;
5
+ avg_read_latency_ms: number | null;
6
+ avg_write_latency_ms: number | null;
7
+ read_iops: number;
8
+ write_iops: number;
9
+ }
10
+
11
+ interface DiskstatsCounters {
12
+ reads_completed: number;
13
+ read_time_ms: number;
14
+ writes_completed: number;
15
+ write_time_ms: number;
16
+ }
17
+
18
+ // Previous cumulative counters for delta computation
19
+ const previousCounters = new Map<string, DiskstatsCounters>();
20
+
21
+ // Match physical block devices, not partitions or virtual devices
22
+ function isPhysicalDevice(name: string): boolean {
23
+ // sd*, vd*, xvd* without trailing partition number
24
+ if (/^(sd|vd|xvd)[a-z]+$/.test(name)) return true;
25
+ // nvme*n* without partition suffix (nvme0n1 yes, nvme0n1p1 no)
26
+ if (/^nvme\d+n\d+$/.test(name)) return true;
27
+ // md* (RAID arrays)
28
+ if (/^md\d+$/.test(name)) return true;
29
+ return false;
30
+ }
31
+
32
+ function parseDiskstats(): Record<string, DiskstatsCounters> {
33
+ const raw = readProcFile("/proc/diskstats") || "";
34
+ const result: Record<string, DiskstatsCounters> = {};
35
+
36
+ for (const line of raw.split("\n")) {
37
+ const parts = line.trim().split(/\s+/);
38
+ if (parts.length < 11) continue;
39
+
40
+ const name = parts[2];
41
+ if (!isPhysicalDevice(name)) continue;
42
+
43
+ result[name] = {
44
+ reads_completed: Number(parts[3]) || 0,
45
+ read_time_ms: Number(parts[6]) || 0,
46
+ writes_completed: Number(parts[7]) || 0,
47
+ write_time_ms: Number(parts[10]) || 0,
48
+ };
49
+ }
50
+
51
+ return result;
52
+ }
53
+
54
+ function delta(current: number, previous: number): number {
55
+ if (current >= previous) return current - previous;
56
+ return current; // counter wrapped or reset
57
+ }
58
+
59
+ export function collectIoLatency(): IoLatencyInfo[] {
60
+ const current = parseDiskstats();
61
+ const results: IoLatencyInfo[] = [];
62
+ const currentDevices = new Set<string>();
63
+
64
+ for (const [name, counters] of Object.entries(current)) {
65
+ currentDevices.add(name);
66
+ const prev = previousCounters.get(name);
67
+
68
+ // Store current for next cycle
69
+ previousCounters.set(name, { ...counters });
70
+
71
+ if (!prev) {
72
+ // First cycle: no delta, report null latency
73
+ results.push({
74
+ device: name,
75
+ avg_read_latency_ms: null,
76
+ avg_write_latency_ms: null,
77
+ read_iops: 0,
78
+ write_iops: 0,
79
+ });
80
+ continue;
81
+ }
82
+
83
+ const deltaReads = delta(counters.reads_completed, prev.reads_completed);
84
+ const deltaReadTime = delta(counters.read_time_ms, prev.read_time_ms);
85
+ const deltaWrites = delta(counters.writes_completed, prev.writes_completed);
86
+ const deltaWriteTime = delta(counters.write_time_ms, prev.write_time_ms);
87
+
88
+ results.push({
89
+ device: name,
90
+ avg_read_latency_ms: deltaReads > 0 ? Math.round((deltaReadTime / deltaReads) * 100) / 100 : null,
91
+ avg_write_latency_ms: deltaWrites > 0 ? Math.round((deltaWriteTime / deltaWrites) * 100) / 100 : null,
92
+ read_iops: deltaReads,
93
+ write_iops: deltaWrites,
94
+ });
95
+ }
96
+
97
+ // Remove stale devices
98
+ for (const name of previousCounters.keys()) {
99
+ if (!currentDevices.has(name)) previousCounters.delete(name);
100
+ }
101
+
102
+ return results;
103
+ }
@@ -0,0 +1,90 @@
1
+ import { run } from "../lib/exec.js";
2
+
3
+ export interface NtpData {
4
+ synced: boolean;
5
+ offset_seconds: number;
6
+ source: string;
7
+ daemon_running: boolean;
8
+ }
9
+
10
+ export async function collectNtp(): Promise<NtpData> {
11
+ // Try timedatectl first (systemd-timesyncd)
12
+ const tdctl = await run("timedatectl", ["show", "--property=NTPSynchronized", "--value"]);
13
+ if (tdctl !== null) {
14
+ const synced = tdctl.trim() === "yes";
15
+ // Get the source daemon name
16
+ const statusOut = await run("timedatectl", ["show", "--property=NTP", "--value"]);
17
+ const ntpEnabled = statusOut?.trim() === "yes";
18
+
19
+ // Try to get offset from timedatectl timesync-status
20
+ let offset = 0;
21
+ try {
22
+ const tsStatus = await run("timedatectl", ["timesync-status"]);
23
+ if (tsStatus) {
24
+ const match = tsStatus.match(/Offset:\s*([+-]?\d+\.?\d*)(us|ms|s)/);
25
+ if (match) {
26
+ const val = parseFloat(match[1]);
27
+ const unit = match[2];
28
+ if (unit === "us") offset = val / 1_000_000;
29
+ else if (unit === "ms") offset = val / 1000;
30
+ else offset = val;
31
+ }
32
+ }
33
+ } catch { /* offset stays 0 */ }
34
+
35
+ return {
36
+ synced,
37
+ offset_seconds: offset,
38
+ source: "systemd-timesyncd",
39
+ daemon_running: ntpEnabled || synced,
40
+ };
41
+ }
42
+
43
+ // Try chrony
44
+ const chronyOut = await run("chronyc", ["tracking"]);
45
+ if (chronyOut) {
46
+ const leapMatch = chronyOut.match(/Leap status\s*:\s*(.+)/);
47
+ const synced = leapMatch ? leapMatch[1].trim() === "Normal" : false;
48
+ let offset = 0;
49
+ const offsetMatch = chronyOut.match(/Last offset\s*:\s*([+-]?\d+\.?\d*)\s*seconds/);
50
+ if (offsetMatch) offset = parseFloat(offsetMatch[1]);
51
+ return {
52
+ synced,
53
+ offset_seconds: Math.abs(offset),
54
+ source: "chrony",
55
+ daemon_running: true,
56
+ };
57
+ }
58
+
59
+ // Try ntpq
60
+ const ntpqOut = await run("ntpq", ["-pn"]);
61
+ if (ntpqOut) {
62
+ // A line starting with * means a selected peer (synced)
63
+ const synced = ntpqOut.split("\n").some((line) => line.startsWith("*"));
64
+ let offset = 0;
65
+ // Parse offset from the selected peer line
66
+ const selectedLine = ntpqOut.split("\n").find((line) => line.startsWith("*"));
67
+ if (selectedLine) {
68
+ const fields = selectedLine.trim().split(/\s+/);
69
+ // offset is typically field 8 (in ms)
70
+ if (fields.length >= 9) {
71
+ const rawOffset = parseFloat(fields[8]);
72
+ if (!isNaN(rawOffset)) offset = Math.abs(rawOffset) / 1000;
73
+ }
74
+ }
75
+ return {
76
+ synced,
77
+ offset_seconds: offset,
78
+ source: "ntpd",
79
+ daemon_running: true,
80
+ };
81
+ }
82
+
83
+ // No time sync daemon found
84
+ return {
85
+ synced: false,
86
+ offset_seconds: 0,
87
+ source: "none",
88
+ daemon_running: false,
89
+ };
90
+ }
@@ -0,0 +1,33 @@
1
+ import { run } from "../lib/exec.js";
2
+
3
+ export interface SystemdData {
4
+ failed_units: string[];
5
+ failed_count: number;
6
+ }
7
+
8
+ // Units commonly in failed state by design or misconfiguration
9
+ const DEFAULT_EXCLUDES = [
10
+ "systemd-networkd-wait-online.service",
11
+ ];
12
+
13
+ export async function collectSystemd(extraExcludes: string[] = []): Promise<SystemdData> {
14
+ const output = await run("systemctl", [
15
+ "list-units", "--type=service", "--state=failed", "--no-legend", "--plain",
16
+ ]);
17
+
18
+ if (!output || output.trim() === "") {
19
+ return { failed_units: [], failed_count: 0 };
20
+ }
21
+
22
+ const excludes = new Set([...DEFAULT_EXCLUDES, ...extraExcludes]);
23
+ const units: string[] = [];
24
+
25
+ for (const line of output.trim().split("\n")) {
26
+ const unit = line.trim().split(/\s+/)[0];
27
+ if (unit && unit.endsWith(".service") && !excludes.has(unit)) {
28
+ units.push(unit);
29
+ }
30
+ }
31
+
32
+ return { failed_units: units, failed_count: units.length };
33
+ }
package/src/index.ts CHANGED
@@ -34,6 +34,11 @@ import { pushToForge, initForgeAgent } from "./push/forge.js";
34
34
  import { collectSecurity, type SecurityData } from "./collect/security.js";
35
35
  import { collectZfs } from "./collect/zfs.js";
36
36
  import { collectIoErrors } from "./collect/io-errors.js";
37
+ import { collectIoLatency } from "./collect/io-latency.js";
38
+ import { collectConntrack } from "./collect/conntrack.js";
39
+ import { collectSystemd } from "./collect/systemd.js";
40
+ import { collectNtp } from "./collect/ntp.js";
41
+ import { collectFileDescriptors } from "./collect/fd.js";
37
42
  import type { Snapshot, IpmiInfo } from "./lib/types.js";
38
43
 
39
44
  const configPath = process.argv[2] || "/etc/glassmkr/collector.yaml";
@@ -94,6 +99,11 @@ async function collect() {
94
99
  // ZFS and I/O errors: collect every cycle (lightweight checks)
95
100
  try { snapshot.zfs = await collectZfs() ?? undefined; } catch { /* skip if ZFS not available */ }
96
101
  try { snapshot.io_errors = await collectIoErrors() ?? undefined; } catch { /* skip on error */ }
102
+ try { snapshot.io_latency = collectIoLatency(); } catch { /* skip on error */ }
103
+ try { snapshot.conntrack = collectConntrack(); } catch { /* skip on error */ }
104
+ try { snapshot.systemd = await collectSystemd(); } catch { /* skip on error */ }
105
+ try { snapshot.ntp = await collectNtp(); } catch { /* skip on error */ }
106
+ try { snapshot.file_descriptors = collectFileDescriptors(); } catch { /* skip on error */ }
97
107
 
98
108
  // Update Prometheus metrics
99
109
  updateMetrics(snapshot);
package/src/lib/types.ts CHANGED
@@ -13,6 +13,37 @@ export interface Snapshot {
13
13
  security?: SecurityData;
14
14
  zfs?: ZfsData;
15
15
  io_errors?: { count: number; devices: string[] };
16
+ io_latency?: Array<{ device: string; avg_read_latency_ms: number | null; avg_write_latency_ms: number | null; read_iops: number; write_iops: number }>;
17
+ conntrack?: ConntrackData;
18
+ systemd?: SystemdData;
19
+ ntp?: NtpData;
20
+ file_descriptors?: FileDescriptorData;
21
+ }
22
+
23
+ export interface ConntrackData {
24
+ available: boolean;
25
+ count: number;
26
+ max: number;
27
+ percent: number;
28
+ }
29
+
30
+ export interface SystemdData {
31
+ failed_units: string[];
32
+ failed_count: number;
33
+ }
34
+
35
+ export interface NtpData {
36
+ synced: boolean;
37
+ offset_seconds: number;
38
+ source: string;
39
+ daemon_running: boolean;
40
+ }
41
+
42
+ export interface FileDescriptorData {
43
+ allocated: number;
44
+ free: number;
45
+ max: number;
46
+ percent: number;
16
47
  }
17
48
 
18
49
  export interface ZfsPool {