@calltelemetry/ct-lab-mcp 0.8.91 → 0.8.93
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 +24 -20
- package/dist/src/hypervisors/manager.d.ts.map +1 -1
- package/dist/src/hypervisors/manager.js +11 -11
- package/dist/src/hypervisors/manager.js.map +1 -1
- package/dist/src/hypervisors/types.d.ts +3 -0
- package/dist/src/hypervisors/types.d.ts.map +1 -1
- package/dist/src/server.js +4 -4
- package/dist/src/server.js.map +1 -1
- package/dist/src/slot/manager.d.ts.map +1 -1
- package/dist/src/slot/manager.js +29 -34
- package/dist/src/slot/manager.js.map +1 -1
- package/dist/src/tools/appliance-claim-tools.d.ts.map +1 -1
- package/dist/src/tools/appliance-claim-tools.js +12 -10
- package/dist/src/tools/appliance-claim-tools.js.map +1 -1
- package/dist/src/tools/auto-host-placement.d.ts +33 -0
- package/dist/src/tools/auto-host-placement.d.ts.map +1 -0
- package/dist/src/tools/auto-host-placement.js +39 -0
- package/dist/src/tools/auto-host-placement.js.map +1 -0
- package/dist/src/tools/claim-creation-projection.js +2 -2
- package/dist/src/tools/claim-creation-projection.js.map +1 -1
- package/dist/src/tools/claim-custody.d.ts +175 -0
- package/dist/src/tools/claim-custody.d.ts.map +1 -0
- package/dist/src/tools/claim-custody.js +196 -0
- package/dist/src/tools/claim-custody.js.map +1 -0
- package/dist/src/tools/claim-kubernetes.d.ts +14 -0
- package/dist/src/tools/claim-kubernetes.d.ts.map +1 -0
- package/dist/src/tools/{slot-kubernetes.js → claim-kubernetes.js} +30 -26
- package/dist/src/tools/claim-kubernetes.js.map +1 -0
- package/dist/src/tools/fleet-metrics.d.ts +37 -2
- package/dist/src/tools/fleet-metrics.d.ts.map +1 -1
- package/dist/src/tools/fleet-metrics.js +25 -15
- package/dist/src/tools/fleet-metrics.js.map +1 -1
- package/dist/src/tools/host-capacity-rank.d.ts +25 -0
- package/dist/src/tools/host-capacity-rank.d.ts.map +1 -0
- package/dist/src/tools/host-capacity-rank.js +17 -0
- package/dist/src/tools/host-capacity-rank.js.map +1 -0
- package/dist/src/tools/index.d.ts.map +1 -1
- package/dist/src/tools/index.js +20 -19
- package/dist/src/tools/index.js.map +1 -1
- package/dist/src/tools/kubernetes-backend.js +1 -1
- package/dist/src/tools/kubernetes-backend.js.map +1 -1
- package/dist/src/tools/legacy-clone-plan.d.ts +2 -0
- package/dist/src/tools/legacy-clone-plan.d.ts.map +1 -1
- package/dist/src/tools/legacy-clone-plan.js +13 -5
- package/dist/src/tools/legacy-clone-plan.js.map +1 -1
- package/dist/src/tools/legacy-guest-guard.d.ts.map +1 -1
- package/dist/src/tools/legacy-guest-guard.js +4 -2
- package/dist/src/tools/legacy-guest-guard.js.map +1 -1
- package/dist/src/tools/{physical-slot.d.ts → legacy-reservation.d.ts} +79 -40
- package/dist/src/tools/legacy-reservation.d.ts.map +1 -0
- package/dist/src/tools/legacy-reservation.js +111 -0
- package/dist/src/tools/legacy-reservation.js.map +1 -0
- package/dist/src/tools/provision-tools.d.ts.map +1 -1
- package/dist/src/tools/provision-tools.js +16 -4
- package/dist/src/tools/provision-tools.js.map +1 -1
- package/dist/src/tools/quick-provision.d.ts.map +1 -1
- package/dist/src/tools/quick-provision.js +15 -4
- package/dist/src/tools/quick-provision.js.map +1 -1
- package/dist/src/tools/renewal-claim.d.ts +2 -1
- package/dist/src/tools/renewal-claim.d.ts.map +1 -1
- package/dist/src/tools/renewal-claim.js +19 -8
- package/dist/src/tools/renewal-claim.js.map +1 -1
- package/dist/src/tools/slot-retirement.d.ts +22 -0
- package/dist/src/tools/slot-retirement.d.ts.map +1 -0
- package/dist/src/tools/slot-retirement.js +42 -0
- package/dist/src/tools/slot-retirement.js.map +1 -0
- package/package.json +2 -2
- package/dist/src/tools/physical-slot.d.ts.map +0 -1
- package/dist/src/tools/physical-slot.js +0 -319
- package/dist/src/tools/physical-slot.js.map +0 -1
- package/dist/src/tools/slot-kubernetes.d.ts +0 -14
- package/dist/src/tools/slot-kubernetes.d.ts.map +0 -1
- package/dist/src/tools/slot-kubernetes.js.map +0 -1
- /package/dist/src/tools/{physical-slot-claim-spec-schema.v1.json → appliance-claim-spec-schema.v1.json} +0 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ApplianceClaim custody (REL-1746, supersedes physical-slot reservation Leases).
|
|
3
|
+
*
|
|
4
|
+
* The original-owner record lives ON the claim as annotation `calltelemetry.com/claim-custody`
|
|
5
|
+
* (canonical JSON, <= 64 KiB, strict parse), written in the SAME create call as the claim. No Lease is
|
|
6
|
+
* ever created, renewed or required: per-guest serialization is in-process keyed by (node, vmid) and
|
|
7
|
+
* cross-actor safety is the claim UID/resourceVersion plus the claim-occupancy scan. Deletion only
|
|
8
|
+
* requests claim deletion with UID + resourceVersion preconditions; the operator owns teardown.
|
|
9
|
+
* Claims that still hold a legacy reservation Lease are verified READ-ONLY (the operator migrates them).
|
|
10
|
+
*/
|
|
11
|
+
import { randomUUID } from "node:crypto";
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
import { actualClaimIssue, claimCreationSha256, effectiveClaimSpec } from "./claim-creation-projection.js";
|
|
14
|
+
import { createClaimApi } from "./claim-kubernetes.js";
|
|
15
|
+
import { ContextSchema, GuestSchema, ProviderSchema, annotationJson, canonicalJson, claimGuest, digest, objectName, requireCustody, readLegacyReservation, sha, uuid, verifyLegacyClaim, legacyReservationLeaseName, } from "./legacy-reservation.js";
|
|
16
|
+
export { canonicalJson, claimGuest, objectName };
|
|
17
|
+
export const CUSTODY_ANNOTATION = "calltelemetry.com/claim-custody";
|
|
18
|
+
const CLAIMS = "/apis/calltelemetry.com/v1alpha1/namespaces/ct-lab/applianceclaims";
|
|
19
|
+
export const CustodySchema = z.object({
|
|
20
|
+
schema: z.literal("claim-custody/v1"), guest: GuestSchema,
|
|
21
|
+
phase: z.enum(["bound", "provider-bound", "retiring"]),
|
|
22
|
+
operationNonce: uuid, requestSha256: sha, context: ContextSchema,
|
|
23
|
+
provider: ProviderSchema.nullable(),
|
|
24
|
+
}).strict();
|
|
25
|
+
/** Strict custody parse: closed field set, duplicate keys rejected, provider/phase coherence, sorted volumes. */
|
|
26
|
+
export function parseCustody(text) {
|
|
27
|
+
const record = CustodySchema.parse(annotationJson(text));
|
|
28
|
+
if (record.provider)
|
|
29
|
+
requireCustody(canonicalJson(record.provider.volumes) === canonicalJson([...new Set(record.provider.volumes)].sort()), "provider volumes are not sorted and unique");
|
|
30
|
+
requireCustody(record.phase !== "provider-bound" || record.provider !== null, "provider phase lacks immutable provider binding");
|
|
31
|
+
requireCustody(record.phase !== "bound" || record.provider === null, "bound phase carries a provider binding");
|
|
32
|
+
return record;
|
|
33
|
+
}
|
|
34
|
+
/** The custody record of a claim, or null when the annotation is absent. Malformed records throw. */
|
|
35
|
+
export function claimCustody(claim) {
|
|
36
|
+
const text = claim?.metadata?.annotations?.[CUSTODY_ANNOTATION];
|
|
37
|
+
return text === undefined ? null : parseCustody(text);
|
|
38
|
+
}
|
|
39
|
+
export function creationOwnerSha(record) {
|
|
40
|
+
const { schema, guest, operationNonce, requestSha256, context } = CustodySchema.parse(record);
|
|
41
|
+
return digest({ schema, guest, operationNonce, requestSha256, context });
|
|
42
|
+
}
|
|
43
|
+
/** Verifies a live claim against its own custody record. */
|
|
44
|
+
export function verifyCustodyClaim(live, record, expectedUid) {
|
|
45
|
+
requireCustody(live?.apiVersion === "calltelemetry.com/v1alpha1" && live.kind === "ApplianceClaim"
|
|
46
|
+
&& live.metadata?.namespace === "ct-lab" && objectName.safeParse(live.metadata.name).success
|
|
47
|
+
&& uuid.safeParse(live.metadata.uid).success && (!expectedUid || live.metadata.uid === expectedUid)
|
|
48
|
+
&& typeof live.metadata.resourceVersion === "string" && live.metadata.resourceVersion.length > 0
|
|
49
|
+
&& canonicalJson(claimGuest(live)) === canonicalJson(record.guest)
|
|
50
|
+
&& (actualClaimIssue(live) ?? null) === (record.context.issueKey ?? null)
|
|
51
|
+
&& claimCreationSha256(live) === record.requestSha256, "claim UID, guest tuple or source lineage differs");
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* A claim with neither a custody annotation nor a legacy reservation Lease was created outside the guarded
|
|
55
|
+
* path: there is no original-owner record to retire it against. Distinct from a record that exists but
|
|
56
|
+
* fails identity checks.
|
|
57
|
+
*/
|
|
58
|
+
export class NoClaimCustodyError extends Error {
|
|
59
|
+
claimName;
|
|
60
|
+
guest;
|
|
61
|
+
constructor(claimName, guest) {
|
|
62
|
+
super(`ApplianceClaim '${claimName}' has no claim-custody annotation (and no legacy reservation Lease) for ${guest.node}/${guest.vmid}: it was created outside the guarded create path, so the guard has no original-owner record to retire it against; nothing was modified`);
|
|
63
|
+
this.claimName = claimName;
|
|
64
|
+
this.guest = guest;
|
|
65
|
+
this.name = "NoClaimCustodyError";
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const defaults = () => ({ api: createClaimApi(), now: Date.now, nonce: randomUUID });
|
|
69
|
+
const tails = new Map();
|
|
70
|
+
/** In-process serialization keyed by (node, vmid). Single MCP replica; cross-actor safety is claim UID/resourceVersion. */
|
|
71
|
+
function serialized(guest, action) {
|
|
72
|
+
const key = `${guest.node}/${guest.vmid}`;
|
|
73
|
+
const run = (tails.get(key) ?? Promise.resolve()).catch(() => { }).then(action);
|
|
74
|
+
const tail = run.catch(() => { });
|
|
75
|
+
tails.set(key, tail);
|
|
76
|
+
void tail.then(() => { if (tails.get(key) === tail)
|
|
77
|
+
tails.delete(key); });
|
|
78
|
+
return run;
|
|
79
|
+
}
|
|
80
|
+
const nodeAlias = (node) => node === "proxmox-lab" ? "proxmox" : ["proxmox-mini", "proxmox-minis-lab"].includes(node) ? "minis" : node === "proxmox-ibm64" ? "ibm64" : node;
|
|
81
|
+
async function noOtherClaim(deps, guest, ownUid) {
|
|
82
|
+
let next = "";
|
|
83
|
+
for (let page = 0; page < 10; page++) {
|
|
84
|
+
const result = await deps.api.request(`${CLAIMS}?limit=500${next ? `&continue=${encodeURIComponent(next)}` : ""}`, "GET");
|
|
85
|
+
requireCustody(result?.kind === "ApplianceClaimList" && result.apiVersion === "calltelemetry.com/v1alpha1" && Array.isArray(result.items), "claim occupancy list unavailable");
|
|
86
|
+
for (const claim of result.items) {
|
|
87
|
+
if (claim.metadata?.uid === ownUid)
|
|
88
|
+
continue;
|
|
89
|
+
const alias = nodeAlias(claim.spec?.node);
|
|
90
|
+
if (String(claim.spec?.vmid) === String(guest.vmid) && (alias === guest.node || !["minis", "proxmox", "ibm64", "esxi"].includes(alias)))
|
|
91
|
+
throw new Error("claim custody guest has another existing ApplianceClaim; custody unchanged");
|
|
92
|
+
}
|
|
93
|
+
next = result.metadata?.continue || "";
|
|
94
|
+
requireCustody(typeof next === "string", "claim occupancy pagination malformed");
|
|
95
|
+
if (!next)
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
throw new Error("claim custody occupancy scan incomplete; custody unchanged");
|
|
99
|
+
}
|
|
100
|
+
function requireResumable(live, manifest, deps) {
|
|
101
|
+
requireCustody(!live.metadata.deletionTimestamp && live.spec?.closed !== true && live.spec?.state !== "Closed" && live.status?.phase !== "Closed", "bound claim is retiring");
|
|
102
|
+
requireCustody(Number.isInteger(live.spec?.ttlHours) && live.spec.ttlHours >= manifest.spec.ttlHours && live.spec.ttlHours <= 8760 && live.spec.claimType !== "fixture", "bound claim finite TTL differs");
|
|
103
|
+
if (live.status?.phase === "Ready" || live.status?.completionTime) {
|
|
104
|
+
const baseline = Date.parse(live.status?.completionTime || "");
|
|
105
|
+
requireCustody(Number.isFinite(baseline) && baseline + live.spec.ttlHours * 3_600_000 > deps.now(), "bound claim expired or Ready TTL baseline unavailable");
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
export async function createClaimWithCustody(manifest, deps = defaults()) {
|
|
109
|
+
requireCustody(manifest.metadata?.namespace === "ct-lab", "namespace must be ct-lab");
|
|
110
|
+
objectName.parse(manifest.metadata.name);
|
|
111
|
+
manifest = { ...manifest, spec: effectiveClaimSpec(manifest.spec) };
|
|
112
|
+
const guest = claimGuest(manifest);
|
|
113
|
+
requireCustody(typeof manifest.spec.vmName === "string" && manifest.spec.vmName.length > 0, "constructor must provide the actual VM name before claim POST");
|
|
114
|
+
requireCustody(Number.isInteger(manifest.spec?.ttlHours) && manifest.spec.ttlHours > 0 && manifest.spec.ttlHours <= 8760, "dynamic claim requires finite TTL");
|
|
115
|
+
const context = ContextSchema.parse({ kind: "mcp", ...(actualClaimIssue(manifest) ? { issueKey: actualClaimIssue(manifest) } : {}) });
|
|
116
|
+
const requestSha256 = claimCreationSha256(manifest);
|
|
117
|
+
const path = `${CLAIMS}/${manifest.metadata.name}`;
|
|
118
|
+
return serialized(guest, async () => {
|
|
119
|
+
const existing = await deps.api.request(path, "GET");
|
|
120
|
+
if (existing)
|
|
121
|
+
return resume(existing, manifest, guest, context, requestSha256, deps);
|
|
122
|
+
await noOtherClaim(deps, guest);
|
|
123
|
+
// A stale legacy reservation for a different (now absent) claim blocks reuse of the guest until the
|
|
124
|
+
// operator's legacy sweep removes it; we never delete it ourselves.
|
|
125
|
+
const stale = await readLegacyReservation(deps.api, guest);
|
|
126
|
+
requireCustody(!stale, `guest ${guest.node}/${guest.vmid} still has a legacy reservation Lease (${legacyReservationLeaseName(guest)}) for another claim; the operator migrates or sweeps it`);
|
|
127
|
+
const record = CustodySchema.parse({ schema: "claim-custody/v1", guest, phase: "bound", operationNonce: deps.nonce(), requestSha256, context, provider: null });
|
|
128
|
+
const body = { ...manifest, metadata: { ...manifest.metadata, annotations: { ...(manifest.metadata.annotations || {}), [CUSTODY_ANNOTATION]: canonicalJson(record) } } };
|
|
129
|
+
const response = await deps.api.request(CLAIMS, "POST", body);
|
|
130
|
+
requireCustody(uuid.safeParse(response?.metadata?.uid).success, "claim creation response lacks actual UID");
|
|
131
|
+
const live = await deps.api.request(path, "GET");
|
|
132
|
+
const held = claimCustody(live);
|
|
133
|
+
requireCustody(held && canonicalJson(held) === canonicalJson(record), "created custody annotation not independently confirmed");
|
|
134
|
+
verifyCustodyClaim(live, held, response.metadata.uid);
|
|
135
|
+
requireCustody(!live.metadata.deletionTimestamp, "new claim is already deleting");
|
|
136
|
+
await noOtherClaim(deps, guest, live.metadata.uid);
|
|
137
|
+
return { claim: live, custody: { source: "annotation", record: held, creationOwnerSha256: creationOwnerSha(held) }, reused: false };
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
async function resume(live, manifest, guest, context, requestSha256, deps) {
|
|
141
|
+
const held = claimCustody(live);
|
|
142
|
+
if (held) {
|
|
143
|
+
requireCustody(["bound", "provider-bound"].includes(held.phase) && held.requestSha256 === requestSha256
|
|
144
|
+
&& canonicalJson(held.context) === canonicalJson(context) && canonicalJson(held.guest) === canonicalJson(guest), "existing claim custody is occupied by a different request");
|
|
145
|
+
verifyCustodyClaim(live, held);
|
|
146
|
+
requireResumable(live, manifest, deps);
|
|
147
|
+
await noOtherClaim(deps, guest, live.metadata.uid);
|
|
148
|
+
return { claim: live, custody: { source: "annotation", record: held, creationOwnerSha256: creationOwnerSha(held) }, reused: true };
|
|
149
|
+
}
|
|
150
|
+
const legacy = await readLegacyReservation(deps.api, guest);
|
|
151
|
+
if (!legacy)
|
|
152
|
+
throw new NoClaimCustodyError(manifest.metadata.name, guest);
|
|
153
|
+
const { record, lease } = legacy;
|
|
154
|
+
requireCustody(["bound", "provider-bound"].includes(record.phase) && record.requestSha256 === requestSha256
|
|
155
|
+
&& canonicalJson(record.context) === canonicalJson(context) && record.intendedClaim.name === manifest.metadata.name, "legacy reservation is occupied by a different request");
|
|
156
|
+
verifyLegacyClaim(live, record, record.claim.uid);
|
|
157
|
+
requireResumable(live, manifest, deps);
|
|
158
|
+
await noOtherClaim(deps, guest, live.metadata.uid);
|
|
159
|
+
return { claim: live, custody: { source: "legacy-lease", record, legacyLease: { name: lease.metadata.name, uid: lease.metadata.uid }, migration: "operator-pending" }, reused: true };
|
|
160
|
+
}
|
|
161
|
+
export async function deleteClaimWithCustody(name, expectedUid, deps = defaults()) {
|
|
162
|
+
objectName.parse(name);
|
|
163
|
+
uuid.parse(expectedUid);
|
|
164
|
+
const live = await deps.api.request(`${CLAIMS}/${name}`, "GET");
|
|
165
|
+
requireCustody(live?.metadata?.uid === expectedUid, "original selected claim UID differs or is absent");
|
|
166
|
+
const guest = claimGuest(live);
|
|
167
|
+
return serialized(guest, async () => {
|
|
168
|
+
const selected = await deps.api.request(`${CLAIMS}/${name}`, "GET");
|
|
169
|
+
requireCustody(selected?.metadata?.uid === expectedUid, "original selected claim UID differs or is absent");
|
|
170
|
+
let source;
|
|
171
|
+
let phase;
|
|
172
|
+
const held = claimCustody(selected);
|
|
173
|
+
if (held) {
|
|
174
|
+
requireCustody(selected.metadata.name === name && canonicalJson(held.guest) === canonicalJson(guest), "claim does not own its custody record");
|
|
175
|
+
verifyCustodyClaim(selected, held, expectedUid);
|
|
176
|
+
source = "annotation";
|
|
177
|
+
phase = held.phase;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
const legacy = await readLegacyReservation(deps.api, guest);
|
|
181
|
+
if (!legacy)
|
|
182
|
+
throw new NoClaimCustodyError(name, guest);
|
|
183
|
+
requireCustody(["bound", "provider-bound", "retiring"].includes(legacy.record.phase) && legacy.record.claim?.uid === expectedUid && legacy.record.intendedClaim.name === name, "claim does not own the bound legacy reservation");
|
|
184
|
+
verifyLegacyClaim(selected, legacy.record, expectedUid);
|
|
185
|
+
source = "legacy-lease";
|
|
186
|
+
phase = legacy.record.phase;
|
|
187
|
+
}
|
|
188
|
+
await deps.api.request(`${CLAIMS}/${name}`, "DELETE", { apiVersion: "v1", kind: "DeleteOptions", preconditions: { uid: expectedUid, resourceVersion: selected.metadata.resourceVersion } });
|
|
189
|
+
return {
|
|
190
|
+
requested: true, expectedUid, custodySource: source, custodyPhase: phase, custodyRetained: true,
|
|
191
|
+
...(source === "legacy-lease" ? { migration: "operator-pending" } : {}),
|
|
192
|
+
next: "The governed operator/controller owns teardown: it proves original claim, provider and owned-volume absence before releasing custody."
|
|
193
|
+
};
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=claim-custody.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-custody.js","sourceRoot":"","sources":["../../../src/tools/claim-custody.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type ClaimMethod = "GET" | "POST" | "DELETE";
|
|
2
|
+
export interface ClaimApi {
|
|
3
|
+
request(path: string, method: ClaimMethod, body?: unknown): Promise<any | null>;
|
|
4
|
+
}
|
|
5
|
+
export declare class ClaimApiError extends Error {
|
|
6
|
+
readonly status: number | null;
|
|
7
|
+
constructor(status: number | null, message: string);
|
|
8
|
+
}
|
|
9
|
+
export declare function assertClaimPath(path: string, method: ClaimMethod): void;
|
|
10
|
+
/** Names the denied operation from the request itself. Remote error text is never echoed. */
|
|
11
|
+
export declare function describeClaimRequest(path: string, method: ClaimMethod): string;
|
|
12
|
+
export declare function claimFailureMessage(status: number | null, path: string, method: ClaimMethod): string;
|
|
13
|
+
export declare function createClaimApi(): ClaimApi;
|
|
14
|
+
//# sourceMappingURL=claim-kubernetes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-kubernetes.d.ts","sourceRoot":"","sources":["../../../src/tools/claim-kubernetes.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;AACpD,MAAM,WAAW,QAAQ;IAAG,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;CAAE;AAC9G,qBAAa,aAAc,SAAQ,KAAK;aACV,MAAM,EAAE,MAAM,GAAG,IAAI;gBAArB,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM;CACnE;AAGD,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAcvE;AACD,6FAA6F;AAC7F,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAO9E;AACD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,MAAM,CAIpG;AAOD,wBAAgB,cAAc,IAAI,QAAQ,CAgCzC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/** Shared configured Kubernetes transport for
|
|
1
|
+
/** Shared configured Kubernetes transport for ApplianceClaim custody (REL-1746). Never retry an unknown write. Leases are read-only legacy evidence. */
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import { fetch, Agent } from "undici";
|
|
4
4
|
import { KubernetesCommandError, resolveLabKubernetesBackend, runLabKubectl } from "./kubernetes-backend.js";
|
|
5
|
-
export class
|
|
5
|
+
export class ClaimApiError extends Error {
|
|
6
6
|
status;
|
|
7
7
|
constructor(status, message) {
|
|
8
8
|
super(message);
|
|
@@ -11,68 +11,72 @@ export class SlotApiError extends Error {
|
|
|
11
11
|
}
|
|
12
12
|
const MAX_BYTES = 1_048_576;
|
|
13
13
|
const COLLECTIONS = ["/apis/coordination.k8s.io/v1/namespaces/ct-lab/leases", "/apis/calltelemetry.com/v1alpha1/namespaces/ct-lab/applianceclaims"];
|
|
14
|
-
export function
|
|
14
|
+
export function assertClaimPath(path, method) {
|
|
15
15
|
const cleanPath = path.split("?")[0];
|
|
16
16
|
if (path !== cleanPath && !/^\?limit=500(?:&continue=[A-Za-z0-9%._~-]+)?$/.test(path.slice(cleanPath.length)))
|
|
17
|
-
throw new Error("
|
|
17
|
+
throw new Error("claim custody query refused");
|
|
18
18
|
const collection = COLLECTIONS.find(p => cleanPath === p || cleanPath.startsWith(p + "/"));
|
|
19
19
|
if (!collection)
|
|
20
|
-
throw new Error("
|
|
20
|
+
throw new Error("claim custody API path refused");
|
|
21
21
|
const name = cleanPath.slice(collection.length + 1);
|
|
22
22
|
if (cleanPath !== collection && (name.length > 253 || !name.split(".").every(label => /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(label))))
|
|
23
|
-
throw new Error("
|
|
24
|
-
if (
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
23
|
+
throw new Error("claim custody object name refused");
|
|
24
|
+
if (collection === COLLECTIONS[0]) {
|
|
25
|
+
// Legacy reservation Leases are read-only evidence: the operator owns migration and deletion.
|
|
26
|
+
if (method !== "GET" || cleanPath === collection || !name.startsWith("ct-physical-slot-reservation."))
|
|
27
|
+
throw new Error("MCP never creates, renews or releases a Lease; only legacy reservation reads are allowed");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (!["GET", "POST", "DELETE"].includes(method))
|
|
31
|
+
throw new Error("claim custody method refused");
|
|
32
|
+
if (method === "POST" && cleanPath !== collection || method !== "POST" && cleanPath === collection && method !== "GET")
|
|
33
|
+
throw new Error("claim custody method refused");
|
|
30
34
|
}
|
|
31
35
|
/** Names the denied operation from the request itself. Remote error text is never echoed. */
|
|
32
|
-
export function
|
|
36
|
+
export function describeClaimRequest(path, method) {
|
|
33
37
|
const cleanPath = path.split("?")[0];
|
|
34
38
|
const match = /^\/apis\/([^/]+)\/[^/]+\/namespaces\/([^/]+)\/([^/]+)(\/[^/]+)?$/.exec(cleanPath);
|
|
35
39
|
if (!match)
|
|
36
40
|
return "an unrecognised resource";
|
|
37
41
|
const [, group, namespace, resource, named] = match;
|
|
38
|
-
const verb = method === "GET" ? (named ? "get" : "list") : method === "POST" ? "create" :
|
|
42
|
+
const verb = method === "GET" ? (named ? "get" : "list") : method === "POST" ? "create" : "delete";
|
|
39
43
|
return `${verb} ${resource}.${group} in namespace ${namespace}`;
|
|
40
44
|
}
|
|
41
|
-
export function
|
|
42
|
-
const detail =
|
|
45
|
+
export function claimFailureMessage(status, path, method) {
|
|
46
|
+
const detail = describeClaimRequest(path, method);
|
|
43
47
|
const hint = status === 403 ? `; the service account needs RBAC to ${detail}` : "";
|
|
44
|
-
return `
|
|
48
|
+
return `claim custody Kubernetes request failed (${status ?? "unknown"}) on ${detail}${hint}; custody unchanged`;
|
|
45
49
|
}
|
|
46
50
|
function parse(text) {
|
|
47
51
|
if (Buffer.byteLength(text) > MAX_BYTES)
|
|
48
|
-
throw new Error("
|
|
52
|
+
throw new Error("claim custody response exceeds bound");
|
|
49
53
|
const value = JSON.parse(text);
|
|
50
54
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
51
|
-
throw new Error("
|
|
55
|
+
throw new Error("claim custody response is not an object");
|
|
52
56
|
return value;
|
|
53
57
|
}
|
|
54
|
-
export function
|
|
58
|
+
export function createClaimApi() {
|
|
55
59
|
return { async request(path, method, body) {
|
|
56
|
-
|
|
60
|
+
assertClaimPath(path, method);
|
|
57
61
|
const backend = resolveLabKubernetesBackend();
|
|
58
62
|
if (backend.mode === "in-cluster") {
|
|
59
63
|
const token = fs.readFileSync(backend.tokenPath, "utf8").trim();
|
|
60
64
|
if (!token)
|
|
61
|
-
throw new Error("
|
|
65
|
+
throw new Error("claim custody Kubernetes service identity refused");
|
|
62
66
|
const agent = new Agent({ connect: { ca: fs.readFileSync(backend.caPath), rejectUnauthorized: true } });
|
|
63
67
|
try {
|
|
64
68
|
const response = await fetch(`${backend.server}${path}`, { method, headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" }, body: body === undefined ? undefined : JSON.stringify(body), dispatcher: agent, signal: AbortSignal.timeout(10_000) });
|
|
65
69
|
if (response.status === 404 && method === "GET")
|
|
66
70
|
return null;
|
|
67
71
|
if (!response.ok)
|
|
68
|
-
throw new
|
|
72
|
+
throw new ClaimApiError(response.status, claimFailureMessage(response.status, path, method));
|
|
69
73
|
return parse(await response.text());
|
|
70
74
|
}
|
|
71
75
|
finally {
|
|
72
76
|
await agent.close();
|
|
73
77
|
}
|
|
74
78
|
}
|
|
75
|
-
const command = method === "GET" ? "get" : method === "POST" ? "create" :
|
|
79
|
+
const command = method === "GET" ? "get" : method === "POST" ? "create" : "delete";
|
|
76
80
|
try {
|
|
77
81
|
const result = await runLabKubectl([command, "--raw", path, ...(body === undefined ? [] : ["-f", "-"])], {
|
|
78
82
|
...(body === undefined ? {} : { input: JSON.stringify(body) }),
|
|
@@ -86,10 +90,10 @@ export function createSlotApi() {
|
|
|
86
90
|
if (method === "GET" && status === 404)
|
|
87
91
|
return null;
|
|
88
92
|
if (error instanceof KubernetesCommandError) {
|
|
89
|
-
throw new
|
|
93
|
+
throw new ClaimApiError(status, claimFailureMessage(status, path, method));
|
|
90
94
|
}
|
|
91
95
|
throw error;
|
|
92
96
|
}
|
|
93
97
|
} };
|
|
94
98
|
}
|
|
95
|
-
//# sourceMappingURL=
|
|
99
|
+
//# sourceMappingURL=claim-kubernetes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-kubernetes.js","sourceRoot":"","sources":["../../../src/tools/claim-kubernetes.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MCP Tool: ferrum_fleet_metrics
|
|
3
3
|
*
|
|
4
|
-
* Query fleet-wide hypervisor metrics
|
|
5
|
-
*
|
|
4
|
+
* Query fleet-wide hypervisor metrics and Prometheus capacity alerts across Proxmox
|
|
5
|
+
* standalone nodes (proxmox-lab, proxmox-minis-lab, proxmox-ibm64). Free RAM, CPU and
|
|
6
|
+
* disk capacity are the authoritative figures (REL-1746); the legacy 8GB slot floor/max
|
|
7
|
+
* numbers are kept as deprecated, non-authoritative fields.
|
|
6
8
|
*/
|
|
7
9
|
import { z } from "zod";
|
|
8
10
|
import { CallToolResult } from "../types/mcp.js";
|
|
9
11
|
import { ToolContext } from "./list-hosts.js";
|
|
12
|
+
import { type CanonicalProxmoxHost } from "./proxmox-host-policy.js";
|
|
10
13
|
export declare const FerrumFleetMetricsInputShape: {
|
|
11
14
|
host: z.ZodDefault<z.ZodOptional<z.ZodEnum<["proxmox-lab", "proxmox-minis-lab", "proxmox-ibm64", "proxmox-mini", "all"]>>>;
|
|
12
15
|
format: z.ZodDefault<z.ZodOptional<z.ZodEnum<["summary", "json"]>>>;
|
|
@@ -22,5 +25,37 @@ export declare const FerrumFleetMetricsInputSchema: z.ZodObject<{
|
|
|
22
25
|
format?: "json" | "summary" | undefined;
|
|
23
26
|
}>;
|
|
24
27
|
export type FerrumFleetMetricsInput = z.infer<typeof FerrumFleetMetricsInputSchema>;
|
|
28
|
+
interface HostMetrics {
|
|
29
|
+
hostAssetId: string;
|
|
30
|
+
online: boolean;
|
|
31
|
+
cpuModel: string;
|
|
32
|
+
cores: number;
|
|
33
|
+
cpuUsagePct: number;
|
|
34
|
+
totalMemoryBytes: number;
|
|
35
|
+
usedMemoryBytes: number;
|
|
36
|
+
freeMemoryBytes: number;
|
|
37
|
+
memoryUsagePct: number;
|
|
38
|
+
storagePool: string;
|
|
39
|
+
totalStorageBytes: number;
|
|
40
|
+
usedStorageBytes: number;
|
|
41
|
+
availStorageBytes: number;
|
|
42
|
+
storageUsagePct: number;
|
|
43
|
+
/** Authoritative live capacity (REL-1746). */
|
|
44
|
+
freeCpuCores: number;
|
|
45
|
+
freeStorageBytes: number;
|
|
46
|
+
capacity: {
|
|
47
|
+
freeMemoryBytes: number;
|
|
48
|
+
freeCpuCores: number;
|
|
49
|
+
freeStorageBytes: number;
|
|
50
|
+
authoritative: true;
|
|
51
|
+
};
|
|
52
|
+
/** @deprecated Slots are retired; numeric compatibility field only. */
|
|
53
|
+
slotFloor: number;
|
|
54
|
+
/** @deprecated Slots are retired; numeric compatibility field only. */
|
|
55
|
+
slotHardMax: number;
|
|
56
|
+
alerts: string[];
|
|
57
|
+
}
|
|
58
|
+
export declare function parseCapacityPayload(hostAssetId: CanonicalProxmoxHost, stdout: string, storage?: string): HostMetrics;
|
|
25
59
|
export declare function handleFerrumFleetMetrics(rawArgs: unknown, context: ToolContext): Promise<CallToolResult>;
|
|
60
|
+
export {};
|
|
26
61
|
//# sourceMappingURL=fleet-metrics.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fleet-metrics.d.ts","sourceRoot":"","sources":["../../../src/tools/fleet-metrics.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"fleet-metrics.d.ts","sourceRoot":"","sources":["../../../src/tools/fleet-metrics.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,cAAc,EAIf,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EAIL,KAAK,oBAAoB,EAC1B,MAAM,0BAA0B,CAAC;AAElC,eAAO,MAAM,4BAA4B;;;CAUxC,CAAC;AAEF,eAAO,MAAM,6BAA6B;;;;;;;;;EAAyC,CAAC;AACpF,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAEpF,UAAU,WAAW;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE;QAAE,eAAe,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,IAAI,CAAA;KAAE,CAAC;IAC3G,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AASD,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,CA2ErH;AA6DD,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,CAAC,CAmDzB"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* MCP Tool: ferrum_fleet_metrics
|
|
3
3
|
*
|
|
4
|
-
* Query fleet-wide hypervisor metrics
|
|
5
|
-
*
|
|
4
|
+
* Query fleet-wide hypervisor metrics and Prometheus capacity alerts across Proxmox
|
|
5
|
+
* standalone nodes (proxmox-lab, proxmox-minis-lab, proxmox-ibm64). Free RAM, CPU and
|
|
6
|
+
* disk capacity are the authoritative figures (REL-1746); the legacy 8GB slot floor/max
|
|
7
|
+
* numbers are kept as deprecated, non-authoritative fields.
|
|
6
8
|
*/
|
|
7
9
|
import { z } from "zod";
|
|
8
10
|
import { ProxmoxErrorCode, createToolErrorResult, createToolSuccessResult, } from "../types/mcp.js";
|
|
@@ -20,7 +22,13 @@ export const FerrumFleetMetricsInputShape = {
|
|
|
20
22
|
.describe("Output format: 'summary' (formatted Markdown report with alert status) or 'json' (raw structured telemetry)."),
|
|
21
23
|
};
|
|
22
24
|
export const FerrumFleetMetricsInputSchema = z.object(FerrumFleetMetricsInputShape);
|
|
23
|
-
|
|
25
|
+
const freeCpu = (cores, usagePct) => Math.max(0, Number((cores * (1 - Math.min(100, Math.max(0, usagePct)) / 100)).toFixed(2)));
|
|
26
|
+
function withCapacity(m) {
|
|
27
|
+
const freeCpuCores = freeCpu(m.cores, m.cpuUsagePct);
|
|
28
|
+
return { ...m, freeCpuCores, freeStorageBytes: m.availStorageBytes,
|
|
29
|
+
capacity: { freeMemoryBytes: m.freeMemoryBytes, freeCpuCores, freeStorageBytes: m.availStorageBytes, authoritative: true } };
|
|
30
|
+
}
|
|
31
|
+
export function parseCapacityPayload(hostAssetId, stdout, storage) {
|
|
24
32
|
const policy = proxmoxSlotPolicy(hostAssetId);
|
|
25
33
|
const alerts = [];
|
|
26
34
|
try {
|
|
@@ -36,8 +44,8 @@ function parseCapacityPayload(hostAssetId, stdout) {
|
|
|
36
44
|
const usedMem = Number(memory.used || 0);
|
|
37
45
|
const freeMem = Number(memory.free || memory.available || 0);
|
|
38
46
|
const memPct = totalMem > 0 ? (usedMem / totalMem) * 100 : 0;
|
|
39
|
-
const lvm = storageList.find((s) => s.storage === policy.storage) || storageList[0] || {
|
|
40
|
-
storage: policy.storage,
|
|
47
|
+
const lvm = storageList.find((s) => s.storage === (storage ?? policy.storage)) || (storage ? undefined : storageList[0]) || {
|
|
48
|
+
storage: storage ?? policy.storage,
|
|
41
49
|
used: 0,
|
|
42
50
|
total: 0,
|
|
43
51
|
avail: 0,
|
|
@@ -51,7 +59,7 @@ function parseCapacityPayload(hostAssetId, stdout) {
|
|
|
51
59
|
alerts.push(`Memory usage critical: ${memPct.toFixed(1)}% >= 85%`);
|
|
52
60
|
if (storagePct >= 85)
|
|
53
61
|
alerts.push(`Storage pool ${lvm.storage} critical: ${storagePct.toFixed(1)}% >= 85%`);
|
|
54
|
-
return {
|
|
62
|
+
return withCapacity({
|
|
55
63
|
hostAssetId,
|
|
56
64
|
online: true,
|
|
57
65
|
cpuModel: cpuinfo.model || "Unknown CPU",
|
|
@@ -69,10 +77,10 @@ function parseCapacityPayload(hostAssetId, stdout) {
|
|
|
69
77
|
slotFloor: policy.floor,
|
|
70
78
|
slotHardMax: policy.hardMax,
|
|
71
79
|
alerts,
|
|
72
|
-
};
|
|
80
|
+
});
|
|
73
81
|
}
|
|
74
82
|
catch {
|
|
75
|
-
return {
|
|
83
|
+
return withCapacity({
|
|
76
84
|
hostAssetId,
|
|
77
85
|
online: false,
|
|
78
86
|
cpuModel: "Unavailable",
|
|
@@ -90,12 +98,12 @@ function parseCapacityPayload(hostAssetId, stdout) {
|
|
|
90
98
|
slotFloor: policy.floor,
|
|
91
99
|
slotHardMax: policy.hardMax,
|
|
92
100
|
alerts: ["Failed to parse host capacity telemetry"],
|
|
93
|
-
};
|
|
101
|
+
});
|
|
94
102
|
}
|
|
95
103
|
}
|
|
96
104
|
function unavailableHostMetrics(hostAssetId, alert) {
|
|
97
105
|
const policy = proxmoxSlotPolicy(hostAssetId);
|
|
98
|
-
return {
|
|
106
|
+
return withCapacity({
|
|
99
107
|
hostAssetId,
|
|
100
108
|
online: false,
|
|
101
109
|
cpuModel: "Unknown",
|
|
@@ -113,7 +121,7 @@ function unavailableHostMetrics(hostAssetId, alert) {
|
|
|
113
121
|
slotFloor: policy.floor,
|
|
114
122
|
slotHardMax: policy.hardMax,
|
|
115
123
|
alerts: [alert],
|
|
116
|
-
};
|
|
124
|
+
});
|
|
117
125
|
}
|
|
118
126
|
function formatBytes(bytes) {
|
|
119
127
|
if (bytes === 0)
|
|
@@ -125,15 +133,17 @@ function formatSummary(metrics) {
|
|
|
125
133
|
const lines = [
|
|
126
134
|
"# Ferrum Proxmox Fleet Metrics & Telemetry",
|
|
127
135
|
"",
|
|
128
|
-
"| Host Asset ID | Status | Cores | CPU % | Memory (Used / Total) | RAM % | Storage (Used / Total) | Pool % |
|
|
129
|
-
"| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |",
|
|
136
|
+
"| Host Asset ID | Status | Cores | CPU % | Free CPU | Free RAM | Memory (Used / Total) | RAM % | Free Disk | Storage (Used / Total) | Pool % | Alerts |",
|
|
137
|
+
"| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |",
|
|
130
138
|
];
|
|
131
139
|
for (const m of metrics) {
|
|
132
140
|
const status = m.online ? "🟢 Online" : "🔴 Offline";
|
|
133
141
|
const alertText = m.alerts.length > 0 ? `⚠️ ${m.alerts.join("; ")}` : "✅ Healthy";
|
|
134
|
-
lines.push(`| **${m.hostAssetId}** | ${status} | ${m.cores} | ${m.cpuUsagePct.toFixed(1)}% | ${formatBytes(m.usedMemoryBytes)} / ${formatBytes(m.totalMemoryBytes)} | ${m.memoryUsagePct.toFixed(1)}% | ${formatBytes(m.usedStorageBytes)} / ${formatBytes(m.totalStorageBytes)} | ${m.storageUsagePct.toFixed(1)}% | ${
|
|
142
|
+
lines.push(`| **${m.hostAssetId}** | ${status} | ${m.cores} | ${m.cpuUsagePct.toFixed(1)}% | ${m.freeCpuCores} | ${formatBytes(m.freeMemoryBytes)} | ${formatBytes(m.usedMemoryBytes)} / ${formatBytes(m.totalMemoryBytes)} | ${m.memoryUsagePct.toFixed(1)}% | ${formatBytes(m.availStorageBytes)} | ${formatBytes(m.usedStorageBytes)} / ${formatBytes(m.totalStorageBytes)} | ${m.storageUsagePct.toFixed(1)}% | ${alertText} |`);
|
|
135
143
|
}
|
|
136
144
|
lines.push("");
|
|
145
|
+
lines.push("Capacity is real host capacity (free RAM, CPU and disk). Legacy 8GB slot floor/max numbers remain in the JSON output as deprecated, non-authoritative fields (slot retirement ADR (REL-1746)).");
|
|
146
|
+
lines.push("");
|
|
137
147
|
lines.push("### Prometheus Alert Policies");
|
|
138
148
|
lines.push("- **FerrumHostMemoryHigh**: Evaluates `(1 - free/total) >= 0.85` for 5m. Threshold: 85%.");
|
|
139
149
|
lines.push("- **FerrumHostStorageHigh**: Evaluates storage pool utilization `>= 0.85`. Threshold: 85%.");
|
|
@@ -172,7 +182,7 @@ export async function handleFerrumFleetMetrics(rawArgs, context) {
|
|
|
172
182
|
}
|
|
173
183
|
}
|
|
174
184
|
if (format === "json") {
|
|
175
|
-
return createToolSuccessResult(JSON.stringify({ schema: "calltelemetry-ferrum-fleet-metrics/v1", metrics: results }, null, 2), {
|
|
185
|
+
return createToolSuccessResult(JSON.stringify({ schema: "calltelemetry-ferrum-fleet-metrics/v1", capacityAuthority: "host-free-ram-cpu-disk", slotFieldsDeprecated: true, metrics: results }, null, 2), {
|
|
176
186
|
metrics: results,
|
|
177
187
|
});
|
|
178
188
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fleet-metrics.js","sourceRoot":"","sources":["../../../src/tools/fleet-metrics.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"fleet-metrics.js","sourceRoot":"","sources":["../../../src/tools/fleet-metrics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capacity-based host selection (REL-1746, supersedes slot-tier placement).
|
|
3
|
+
*
|
|
4
|
+
* Eligible Proxmox hosts are ranked by live free RAM headroom, then free disk headroom, then host id
|
|
5
|
+
* (stable tie-break). Eligibility (online, verified resources, protected-host and credential rules) is
|
|
6
|
+
* decided by the caller; this module only orders hosts that already passed it. Slot counts and the old
|
|
7
|
+
* "Mini first" tier boost play no part in the order. ESXi (retired) is always last when allowed at all.
|
|
8
|
+
*/
|
|
9
|
+
export interface HostCapacityCandidate {
|
|
10
|
+
hostId: string;
|
|
11
|
+
/** Live free RAM in MB (verified reading, not a registered slot size). */
|
|
12
|
+
freeMemoryMb: number;
|
|
13
|
+
/** Live free disk headroom in GB for the guest's storage. */
|
|
14
|
+
freeDiskGb: number;
|
|
15
|
+
/** ESXi is a retired fallback; it sorts after every Proxmox host. */
|
|
16
|
+
fallback?: boolean;
|
|
17
|
+
/** True when the host cannot take the requested guest right now; such hosts sort after every host that can. */
|
|
18
|
+
unavailable?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare function compareHostCapacity(a: HostCapacityCandidate, b: HostCapacityCandidate): number;
|
|
21
|
+
/** Returns a new array ordered best-capacity first. */
|
|
22
|
+
export declare function rankHostsByCapacity<T extends HostCapacityCandidate>(candidates: readonly T[]): T[];
|
|
23
|
+
/** Informational numeric score that agrees with the order (kept for the legacy `score` field). */
|
|
24
|
+
export declare function capacityScore(candidate: HostCapacityCandidate): number;
|
|
25
|
+
//# sourceMappingURL=host-capacity-rank.d.ts.map
|