@calltelemetry/ct-lab-mcp 0.4.2 → 0.4.4
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 +16 -0
- package/dist/bin/ct-lab-mcp.js +118 -3
- package/dist/src/crd/reconciler.d.ts +41 -0
- package/dist/src/crd/reconciler.d.ts.map +1 -0
- package/dist/src/crd/reconciler.js +302 -0
- package/dist/src/crd/reconciler.js.map +1 -0
- package/dist/src/crd/types.d.ts +101 -0
- package/dist/src/crd/types.d.ts.map +1 -0
- package/dist/src/crd/types.js +7 -0
- package/dist/src/crd/types.js.map +1 -0
- package/dist/src/hypervisors/esxi/client.d.ts.map +1 -1
- package/dist/src/hypervisors/esxi/client.js +6 -7
- package/dist/src/hypervisors/esxi/client.js.map +1 -1
- package/dist/src/hypervisors/proxmox/client.d.ts +2 -0
- package/dist/src/hypervisors/proxmox/client.d.ts.map +1 -1
- package/dist/src/hypervisors/proxmox/client.js +90 -14
- package/dist/src/hypervisors/proxmox/client.js.map +1 -1
- package/dist/src/proxmox/mock.d.ts.map +1 -1
- package/dist/src/proxmox/mock.js +46 -0
- package/dist/src/proxmox/mock.js.map +1 -1
- package/dist/src/server.js +2 -2
- package/dist/src/server.js.map +1 -1
- package/dist/src/slot/manager.d.ts.map +1 -1
- package/dist/src/slot/manager.js +16 -13
- package/dist/src/slot/manager.js.map +1 -1
- package/dist/src/slot/purge-controller.d.ts +63 -0
- package/dist/src/slot/purge-controller.d.ts.map +1 -0
- package/dist/src/slot/purge-controller.js +119 -0
- package/dist/src/slot/purge-controller.js.map +1 -0
- package/dist/src/slot/types.d.ts +6 -0
- package/dist/src/slot/types.d.ts.map +1 -1
- package/dist/src/slot/types.js +11 -0
- package/dist/src/slot/types.js.map +1 -1
- package/dist/src/tools/index.d.ts +1 -0
- package/dist/src/tools/index.d.ts.map +1 -1
- package/dist/src/tools/index.js +26 -1
- package/dist/src/tools/index.js.map +1 -1
- package/dist/src/tools/list-hosts.d.ts +2 -0
- package/dist/src/tools/list-hosts.d.ts.map +1 -1
- package/dist/src/tools/list-hosts.js.map +1 -1
- package/dist/src/tools/manifest-tools.d.ts +38 -0
- package/dist/src/tools/manifest-tools.d.ts.map +1 -0
- package/dist/src/tools/manifest-tools.js +148 -0
- package/dist/src/tools/manifest-tools.js.map +1 -0
- package/dist/src/tools/provision-tools.d.ts +28 -8
- package/dist/src/tools/provision-tools.d.ts.map +1 -1
- package/dist/src/tools/provision-tools.js +153 -25
- package/dist/src/tools/provision-tools.js.map +1 -1
- package/dist/src/tools/slot-tools.d.ts.map +1 -1
- package/dist/src/tools/slot-tools.js +9 -3
- package/dist/src/tools/slot-tools.js.map +1 -1
- package/dist/src/tools/vm-cleanup.d.ts +79 -0
- package/dist/src/tools/vm-cleanup.d.ts.map +1 -0
- package/dist/src/tools/vm-cleanup.js +102 -0
- package/dist/src/tools/vm-cleanup.js.map +1 -0
- package/package.json +3 -1
|
@@ -59,6 +59,8 @@ export declare const ProvisionVmInputShape: {
|
|
|
59
59
|
tags: z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodString, "many">, z.ZodString]>>;
|
|
60
60
|
requester: z.ZodOptional<z.ZodString>;
|
|
61
61
|
fullClone: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
62
|
+
waitForReady: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
63
|
+
readyTimeoutSeconds: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
62
64
|
description: z.ZodOptional<z.ZodString>;
|
|
63
65
|
};
|
|
64
66
|
export declare const ProvisionVmInputSchema: z.ZodEffects<z.ZodEffects<z.ZodObject<{
|
|
@@ -113,6 +115,8 @@ export declare const ProvisionVmInputSchema: z.ZodEffects<z.ZodEffects<z.ZodObje
|
|
|
113
115
|
tags: z.ZodOptional<z.ZodUnion<[z.ZodArray<z.ZodString, "many">, z.ZodString]>>;
|
|
114
116
|
requester: z.ZodOptional<z.ZodString>;
|
|
115
117
|
fullClone: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
118
|
+
waitForReady: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
119
|
+
readyTimeoutSeconds: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
116
120
|
description: z.ZodOptional<z.ZodString>;
|
|
117
121
|
}, "strip", z.ZodTypeAny, {
|
|
118
122
|
hypervisorType: "proxmox" | "esxi" | "any";
|
|
@@ -121,6 +125,8 @@ export declare const ProvisionVmInputSchema: z.ZodEffects<z.ZodEffects<z.ZodObje
|
|
|
121
125
|
recoverIncomplete: boolean;
|
|
122
126
|
startOnDeploy: boolean;
|
|
123
127
|
fullClone: boolean;
|
|
128
|
+
waitForReady: boolean;
|
|
129
|
+
readyTimeoutSeconds: number;
|
|
124
130
|
storage?: string | undefined;
|
|
125
131
|
name?: string | undefined;
|
|
126
132
|
cores?: number | undefined;
|
|
@@ -146,9 +152,9 @@ export declare const ProvisionVmInputSchema: z.ZodEffects<z.ZodEffects<z.ZodObje
|
|
|
146
152
|
confirmAssetId?: string | undefined;
|
|
147
153
|
targetHost?: string | undefined;
|
|
148
154
|
hostAssetId?: string | undefined;
|
|
155
|
+
outputFile?: string | undefined;
|
|
149
156
|
templateAssetId?: string | undefined;
|
|
150
157
|
diskGb?: number | undefined;
|
|
151
|
-
outputFile?: string | undefined;
|
|
152
158
|
workspaceRoot?: string | undefined;
|
|
153
159
|
nativeCliDir?: string | undefined;
|
|
154
160
|
importStorage?: string | undefined;
|
|
@@ -195,10 +201,10 @@ export declare const ProvisionVmInputSchema: z.ZodEffects<z.ZodEffects<z.ZodObje
|
|
|
195
201
|
confirmAssetId?: string | undefined;
|
|
196
202
|
targetHost?: string | undefined;
|
|
197
203
|
hostAssetId?: string | undefined;
|
|
204
|
+
outputFile?: string | undefined;
|
|
198
205
|
templateAssetId?: string | undefined;
|
|
199
206
|
source?: "template" | "alma97-cloud" | "alma97-iso" | undefined;
|
|
200
207
|
diskGb?: number | undefined;
|
|
201
|
-
outputFile?: string | undefined;
|
|
202
208
|
workspaceRoot?: string | undefined;
|
|
203
209
|
nativeCliDir?: string | undefined;
|
|
204
210
|
importStorage?: string | undefined;
|
|
@@ -220,6 +226,8 @@ export declare const ProvisionVmInputSchema: z.ZodEffects<z.ZodEffects<z.ZodObje
|
|
|
220
226
|
networkBridge?: string | undefined;
|
|
221
227
|
startOnDeploy?: boolean | undefined;
|
|
222
228
|
fullClone?: boolean | undefined;
|
|
229
|
+
waitForReady?: boolean | undefined;
|
|
230
|
+
readyTimeoutSeconds?: number | undefined;
|
|
223
231
|
}>, {
|
|
224
232
|
hypervisorType: "proxmox" | "esxi" | "any";
|
|
225
233
|
apply: boolean;
|
|
@@ -227,6 +235,8 @@ export declare const ProvisionVmInputSchema: z.ZodEffects<z.ZodEffects<z.ZodObje
|
|
|
227
235
|
recoverIncomplete: boolean;
|
|
228
236
|
startOnDeploy: boolean;
|
|
229
237
|
fullClone: boolean;
|
|
238
|
+
waitForReady: boolean;
|
|
239
|
+
readyTimeoutSeconds: number;
|
|
230
240
|
storage?: string | undefined;
|
|
231
241
|
name?: string | undefined;
|
|
232
242
|
cores?: number | undefined;
|
|
@@ -252,9 +262,9 @@ export declare const ProvisionVmInputSchema: z.ZodEffects<z.ZodEffects<z.ZodObje
|
|
|
252
262
|
confirmAssetId?: string | undefined;
|
|
253
263
|
targetHost?: string | undefined;
|
|
254
264
|
hostAssetId?: string | undefined;
|
|
265
|
+
outputFile?: string | undefined;
|
|
255
266
|
templateAssetId?: string | undefined;
|
|
256
267
|
diskGb?: number | undefined;
|
|
257
|
-
outputFile?: string | undefined;
|
|
258
268
|
workspaceRoot?: string | undefined;
|
|
259
269
|
nativeCliDir?: string | undefined;
|
|
260
270
|
importStorage?: string | undefined;
|
|
@@ -301,10 +311,10 @@ export declare const ProvisionVmInputSchema: z.ZodEffects<z.ZodEffects<z.ZodObje
|
|
|
301
311
|
confirmAssetId?: string | undefined;
|
|
302
312
|
targetHost?: string | undefined;
|
|
303
313
|
hostAssetId?: string | undefined;
|
|
314
|
+
outputFile?: string | undefined;
|
|
304
315
|
templateAssetId?: string | undefined;
|
|
305
316
|
source?: "template" | "alma97-cloud" | "alma97-iso" | undefined;
|
|
306
317
|
diskGb?: number | undefined;
|
|
307
|
-
outputFile?: string | undefined;
|
|
308
318
|
workspaceRoot?: string | undefined;
|
|
309
319
|
nativeCliDir?: string | undefined;
|
|
310
320
|
importStorage?: string | undefined;
|
|
@@ -326,6 +336,8 @@ export declare const ProvisionVmInputSchema: z.ZodEffects<z.ZodEffects<z.ZodObje
|
|
|
326
336
|
networkBridge?: string | undefined;
|
|
327
337
|
startOnDeploy?: boolean | undefined;
|
|
328
338
|
fullClone?: boolean | undefined;
|
|
339
|
+
waitForReady?: boolean | undefined;
|
|
340
|
+
readyTimeoutSeconds?: number | undefined;
|
|
329
341
|
}>, {
|
|
330
342
|
hypervisorType: "proxmox" | "esxi" | "any";
|
|
331
343
|
apply: boolean;
|
|
@@ -333,6 +345,8 @@ export declare const ProvisionVmInputSchema: z.ZodEffects<z.ZodEffects<z.ZodObje
|
|
|
333
345
|
recoverIncomplete: boolean;
|
|
334
346
|
startOnDeploy: boolean;
|
|
335
347
|
fullClone: boolean;
|
|
348
|
+
waitForReady: boolean;
|
|
349
|
+
readyTimeoutSeconds: number;
|
|
336
350
|
storage?: string | undefined;
|
|
337
351
|
name?: string | undefined;
|
|
338
352
|
cores?: number | undefined;
|
|
@@ -358,9 +372,9 @@ export declare const ProvisionVmInputSchema: z.ZodEffects<z.ZodEffects<z.ZodObje
|
|
|
358
372
|
confirmAssetId?: string | undefined;
|
|
359
373
|
targetHost?: string | undefined;
|
|
360
374
|
hostAssetId?: string | undefined;
|
|
375
|
+
outputFile?: string | undefined;
|
|
361
376
|
templateAssetId?: string | undefined;
|
|
362
377
|
diskGb?: number | undefined;
|
|
363
|
-
outputFile?: string | undefined;
|
|
364
378
|
workspaceRoot?: string | undefined;
|
|
365
379
|
nativeCliDir?: string | undefined;
|
|
366
380
|
importStorage?: string | undefined;
|
|
@@ -407,10 +421,10 @@ export declare const ProvisionVmInputSchema: z.ZodEffects<z.ZodEffects<z.ZodObje
|
|
|
407
421
|
confirmAssetId?: string | undefined;
|
|
408
422
|
targetHost?: string | undefined;
|
|
409
423
|
hostAssetId?: string | undefined;
|
|
424
|
+
outputFile?: string | undefined;
|
|
410
425
|
templateAssetId?: string | undefined;
|
|
411
426
|
source?: "template" | "alma97-cloud" | "alma97-iso" | undefined;
|
|
412
427
|
diskGb?: number | undefined;
|
|
413
|
-
outputFile?: string | undefined;
|
|
414
428
|
workspaceRoot?: string | undefined;
|
|
415
429
|
nativeCliDir?: string | undefined;
|
|
416
430
|
importStorage?: string | undefined;
|
|
@@ -432,6 +446,8 @@ export declare const ProvisionVmInputSchema: z.ZodEffects<z.ZodEffects<z.ZodObje
|
|
|
432
446
|
networkBridge?: string | undefined;
|
|
433
447
|
startOnDeploy?: boolean | undefined;
|
|
434
448
|
fullClone?: boolean | undefined;
|
|
449
|
+
waitForReady?: boolean | undefined;
|
|
450
|
+
readyTimeoutSeconds?: number | undefined;
|
|
435
451
|
}>;
|
|
436
452
|
export type ProvisionVmInput = z.infer<typeof ProvisionVmInputSchema>;
|
|
437
453
|
export type ProvisionVmRawInput = z.input<typeof ProvisionVmInputSchema>;
|
|
@@ -487,6 +503,7 @@ export declare const AutoProvisionVmInputShape: {
|
|
|
487
503
|
ipAddress: z.ZodOptional<z.ZodString>;
|
|
488
504
|
networkBridge: z.ZodOptional<z.ZodString>;
|
|
489
505
|
storage: z.ZodOptional<z.ZodString>;
|
|
506
|
+
fullClone: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
490
507
|
waitForReady: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
491
508
|
readyTimeoutSeconds: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
492
509
|
};
|
|
@@ -541,6 +558,7 @@ export declare const AutoProvisionVmInputSchema: z.ZodObject<{
|
|
|
541
558
|
ipAddress: z.ZodOptional<z.ZodString>;
|
|
542
559
|
networkBridge: z.ZodOptional<z.ZodString>;
|
|
543
560
|
storage: z.ZodOptional<z.ZodString>;
|
|
561
|
+
fullClone: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
544
562
|
waitForReady: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
545
563
|
readyTimeoutSeconds: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
546
564
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -549,6 +567,7 @@ export declare const AutoProvisionVmInputSchema: z.ZodObject<{
|
|
|
549
567
|
apply: boolean;
|
|
550
568
|
source: "template" | "alma97-cloud" | "alma97-iso";
|
|
551
569
|
recoverIncomplete: boolean;
|
|
570
|
+
fullClone: boolean;
|
|
552
571
|
waitForReady: boolean;
|
|
553
572
|
readyTimeoutSeconds: number;
|
|
554
573
|
storage?: string | undefined;
|
|
@@ -577,9 +596,9 @@ export declare const AutoProvisionVmInputSchema: z.ZodObject<{
|
|
|
577
596
|
confirmAssetId?: string | undefined;
|
|
578
597
|
targetHost?: string | undefined;
|
|
579
598
|
hostAssetId?: string | undefined;
|
|
599
|
+
outputFile?: string | undefined;
|
|
580
600
|
templateAssetId?: string | undefined;
|
|
581
601
|
diskGb?: number | undefined;
|
|
582
|
-
outputFile?: string | undefined;
|
|
583
602
|
workspaceRoot?: string | undefined;
|
|
584
603
|
nativeCliDir?: string | undefined;
|
|
585
604
|
importStorage?: string | undefined;
|
|
@@ -626,10 +645,10 @@ export declare const AutoProvisionVmInputSchema: z.ZodObject<{
|
|
|
626
645
|
confirmAssetId?: string | undefined;
|
|
627
646
|
targetHost?: string | undefined;
|
|
628
647
|
hostAssetId?: string | undefined;
|
|
648
|
+
outputFile?: string | undefined;
|
|
629
649
|
templateAssetId?: string | undefined;
|
|
630
650
|
source?: "template" | "alma97-cloud" | "alma97-iso" | undefined;
|
|
631
651
|
diskGb?: number | undefined;
|
|
632
|
-
outputFile?: string | undefined;
|
|
633
652
|
workspaceRoot?: string | undefined;
|
|
634
653
|
nativeCliDir?: string | undefined;
|
|
635
654
|
importStorage?: string | undefined;
|
|
@@ -647,6 +666,7 @@ export declare const AutoProvisionVmInputSchema: z.ZodObject<{
|
|
|
647
666
|
newVmName?: string | undefined;
|
|
648
667
|
newVmId?: string | number | undefined;
|
|
649
668
|
networkBridge?: string | undefined;
|
|
669
|
+
fullClone?: boolean | undefined;
|
|
650
670
|
waitForReady?: boolean | undefined;
|
|
651
671
|
readyTimeoutSeconds?: number | undefined;
|
|
652
672
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provision-tools.d.ts","sourceRoot":"","sources":["../../../src/tools/provision-tools.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAA2C,MAAM,iBAAiB,CAAC;AAM1F,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"provision-tools.d.ts","sourceRoot":"","sources":["../../../src/tools/provision-tools.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAA2C,MAAM,iBAAiB,CAAC;AAM1F,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAc9C,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+NjC,CAAC;AAEF,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAShC,CAAC;AAEJ,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACtE,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEzE,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,mBAAmB,EAC5B,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,CAAC,CA8UzB;AAMD,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+IrC,CAAC;AAEF,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAsC,CAAC;AAC9E,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAC9E,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAEjF,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,uBAAuB,EAChC,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,CAAC,CA6czB"}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
import { ProxmoxErrorCode, createToolErrorResult } from "../types/mcp.js";
|
|
9
9
|
import { formatProvisionResult, formatAutoProvisionResult, } from "../utils/formatters.js";
|
|
10
|
-
import { CapacityExceededError, GlobalCapacityExceededError, DatastoreCapacityExceededError, } from "../slot/types.js";
|
|
10
|
+
import { CapacityExceededError, GlobalCapacityExceededError, DatastoreCapacityExceededError, HypervisorSlotUnsupportedError, } from "../slot/types.js";
|
|
11
11
|
import { ProtectedVmViolationError } from "../guardrails/types.js";
|
|
12
12
|
import { applianceSshExecutor } from "../appliance/executor.js";
|
|
13
13
|
import { MockProxmoxClient } from "../proxmox/mock.js";
|
|
@@ -216,8 +216,21 @@ export const ProvisionVmInputShape = {
|
|
|
216
216
|
fullClone: z
|
|
217
217
|
.boolean()
|
|
218
218
|
.optional()
|
|
219
|
-
.default(
|
|
220
|
-
.describe("Whether to perform a full disk clone (default:
|
|
219
|
+
.default(false)
|
|
220
|
+
.describe("Whether to perform a full disk clone (default: false for fast linked cloning)."),
|
|
221
|
+
waitForReady: z
|
|
222
|
+
.boolean()
|
|
223
|
+
.optional()
|
|
224
|
+
.default(false)
|
|
225
|
+
.describe("Whether to poll port 2222 SSH readiness after provisioning (default: false)."),
|
|
226
|
+
readyTimeoutSeconds: z
|
|
227
|
+
.number()
|
|
228
|
+
.int()
|
|
229
|
+
.min(5)
|
|
230
|
+
.max(600)
|
|
231
|
+
.optional()
|
|
232
|
+
.default(180)
|
|
233
|
+
.describe("Readiness polling timeout in seconds (default: 180)."),
|
|
221
234
|
description: z
|
|
222
235
|
.string()
|
|
223
236
|
.optional()
|
|
@@ -230,6 +243,8 @@ export const ProvisionVmInputSchema = z
|
|
|
230
243
|
export async function handleProvisionVm(rawArgs, context) {
|
|
231
244
|
let autoClaimId;
|
|
232
245
|
let slotClaim;
|
|
246
|
+
let createdVmId;
|
|
247
|
+
let createdHost;
|
|
233
248
|
try {
|
|
234
249
|
const parseResult = ProvisionVmInputSchema.safeParse(rawArgs);
|
|
235
250
|
if (!parseResult.success) {
|
|
@@ -344,6 +359,8 @@ export async function handleProvisionVm(rawArgs, context) {
|
|
|
344
359
|
const linearIssue = args.linearIssue;
|
|
345
360
|
const requester = args.requester;
|
|
346
361
|
const startOnDeploy = args.start !== undefined ? args.start : args.startOnDeploy;
|
|
362
|
+
const waitForReady = args.waitForReady ?? false;
|
|
363
|
+
const readyTimeoutSeconds = args.readyTimeoutSeconds ?? 180;
|
|
347
364
|
let parsedTags = [];
|
|
348
365
|
if (args.tags) {
|
|
349
366
|
if (Array.isArray(args.tags)) {
|
|
@@ -362,14 +379,14 @@ export async function handleProvisionVm(rawArgs, context) {
|
|
|
362
379
|
}
|
|
363
380
|
}
|
|
364
381
|
let claimId = args.slotClaimId;
|
|
365
|
-
if (!claimId) {
|
|
382
|
+
if (!claimId && (!effectiveTargetHost || (!effectiveTargetHost.startsWith("esxi") && args.hypervisorType !== "esxi"))) {
|
|
366
383
|
const claimResult = await context.slotManager.claimSlot({
|
|
367
384
|
tag: linearIssue || newName,
|
|
368
385
|
requester: requester || newName,
|
|
369
386
|
linearIssue: linearIssue || (newName.startsWith("CTUAT-") ? newName : undefined),
|
|
370
387
|
targetHost: effectiveTargetHost,
|
|
371
388
|
hypervisorType: args.hypervisorType,
|
|
372
|
-
durationMinutes:
|
|
389
|
+
durationMinutes: 120,
|
|
373
390
|
purpose: `Disposable test VM: ${newName}`,
|
|
374
391
|
});
|
|
375
392
|
autoClaimId = claimResult.claimId;
|
|
@@ -384,7 +401,7 @@ export async function handleProvisionVm(rawArgs, context) {
|
|
|
384
401
|
newVmId: newVmId,
|
|
385
402
|
targetHost: effectiveTargetHost || (slotClaim ? String(slotClaim.hostId) : undefined),
|
|
386
403
|
hypervisorType: hypervisorPref,
|
|
387
|
-
fullClone: args.fullClone,
|
|
404
|
+
fullClone: args.fullClone ?? false,
|
|
388
405
|
description: args.description || `Call Telemetry test VM (${linearIssue || "disposable"})`,
|
|
389
406
|
memoryBytes: args.memoryBytes || (args.memoryMb ? args.memoryMb * 1024 * 1024 : 8192 * 1024 * 1024),
|
|
390
407
|
cpuCores: args.cpuCores ?? args.cores ?? 2,
|
|
@@ -413,7 +430,7 @@ export async function handleProvisionVm(rawArgs, context) {
|
|
|
413
430
|
const sourceVmidNum = typeof source === "number" ? source : parseInt(String(source), 10);
|
|
414
431
|
const targetVmid = newVmidNum > 0 ? newVmidNum : 105;
|
|
415
432
|
const cloneTask = await client.cloneVm(sourceVmidNum, targetVmid, newName, {
|
|
416
|
-
full: cloneParams.fullClone,
|
|
433
|
+
full: cloneParams.fullClone ?? false,
|
|
417
434
|
description: cloneParams.description,
|
|
418
435
|
});
|
|
419
436
|
if (startOnDeploy) {
|
|
@@ -447,6 +464,8 @@ export async function handleProvisionVm(rawArgs, context) {
|
|
|
447
464
|
else {
|
|
448
465
|
throw new Error("No hypervisor manager available to provision VM.");
|
|
449
466
|
}
|
|
467
|
+
createdVmId = provisionResult.vmId;
|
|
468
|
+
createdHost = provisionResult.hostName;
|
|
450
469
|
// 3. Attach VM ID/Name to the claimed slot
|
|
451
470
|
if (claimId) {
|
|
452
471
|
try {
|
|
@@ -456,6 +475,39 @@ export async function handleProvisionVm(rawArgs, context) {
|
|
|
456
475
|
console.error("[provision_vm] Warning attaching VM to slot lease:", attachErr);
|
|
457
476
|
}
|
|
458
477
|
}
|
|
478
|
+
// 4. Optional port 2222 SSH readiness polling
|
|
479
|
+
if (waitForReady) {
|
|
480
|
+
const assignedIp = args.ipAddress ||
|
|
481
|
+
(provisionResult.ipAddresses && provisionResult.ipAddresses.length > 0
|
|
482
|
+
? provisionResult.ipAddresses[0]
|
|
483
|
+
: `192.168.124.${100 + (Number(provisionResult.vmId) % 100 || 5)}`);
|
|
484
|
+
const executor = context.applianceExecutor || applianceSshExecutor;
|
|
485
|
+
const pollStart = Date.now();
|
|
486
|
+
const timeoutMs = readyTimeoutSeconds * 1000;
|
|
487
|
+
let isReady = false;
|
|
488
|
+
while (Date.now() - pollStart < timeoutMs) {
|
|
489
|
+
try {
|
|
490
|
+
const connected = await executor.testConnection(assignedIp, 2222, 2000);
|
|
491
|
+
if (connected) {
|
|
492
|
+
isReady = true;
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
catch {
|
|
497
|
+
// continue polling
|
|
498
|
+
}
|
|
499
|
+
const elapsed = Date.now() - pollStart;
|
|
500
|
+
if (elapsed + 1000 < timeoutMs) {
|
|
501
|
+
await new Promise((r) => setTimeout(r, Math.min(1000, timeoutMs - elapsed)));
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
if (!isReady) {
|
|
508
|
+
throw new Error(`VM readiness timed out after ${readyTimeoutSeconds}s on ${assignedIp}:2222`);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
459
511
|
const formatted = formatProvisionResult(provisionResult, slotClaim);
|
|
460
512
|
return {
|
|
461
513
|
content: [
|
|
@@ -477,6 +529,28 @@ export async function handleProvisionVm(rawArgs, context) {
|
|
|
477
529
|
// ignore rollback error
|
|
478
530
|
}
|
|
479
531
|
}
|
|
532
|
+
// Atomic Rollback: Destroy created VM if created before failure
|
|
533
|
+
if (createdVmId && createdHost && context.hypervisorManager) {
|
|
534
|
+
try {
|
|
535
|
+
await context.hypervisorManager.vmAction({
|
|
536
|
+
targetHost: createdHost,
|
|
537
|
+
vmIdOrName: String(createdVmId),
|
|
538
|
+
action: "destroy",
|
|
539
|
+
force: true,
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
catch {
|
|
543
|
+
// ignore rollback error
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
else if (createdVmId && createdHost && context.proxmoxManager) {
|
|
547
|
+
try {
|
|
548
|
+
await context.proxmoxManager.getClient(createdHost).destroyVm(Number(createdVmId), { purge: true });
|
|
549
|
+
}
|
|
550
|
+
catch {
|
|
551
|
+
// ignore rollback error
|
|
552
|
+
}
|
|
553
|
+
}
|
|
480
554
|
if (error instanceof ProtectedVmViolationError) {
|
|
481
555
|
return createToolErrorResult(ProxmoxErrorCode.PROTECTED_VM_ACTION_DENIED, error.message, {
|
|
482
556
|
vmid: error.vmid,
|
|
@@ -557,11 +631,11 @@ export const AutoProvisionVmInputShape = {
|
|
|
557
631
|
targetVersion: z
|
|
558
632
|
.string()
|
|
559
633
|
.optional()
|
|
560
|
-
.describe("Call Telemetry release version to deploy/pin (e.g. '0.8.7-stable', '0.8.8')."),
|
|
634
|
+
.describe("Call Telemetry release version to deploy/pin (e.g. '0.8.7-rc', '0.8.7-stable', '0.8.8'). Defaults to '0.8.7-rc'."),
|
|
561
635
|
version: z
|
|
562
636
|
.string()
|
|
563
637
|
.optional()
|
|
564
|
-
.describe("Alias for targetVersion."),
|
|
638
|
+
.describe("Alias for targetVersion (e.g. '0.8.7-rc')."),
|
|
565
639
|
workloadType: z
|
|
566
640
|
.enum(["uat", "perf", "soak", "dev", "regression", "reproduction", "demo"])
|
|
567
641
|
.optional()
|
|
@@ -592,8 +666,8 @@ export const AutoProvisionVmInputShape = {
|
|
|
592
666
|
.min(1)
|
|
593
667
|
.max(1440)
|
|
594
668
|
.optional()
|
|
595
|
-
.default(
|
|
596
|
-
.describe("Slot lease TTL in minutes (default:
|
|
669
|
+
.default(120)
|
|
670
|
+
.describe("Slot lease TTL in minutes (default: 120)."),
|
|
597
671
|
durationMinutes: z
|
|
598
672
|
.number()
|
|
599
673
|
.int()
|
|
@@ -620,6 +694,11 @@ export const AutoProvisionVmInputShape = {
|
|
|
620
694
|
.string()
|
|
621
695
|
.optional()
|
|
622
696
|
.describe("Target storage pool / datastore name."),
|
|
697
|
+
fullClone: z
|
|
698
|
+
.boolean()
|
|
699
|
+
.optional()
|
|
700
|
+
.default(false)
|
|
701
|
+
.describe("Whether to perform a full disk clone (default: false for fast linked cloning)."),
|
|
623
702
|
waitForReady: z
|
|
624
703
|
.boolean()
|
|
625
704
|
.optional()
|
|
@@ -755,7 +834,7 @@ export async function handleAutoProvisionVm(rawArgs, context) {
|
|
|
755
834
|
const linearIssue = args.linearIssue;
|
|
756
835
|
const label = args.label;
|
|
757
836
|
const branch = args.branch;
|
|
758
|
-
const durationMinutes = args.durationMinutes ?? args.ttlMinutes ??
|
|
837
|
+
const durationMinutes = args.durationMinutes ?? args.ttlMinutes ?? 120;
|
|
759
838
|
const vmName = args.name ||
|
|
760
839
|
args.newVmName ||
|
|
761
840
|
`ct-appliance-${workloadType}-${Date.now().toString(36)}`;
|
|
@@ -789,7 +868,7 @@ export async function handleAutoProvisionVm(rawArgs, context) {
|
|
|
789
868
|
const hostId = claimedSlot.hostId;
|
|
790
869
|
createdHost = hostId;
|
|
791
870
|
const hypType = hostId.startsWith("esxi") ? "esxi" : "proxmox";
|
|
792
|
-
// 2. Resolve Base Template
|
|
871
|
+
// 2. Resolve Base Template (prioritize AlmaLinux 9.7 base template: VMID 9000 / CT-ALMALINUX9-TEMPLATE / /almalinux.*9.*7/i)
|
|
793
872
|
let template = args.template ?? args.sourceVmIdOrName;
|
|
794
873
|
if (!template && context.hypervisorManager) {
|
|
795
874
|
try {
|
|
@@ -797,7 +876,16 @@ export async function handleAutoProvisionVm(rawArgs, context) {
|
|
|
797
876
|
includeTemplates: true,
|
|
798
877
|
host: hostId,
|
|
799
878
|
});
|
|
800
|
-
const templateVm = vms.find((v) => v.isTemplate
|
|
879
|
+
const templateVm = vms.find((v) => v.isTemplate &&
|
|
880
|
+
(v.id === "9000" ||
|
|
881
|
+
v.name.toUpperCase() === "CT-ALMALINUX9-TEMPLATE" ||
|
|
882
|
+
v.name.toUpperCase() === "CT-ALMALINUX9-MINI-TEMPLATE" ||
|
|
883
|
+
/almalinux.*9.*7/i.test(v.name) ||
|
|
884
|
+
(v.tags && v.tags.some((t) => /almalinux.*9.*7/i.test(t))))) ||
|
|
885
|
+
vms.find((v) => /almalinux.*9.*7/i.test(v.name) ||
|
|
886
|
+
v.name.toUpperCase().includes("ALMALINUX9") ||
|
|
887
|
+
v.id === "9000") ||
|
|
888
|
+
vms.find((v) => v.isTemplate) ||
|
|
801
889
|
vms.find((v) => v.name.toLowerCase().includes("template") ||
|
|
802
890
|
v.name.toLowerCase().includes("tmpl") ||
|
|
803
891
|
(v.tags && v.tags.some((t) => t.toLowerCase().includes("template"))));
|
|
@@ -814,10 +902,10 @@ export async function handleAutoProvisionVm(rawArgs, context) {
|
|
|
814
902
|
template = "CT-087-STABLE-TEMPLATE";
|
|
815
903
|
}
|
|
816
904
|
else if (hostId === "proxmox-mini") {
|
|
817
|
-
template = "
|
|
905
|
+
template = "9000";
|
|
818
906
|
}
|
|
819
907
|
else {
|
|
820
|
-
template = "
|
|
908
|
+
template = "9000";
|
|
821
909
|
}
|
|
822
910
|
}
|
|
823
911
|
// 3. Clone VM from Template
|
|
@@ -826,7 +914,7 @@ export async function handleAutoProvisionVm(rawArgs, context) {
|
|
|
826
914
|
newVmName: vmName,
|
|
827
915
|
targetHost: hostId,
|
|
828
916
|
hypervisorType: hypType,
|
|
829
|
-
fullClone:
|
|
917
|
+
fullClone: args.fullClone ?? false,
|
|
830
918
|
description: `Auto-provisioned ${workloadType} VM (${linearIssue || label || "disposable"})`,
|
|
831
919
|
memoryBytes: 8192 * 1024 * 1024,
|
|
832
920
|
cpuCores: 2,
|
|
@@ -850,7 +938,7 @@ export async function handleAutoProvisionVm(rawArgs, context) {
|
|
|
850
938
|
const clientIsMock = client instanceof MockProxmoxClient;
|
|
851
939
|
const targetVmid = 500;
|
|
852
940
|
const sourceNum = typeof template === "number" ? template : parseInt(String(template), 10) || 9000;
|
|
853
|
-
const cloneTask = await client.cloneVm(sourceNum, targetVmid, vmName, { full:
|
|
941
|
+
const cloneTask = await client.cloneVm(sourceNum, targetVmid, vmName, { full: args.fullClone ?? false });
|
|
854
942
|
await client.startVm(targetVmid).catch(() => { });
|
|
855
943
|
provisionResult = {
|
|
856
944
|
vmId: String(targetVmid),
|
|
@@ -916,23 +1004,49 @@ export async function handleAutoProvisionVm(rawArgs, context) {
|
|
|
916
1004
|
throw new Error(`VM readiness timed out after ${readyTimeoutSeconds}s on ${assignedIp}:2222 (healthStatus unverified).`);
|
|
917
1005
|
}
|
|
918
1006
|
}
|
|
919
|
-
// 7. Deploy/Pin Target Release Version if
|
|
1007
|
+
// 7. Deploy/Pin Target Release Version (defaults to 0.8.7-rc if omitted)
|
|
1008
|
+
const versionToDeploy = targetVersion || "0.8.7-rc";
|
|
920
1009
|
let deployedVersion;
|
|
921
|
-
if (
|
|
1010
|
+
if (versionToDeploy) {
|
|
922
1011
|
const executor = context.applianceExecutor || applianceSshExecutor;
|
|
923
1012
|
const verRes = await executor.execute({
|
|
924
1013
|
targetIp: assignedIp,
|
|
925
1014
|
port: 2222,
|
|
926
1015
|
commandType: "set_version",
|
|
927
|
-
args: { version:
|
|
1016
|
+
args: { version: versionToDeploy },
|
|
928
1017
|
timeoutMs: 60000,
|
|
929
1018
|
});
|
|
930
1019
|
if (verRes.exitCode !== 0) {
|
|
931
|
-
throw new Error(`Failed to deploy requested version '${
|
|
1020
|
+
throw new Error(`Failed to deploy requested version '${versionToDeploy}' on ${assignedIp}: ${verRes.stderr || verRes.stdout}`);
|
|
932
1021
|
}
|
|
933
|
-
deployedVersion =
|
|
1022
|
+
deployedVersion = versionToDeploy;
|
|
934
1023
|
}
|
|
935
|
-
// 8.
|
|
1024
|
+
// 8. Chained Health Assertions & Database Migration Verification
|
|
1025
|
+
if (waitForReady) {
|
|
1026
|
+
const executor = context.applianceExecutor || applianceSshExecutor;
|
|
1027
|
+
// Assert health (/healthz 200 OK, Caddy SSL, containers healthy)
|
|
1028
|
+
const healthRes = await executor.execute({
|
|
1029
|
+
targetIp: assignedIp,
|
|
1030
|
+
port: 2222,
|
|
1031
|
+
commandType: "assert_health",
|
|
1032
|
+
timeoutMs: 60000,
|
|
1033
|
+
});
|
|
1034
|
+
if (healthRes.exitCode !== 0) {
|
|
1035
|
+
throw new Error(`Appliance health assertion failed on ${assignedIp}: ${healthRes.stderr || healthRes.stdout}`);
|
|
1036
|
+
}
|
|
1037
|
+
// Verify database migrations
|
|
1038
|
+
const migRes = await executor.execute({
|
|
1039
|
+
targetIp: assignedIp,
|
|
1040
|
+
port: 2222,
|
|
1041
|
+
commandType: "migrate_status",
|
|
1042
|
+
timeoutMs: 60000,
|
|
1043
|
+
});
|
|
1044
|
+
if (migRes.exitCode !== 0) {
|
|
1045
|
+
throw new Error(`Appliance database migration check failed on ${assignedIp}: ${migRes.stderr || migRes.stdout}`);
|
|
1046
|
+
}
|
|
1047
|
+
healthStatus = "healthy";
|
|
1048
|
+
}
|
|
1049
|
+
// 9. Return Verified Endpoints & Summary
|
|
936
1050
|
const autoResult = {
|
|
937
1051
|
success: true,
|
|
938
1052
|
slotId: claimedSlot.slotId,
|
|
@@ -948,7 +1062,7 @@ export async function handleAutoProvisionVm(rawArgs, context) {
|
|
|
948
1062
|
user: "admin",
|
|
949
1063
|
},
|
|
950
1064
|
apiEndpoint: `https://${assignedIp}:443`,
|
|
951
|
-
targetVersion,
|
|
1065
|
+
targetVersion: targetVersion || versionToDeploy,
|
|
952
1066
|
deployedVersion,
|
|
953
1067
|
workloadType,
|
|
954
1068
|
label,
|
|
@@ -996,6 +1110,20 @@ export async function handleAutoProvisionVm(rawArgs, context) {
|
|
|
996
1110
|
// ignore VM teardown error
|
|
997
1111
|
}
|
|
998
1112
|
}
|
|
1113
|
+
else if (createdVmId && createdHost && context.proxmoxManager) {
|
|
1114
|
+
try {
|
|
1115
|
+
await context.proxmoxManager.getClient(createdHost).destroyVm(Number(createdVmId), { purge: true });
|
|
1116
|
+
}
|
|
1117
|
+
catch {
|
|
1118
|
+
// ignore VM teardown error
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
if (error instanceof HypervisorSlotUnsupportedError) {
|
|
1122
|
+
return createToolErrorResult(ProxmoxErrorCode.OPERATION_NOT_SUPPORTED, error.message, {
|
|
1123
|
+
host: error.hostId,
|
|
1124
|
+
hypervisorType: error.hypervisorType,
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
999
1127
|
if (error instanceof ProtectedVmViolationError) {
|
|
1000
1128
|
return createToolErrorResult(ProxmoxErrorCode.PROTECTED_VM_ACTION_DENIED, error.message, {
|
|
1001
1129
|
vmid: error.vmid,
|