mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-29 14:45:24 +00:00
fix: finish 2.9.0 plugin moves and get every check green
This commit is contained in:
1 parent
8c1e02af89
commit
6489b62ece
335 files changed
+29878
-17749
No files matched your search
@@ -98,7 +98,12 @@ export async function collectMetrics(
|
||||
onAuthenticated?.();
|
||||
const platform = await detectHostPlatform(client);
|
||||
const result: CollectedMetrics = {
|
||||
cpu: await collectCpuMetrics(client, platform, host.id),
|
||||
cpu: await collectCpuMetrics(
|
||||
client,
|
||||
platform,
|
||||
host.id,
|
||||
state.rateSamples.cpu,
|
||||
),
|
||||
memory: await collectMemoryMetrics(client, platform),
|
||||
disk: await collectDiskMetrics(
|
||||
client,
|
||||
@@ -106,7 +111,12 @@ export async function collectMetrics(
|
||||
mounts.monitoredMounts,
|
||||
platform,
|
||||
),
|
||||
network: await collectNetworkMetrics(client, platform, host.id),
|
||||
network: await collectNetworkMetrics(
|
||||
client,
|
||||
platform,
|
||||
host.id,
|
||||
state.rateSamples.network,
|
||||
),
|
||||
uptime: await collectUptimeMetrics(client, platform),
|
||||
processes: await collectProcessesMetrics(client),
|
||||
system: await collectSystemMetrics(client, platform),
|
||||
|
||||
@@ -30,3 +30,32 @@ export function hostImportNormalizer(
|
||||
});
|
||||
return { ...settings };
|
||||
}
|
||||
|
||||
/**
|
||||
* Registered as "host-metrics.hostPayloadLegacy": the 2.8 statsConfig shape
|
||||
* Termix-Mobile still reads. Remove once the mobile app reads
|
||||
* pluginSettings["host-metrics"] and GET /host/status.
|
||||
*/
|
||||
export function hostPayloadLegacy(
|
||||
values: Record<string, unknown>,
|
||||
host: Record<string, unknown>,
|
||||
): Record<string, unknown> {
|
||||
const settings = readHostMetricsSettings(values);
|
||||
const statusInterval =
|
||||
typeof host.statusCheckInterval === "number"
|
||||
? host.statusCheckInterval
|
||||
: null;
|
||||
return {
|
||||
statsConfig: {
|
||||
enabledWidgets: settings.enabledWidgets,
|
||||
statusCheckEnabled: host.statusCheckEnabled !== false,
|
||||
statusCheckInterval: statusInterval ?? 30,
|
||||
useGlobalStatusInterval: statusInterval === null,
|
||||
metricsEnabled: settings.metricsEnabled,
|
||||
metricsInterval: settings.metricsInterval ?? 30,
|
||||
useGlobalMetricsInterval: settings.metricsInterval == null,
|
||||
excludedMounts: settings.excludedMounts,
|
||||
monitoredMounts: settings.monitoredMounts,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import { MetricsSessions } from "./sessions.js";
|
||||
import { CollectorRegistry } from "./collectors.js";
|
||||
import { MetricsPoller, TOPIC_HEALTH_CHECK } from "./poller.js";
|
||||
import { registerRoutes } from "./routes.js";
|
||||
import { hostImportNormalizer } from "./host-import.js";
|
||||
import { hostImportNormalizer, hostPayloadLegacy } from "./host-import.js";
|
||||
import { newSessionId, supportsMetrics } from "./helpers.js";
|
||||
|
||||
export type { MetricsCollectorV1 } from "./collectors.js";
|
||||
@@ -101,6 +101,7 @@ export async function activate(ctx: PluginContext) {
|
||||
"host-metrics.hostImportNormalizer",
|
||||
hostImportNormalizer,
|
||||
);
|
||||
ctx.registry.provide("host-metrics.hostPayloadLegacy", hostPayloadLegacy);
|
||||
|
||||
ctx.log.info("Host Metrics mounted at /plugin-api/host-metrics");
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@ import {
|
||||
metricsConcurrencyFor,
|
||||
type MetricsState,
|
||||
} from "./state.js";
|
||||
import { clearCpuSampleCache } from "./widgets/cpu-collector.js";
|
||||
import { clearNetworkSampleCache } from "./widgets/network-collector.js";
|
||||
|
||||
export const DEFAULT_METRICS_INTERVAL = 30;
|
||||
export const DEFAULT_RETENTION_DAYS = 7;
|
||||
@@ -201,8 +199,7 @@ export class MetricsPoller {
|
||||
for (const hostId of [...this.polled.keys()]) this.stop(hostId, true);
|
||||
this.viewers.clear();
|
||||
this.viewerDetails.clear();
|
||||
clearCpuSampleCache();
|
||||
clearNetworkSampleCache();
|
||||
this.state.rateSamples.clear();
|
||||
}
|
||||
|
||||
/** The host as `userId` may reach it, cached for a few minutes. */
|
||||
@@ -274,8 +271,7 @@ export class MetricsPoller {
|
||||
if (clearData) {
|
||||
this.metrics.delete(hostId);
|
||||
this.state.hostCache.invalidate(hostId);
|
||||
clearCpuSampleCache(hostId);
|
||||
clearNetworkSampleCache(hostId);
|
||||
this.state.rateSamples.clear(hostId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import type { CpuSamples } from "./widgets/cpu-collector.js";
|
||||
import { createNetworkSamples } from "./widgets/network-collector.js";
|
||||
|
||||
export type HostStatus = "online" | "reachable" | "offline";
|
||||
|
||||
export class RequestQueue {
|
||||
@@ -422,8 +425,30 @@ export function canStartInitialMetrics(
|
||||
}
|
||||
|
||||
/** Everything one activation of the plugin polls with. */
|
||||
/** The last CPU and network sample per host, for rates between polls. */
|
||||
export function createRateSamples() {
|
||||
const cpu: CpuSamples = new Map();
|
||||
const network = createNetworkSamples();
|
||||
return {
|
||||
cpu,
|
||||
network,
|
||||
clear(hostId?: number) {
|
||||
if (hostId === undefined) {
|
||||
cpu.clear();
|
||||
network.counters.clear();
|
||||
network.windows.clear();
|
||||
return;
|
||||
}
|
||||
cpu.delete(hostId);
|
||||
network.counters.delete(hostId);
|
||||
network.windows.delete(hostId);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createMetricsState() {
|
||||
return {
|
||||
rateSamples: createRateSamples(),
|
||||
/** SSH metrics execs are expensive; resized with the fleet. */
|
||||
metricsLimiter: new ConcurrentLimiter(5),
|
||||
/** Viewer registration is bursty; admit only two first samples at a time. */
|
||||
|
||||
@@ -9,20 +9,13 @@ import type { Client } from "ssh2";
|
||||
/**
|
||||
* CPU percent is derived from the delta against the previous poll's
|
||||
* /proc/stat sample rather than a same-poll double-read, so a poll no longer
|
||||
* has to block for a fixed settle window. Keyed by host.
|
||||
* has to block for a fixed settle window. Keyed by host, and owned by the
|
||||
* poller so it goes away with the plugin.
|
||||
*/
|
||||
const previousStat = new Map<
|
||||
export type CpuSamples = Map<
|
||||
number,
|
||||
{ total: number; idle: number; timestamp: number }
|
||||
>();
|
||||
|
||||
export function clearCpuSampleCache(hostId?: number): void {
|
||||
if (hostId === undefined) {
|
||||
previousStat.clear();
|
||||
return;
|
||||
}
|
||||
previousStat.delete(hostId);
|
||||
}
|
||||
>;
|
||||
|
||||
export function parseCpuLine(
|
||||
cpuLine: string,
|
||||
@@ -149,6 +142,7 @@ export async function collectCpuMetrics(
|
||||
client: Client,
|
||||
platform?: HostPlatform,
|
||||
hostId?: number,
|
||||
previousStat: CpuSamples = new Map(),
|
||||
): Promise<{
|
||||
percent: number | null;
|
||||
cores: number | null;
|
||||
|
||||
@@ -49,24 +49,16 @@ export function counterRate(
|
||||
* double-sample, so a poll no longer has to block for a fixed settle window.
|
||||
* Keyed by host, since counters from one host are meaningless against another.
|
||||
*/
|
||||
const previousSamples = new Map<
|
||||
number,
|
||||
{ counters: Map<string, NetworkCounters>; timestamp: number }
|
||||
>();
|
||||
export interface NetworkSamples {
|
||||
counters: Map<
|
||||
number,
|
||||
{ counters: Map<string, NetworkCounters>; timestamp: number }
|
||||
>;
|
||||
windows: Map<number, { rows: WindowsAdapterRow[]; timestamp: number }>;
|
||||
}
|
||||
|
||||
const previousWindowsSamples = new Map<
|
||||
number,
|
||||
{ rows: WindowsAdapterRow[]; timestamp: number }
|
||||
>();
|
||||
|
||||
export function clearNetworkSampleCache(hostId?: number): void {
|
||||
if (hostId === undefined) {
|
||||
previousSamples.clear();
|
||||
previousWindowsSamples.clear();
|
||||
return;
|
||||
}
|
||||
previousSamples.delete(hostId);
|
||||
previousWindowsSamples.delete(hostId);
|
||||
export function createNetworkSamples(): NetworkSamples {
|
||||
return { counters: new Map(), windows: new Map() };
|
||||
}
|
||||
|
||||
export function parseDarwinIfconfig(
|
||||
@@ -112,7 +104,8 @@ export function parseDarwinNetstat(
|
||||
|
||||
async function collectDarwinNetworkMetrics(
|
||||
client: Client,
|
||||
hostId?: number,
|
||||
hostId: number | undefined,
|
||||
samples: NetworkSamples,
|
||||
): Promise<{
|
||||
interfaces: Array<{
|
||||
name: string;
|
||||
@@ -143,7 +136,7 @@ async function collectDarwinNetworkMetrics(
|
||||
const netstatOut = await execCommand(client, "netstat -ib 2>/dev/null");
|
||||
const current = parseDarwinNetstat(netstatOut.stdout);
|
||||
const previous =
|
||||
hostId !== undefined ? previousSamples.get(hostId) : undefined;
|
||||
hostId !== undefined ? samples.counters.get(hostId) : undefined;
|
||||
const elapsedSeconds = previous
|
||||
? (readAt - previous.timestamp) / 1000
|
||||
: 0;
|
||||
@@ -166,7 +159,7 @@ async function collectDarwinNetworkMetrics(
|
||||
});
|
||||
}
|
||||
if (hostId !== undefined) {
|
||||
previousSamples.set(hostId, { counters: current, timestamp: readAt });
|
||||
samples.counters.set(hostId, { counters: current, timestamp: readAt });
|
||||
}
|
||||
} catch {
|
||||
for (const [name, data] of ifMap.entries()) {
|
||||
@@ -225,7 +218,8 @@ export function parseWindowsAdapterJson(output: string): WindowsAdapterRow[] {
|
||||
|
||||
async function collectWindowsNetworkMetrics(
|
||||
client: Client,
|
||||
hostId?: number,
|
||||
hostId: number | undefined,
|
||||
samples: NetworkSamples,
|
||||
): Promise<{
|
||||
interfaces: Array<{
|
||||
name: string;
|
||||
@@ -252,7 +246,7 @@ async function collectWindowsNetworkMetrics(
|
||||
const result = await execPowerShell(client, WINDOWS_ADAPTER_SCRIPT);
|
||||
const currentRows = parseWindowsAdapterJson(result.stdout);
|
||||
const previous =
|
||||
hostId !== undefined ? previousWindowsSamples.get(hostId) : undefined;
|
||||
hostId !== undefined ? samples.windows.get(hostId) : undefined;
|
||||
const elapsedSeconds = previous ? (readAt - previous.timestamp) / 1000 : 0;
|
||||
const previousMap = new Map(
|
||||
(previous?.rows ?? []).map((row) => [row.name, row]),
|
||||
@@ -276,7 +270,7 @@ async function collectWindowsNetworkMetrics(
|
||||
}
|
||||
|
||||
if (hostId !== undefined) {
|
||||
previousWindowsSamples.set(hostId, {
|
||||
samples.windows.set(hostId, {
|
||||
rows: currentRows,
|
||||
timestamp: readAt,
|
||||
});
|
||||
@@ -292,6 +286,7 @@ export async function collectNetworkMetrics(
|
||||
client: Client,
|
||||
platform?: HostPlatform,
|
||||
hostId?: number,
|
||||
samples: NetworkSamples = createNetworkSamples(),
|
||||
): Promise<{
|
||||
interfaces: Array<{
|
||||
name: string;
|
||||
@@ -304,10 +299,10 @@ export async function collectNetworkMetrics(
|
||||
}>;
|
||||
}> {
|
||||
if (platform === "darwin") {
|
||||
return collectDarwinNetworkMetrics(client, hostId);
|
||||
return collectDarwinNetworkMetrics(client, hostId, samples);
|
||||
}
|
||||
if (platform === "windows") {
|
||||
return collectWindowsNetworkMetrics(client, hostId);
|
||||
return collectWindowsNetworkMetrics(client, hostId, samples);
|
||||
}
|
||||
|
||||
const interfaces: Array<{
|
||||
@@ -368,7 +363,7 @@ export async function collectNetworkMetrics(
|
||||
const procNet = await execCommand(client, "cat /proc/net/dev");
|
||||
const rxTxMap = parseNetworkCounters(procNet.stdout);
|
||||
const previous =
|
||||
hostId !== undefined ? previousSamples.get(hostId) : undefined;
|
||||
hostId !== undefined ? samples.counters.get(hostId) : undefined;
|
||||
const elapsedSeconds = previous
|
||||
? (readAt - previous.timestamp) / 1000
|
||||
: 0;
|
||||
@@ -395,7 +390,7 @@ export async function collectNetworkMetrics(
|
||||
});
|
||||
}
|
||||
if (hostId !== undefined) {
|
||||
previousSamples.set(hostId, {
|
||||
samples.counters.set(hostId, {
|
||||
counters: rxTxMap,
|
||||
timestamp: readAt,
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user