@homeflare/alchemy 0.34.0 → 0.36.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/dist/grafana/alert-rule-group-drop-warning.d.ts +25 -0
- package/dist/grafana/alert-rule-group-drop-warning.d.ts.map +1 -0
- package/dist/grafana/alert-rule-group-fixtures.d.ts +73 -0
- package/dist/grafana/alert-rule-group-fixtures.d.ts.map +1 -0
- package/dist/grafana/alert-rule-group-model.d.ts +28 -0
- package/dist/grafana/alert-rule-group-model.d.ts.map +1 -0
- package/dist/grafana/alert-rule-group.d.ts +60 -0
- package/dist/grafana/alert-rule-group.d.ts.map +1 -0
- package/dist/grafana/index.d.ts +6 -4
- package/dist/grafana/index.d.ts.map +1 -1
- package/dist/grafana/index.js +355 -139
- package/dist/grafana/index.js.map +14 -8
- package/dist/grafana/notification-policy-drop-warning.d.ts +27 -0
- package/dist/grafana/notification-policy-drop-warning.d.ts.map +1 -0
- package/dist/grafana/notification-policy-fixtures.d.ts +34 -0
- package/dist/grafana/notification-policy-fixtures.d.ts.map +1 -0
- package/dist/grafana/notification-policy-model.d.ts +53 -0
- package/dist/grafana/notification-policy-model.d.ts.map +1 -0
- package/dist/grafana/notification-policy.d.ts +57 -0
- package/dist/grafana/notification-policy.d.ts.map +1 -0
- package/dist/grafana/providers.d.ts +1 -1
- package/dist/grafana/providers.d.ts.map +1 -1
- package/dist/proxmox/index.js +349 -287
- package/dist/proxmox/index.js.map +8 -7
- package/dist/proxmox/network-apply-read.d.ts +12 -0
- package/dist/proxmox/network-apply-read.d.ts.map +1 -1
- package/dist/proxmox/network-apply.d.ts +2 -0
- package/dist/proxmox/network-apply.d.ts.map +1 -1
- package/dist/proxmox/node-network-form.d.ts +86 -54
- package/dist/proxmox/node-network-form.d.ts.map +1 -1
- package/dist/proxmox/node-network-wire.d.ts +100 -0
- package/dist/proxmox/node-network-wire.d.ts.map +1 -0
- package/dist/proxmox/node-network.d.ts +21 -106
- package/dist/proxmox/node-network.d.ts.map +1 -1
- package/docs/grafana-alerting-rules.md +143 -0
- package/docs/grafana-alerting.md +7 -4
- package/docs/grafana-notification-policy.md +127 -0
- package/docs/grafana.md +12 -6
- package/docs/upstream-conformance.md +30 -4
- package/package.json +1 -1
- package/src/grafana/alert-rule-group-drop-warning.test.ts +107 -0
- package/src/grafana/alert-rule-group-drop-warning.ts +41 -0
- package/src/grafana/alert-rule-group-fixtures.ts +46 -0
- package/src/grafana/alert-rule-group-model.test.ts +75 -0
- package/src/grafana/alert-rule-group-model.ts +66 -0
- package/src/grafana/alert-rule-group-provenance.test.ts +116 -0
- package/src/grafana/alert-rule-group.test.ts +164 -0
- package/src/grafana/alert-rule-group.ts +231 -0
- package/src/grafana/index.ts +18 -4
- package/src/grafana/notification-policy-drop-warning.test.ts +163 -0
- package/src/grafana/notification-policy-drop-warning.ts +60 -0
- package/src/grafana/notification-policy-fixtures.ts +27 -0
- package/src/grafana/notification-policy-model.test.ts +104 -0
- package/src/grafana/notification-policy-model.ts +100 -0
- package/src/grafana/notification-policy-refusals.test.ts +119 -0
- package/src/grafana/notification-policy.test.ts +86 -0
- package/src/grafana/notification-policy.ts +191 -0
- package/src/grafana/providers.ts +4 -0
- package/src/proxmox/constraints-host-forms.test.ts +2 -2
- package/src/proxmox/network-apply-read.ts +12 -0
- package/src/proxmox/network-apply.ts +2 -0
- package/src/proxmox/node-network-form.ts +93 -99
- package/src/proxmox/node-network-read-failure.test.ts +206 -0
- package/src/proxmox/node-network-wire.ts +239 -0
- package/src/proxmox/node-network.ts +155 -167
|
@@ -18,6 +18,12 @@
|
|
|
18
18
|
* drops the envelope, so the one readable pending signal is invisible to every other resource in
|
|
19
19
|
* this package. Without the fetch below, `Proxmox.NetworkApply` could not diff honestly at all.
|
|
20
20
|
* ★ `pveEnvelopeWith` in client.ts now carries the same member failover as every other call.
|
|
21
|
+
* ⛔ WHY THIS FAMILY DID NOT MOVE TO DISTILLED WITH `Proxmox.NodeNetwork` (2026-09-24, decision
|
|
22
|
+
* 43's walk-down): CONFIRMED against `@distilled.cloud/proxmox`'s own `protocol.ts` that its
|
|
23
|
+
* `transformResponse` unconditionally returns `body.data ?? {}` for every operation, before a
|
|
24
|
+
* generated call's typed output is even built — no typed call, current or future, could see
|
|
25
|
+
* `changes` without a protocol-level change upstream. `pveEnvelopeWith` stays because nothing
|
|
26
|
+
* else preserves the sibling this function needs.
|
|
21
27
|
*
|
|
22
28
|
* ⛔ AND THE INTERFACE ROWS CANNOT TELL YOU EITHER. MEASURED in PVE/INotify.pm `read_file`: when
|
|
23
29
|
* `/etc/network/interfaces.new` exists, THAT is the file that gets parsed, so
|
|
@@ -88,6 +94,12 @@ export declare const UNREACHABLE = "the task could not be read -- the node stopp
|
|
|
88
94
|
* of the task". Every mint returns a NEW token id, so a second mint is a different identity and
|
|
89
95
|
* is not the owner — which is why this takes a `PveCredential` and the caller keeps one lease
|
|
90
96
|
* across both calls, instead of widening the provisioning role to read its own tasks.
|
|
97
|
+
*
|
|
98
|
+
* ⛔ THE OTHER REASON THIS FAMILY DID NOT MOVE TO DISTILLED (2026-09-24): CHECKED against
|
|
99
|
+
* `@distilled.cloud/proxmox`'s own `Task.awaitTask` (`src/task.ts`) before deciding — it
|
|
100
|
+
* propagates a poll failure as a genuine typed `GetNodeTaskStatusError` rather than collapsing
|
|
101
|
+
* it to `UNREACHABLE` the way this function does, which would turn the ⛔ two paragraphs above
|
|
102
|
+
* into a failed `reconcile` on exactly the poll a mid-reload connection drop is expected to hit.
|
|
91
103
|
*/
|
|
92
104
|
export declare const awaitTask: (target: PveTarget, credential: PveCredential, node: string, upid: string, attempts?: number) => Effect.Effect<string, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
93
105
|
//# sourceMappingURL=network-apply-read.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"network-apply-read.d.ts","sourceRoot":"","sources":["../../src/proxmox/network-apply-read.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"network-apply-read.d.ts","sourceRoot":"","sources":["../../src/proxmox/network-apply-read.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAatE,oGAAoG;AACpG,MAAM,MAAM,aAAa,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;AA0B7E;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,SAAU,MAAM,WAWxC,CAAC;AAEF,mFAAmF;AACnF,eAAO,MAAM,YAAY,WAAY,SAAS,QAAQ,MAAM,8IACH,CAAC;AAiC1D;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,WAAY,SAAS,0JAO7C,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,WAAW,0FACiE,CAAC;AAY1F;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,SAAS,WACZ,SAAS,cACL,aAAa,QACnB,MAAM,QACN,MAAM,aAEF,MAAM,uFAgCd,CAAC"}
|
|
@@ -44,6 +44,8 @@
|
|
|
44
44
|
* task it starts so it can poll it without `Sys.Audit`. ⛔ `Sys.Modify` ON A NODE IS NOT A SMALL
|
|
45
45
|
* GRANT — it also carries that node's DNS, hosts file, time and service configuration. Granting
|
|
46
46
|
* it cluster-wide to run this resource widens the provisioning role well beyond networking.
|
|
47
|
+
*
|
|
48
|
+
* ⛔ STAYS ON `client.ts` (2026-09-24) — network-apply-read.ts's header has the two MEASURED reasons.
|
|
47
49
|
*/
|
|
48
50
|
import { Resource } from 'alchemy';
|
|
49
51
|
import * as Provider from 'alchemy/Provider';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"network-apply.d.ts","sourceRoot":"","sources":["../../src/proxmox/network-apply.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"network-apply.d.ts","sourceRoot":"","sources":["../../src/proxmox/network-apply.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAM7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,WAAW,iBAAkB,SAAQ,UAAU;IACnD;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC,+FAA+F;IAC/F,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAoB,SAAQ,QAAQ,CACnD,sBAAsB,EACtB,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,eAAO,MAAM,mBAAmB,sDAAwD,CAAC;AAiIzF,eAAO,MAAM,2BAA2B,yIACyD,CAAC"}
|
|
@@ -1,31 +1,63 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* directions, because a read that normalises differently from the write that produced it is
|
|
8
|
-
* exactly how a forever-diff is born — keeping `ifaceList` next to both ends is what stops that.
|
|
9
|
-
* node-network.ts answers "what is an interface, and which of its fields are safe to compare at
|
|
10
|
-
* all". Nothing here calls the cluster.
|
|
11
|
-
*
|
|
12
|
-
* ⚠️ THE `import type` BACK TO node-network.ts IS A CYCLE ON PAPER ONLY — type-only, erased before
|
|
13
|
-
* anything runs, so `NodeNetworkProps` stays in the file that declares the resource.
|
|
14
|
-
*
|
|
15
|
-
* * ⛔ THE LAST SEVEN ARE REPORTED AND NEVER COMPARED, AND EACH ONE IS A MEASURED FOREVER-DIFF.
|
|
16
|
-
* `priority` is assigned by PVE FROM FILE ORDER — `my $priority = 2; ... $d->{priority} =
|
|
17
|
-
* $priority++` — and it is not a POST or PUT parameter at all. MEASURED ACROSS C1: vmbr1.42 is
|
|
18
|
-
* priority 16 on node-b and node-c and 15 on node-d, because node-d has no `wlan0` above it. One declaration
|
|
19
|
-
* reused across three nodes would diff on exactly one of them, forever, over nothing.
|
|
20
|
-
* `method` is RECOMPUTED on every write from whether the form carried an address, `families` is
|
|
21
|
-
* recomputed the same way and never written to the file, `active` and `exists` describe the
|
|
22
|
-
* kernel rather than the config, and `bond_miimon`, `bridge_stp` and `bridge_fd` are emitted by
|
|
23
|
-
* PVE's writer with defaults (100, `off`, 0) while appearing in NEITHER write schema —
|
|
24
|
-
* `additionalProperties => 0` means sending one is a 400. Live proof of all three: node-b's bond0
|
|
25
|
-
* returns `bond_miimon "100"` and vmbr1 returns `bridge_stp "off"`, `bridge_fd "0"`, none of
|
|
26
|
-
* which any declaration can set.
|
|
2
|
+
* `Proxmox.NodeNetwork`'s declared shape (`NodeNetworkProps`) and WRITE side, plus the wire
|
|
3
|
+
* coercions the read side needs too — kept here since neither `matches` nor `updateForm` makes
|
|
4
|
+
* sense without agreeing on the same normalisation. Split out of node-network.ts (2026-09-24,
|
|
5
|
+
* the distilled migration; `Props` joined it in this PR's later line-cap trim) — the
|
|
6
|
+
* storage.ts/storage-form.ts seam.
|
|
27
7
|
*/
|
|
28
|
-
import type {
|
|
8
|
+
import type { NodeNetworkType } from './node-network-wire.ts';
|
|
9
|
+
import { type WithTarget } from './resource-spec.ts';
|
|
10
|
+
export interface NodeNetworkProps extends WithTarget {
|
|
11
|
+
/** Which node's file this stanza lives in. Interfaces are per node, never cluster-wide. */
|
|
12
|
+
node: string;
|
|
13
|
+
/** `vmbr0`, `bond0`, `vmbr1.42`. 2-20 characters, PVE's `pve-iface` format. */
|
|
14
|
+
iface: string;
|
|
15
|
+
/** ⛔ REQUIRED ON EVERY WRITE, update included, never used to retype — see node-network-wire.ts. */
|
|
16
|
+
type: NodeNetworkType;
|
|
17
|
+
/**
|
|
18
|
+
* `198.51.100.12/24`. ⛔ THE ONE FIELD WITH NO UNMANAGED MODE: leaving it out is an instruction
|
|
19
|
+
* to make the interface `manual`, not an instruction to leave its address alone. The ⛔ on
|
|
20
|
+
* `updateForm` in node-network-form.ts has the measurement and the consequence.
|
|
21
|
+
*/
|
|
22
|
+
cidr?: string;
|
|
23
|
+
/** ⚠️ PVE allows exactly ONE default gateway per node and refuses a second with "Default
|
|
24
|
+
* gateway already exists on interface '<other>'". On C1 it is vmbr0.41's. */
|
|
25
|
+
gateway?: string;
|
|
26
|
+
/**
|
|
27
|
+
* `auto <iface>` in the file. Absent on read means off, which is why `bool`'s fallback is used.
|
|
28
|
+
* ⚠️ UNDECLARED IS UNMANAGED ON AN UPDATE AND OFF ON A CREATE — the same asymmetry storage.ts
|
|
29
|
+
* has, and it bites harder here: an interface created without `autostart` is one the node
|
|
30
|
+
* will not bring up at boot. Every C1 bridge, bond and vlan carries it.
|
|
31
|
+
*/
|
|
32
|
+
autostart?: boolean;
|
|
33
|
+
/** 1280-65520. Unset leaves the file without an `mtu` line and the kernel default in force. */
|
|
34
|
+
mtu?: number;
|
|
35
|
+
/** ⚠️ Round-trips only after normalisation — see `comment` in node-network-wire.ts. */
|
|
36
|
+
comments?: string;
|
|
37
|
+
/** Space-separated, a SET: `enp87s0`, or `bond0`. ⚠️ PVE refuses a port already used elsewhere. */
|
|
38
|
+
bridge_ports?: string;
|
|
39
|
+
/** `2-4094`, or `2 100-200`. Only written when `bridge_vlan_aware` is on. */
|
|
40
|
+
bridge_vids?: string;
|
|
41
|
+
/** ⛔ A `false` here is sent as `delete=`, never as `0` — node-network-form.ts's ⛔ says why. */
|
|
42
|
+
bridge_vlan_aware?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* A bond's members, space separated. ⚠️ THE PARAMETER IS `slaves`, NOT `bond_slaves`, in BOTH
|
|
45
|
+
* directions on this PVE: MEASURED, the POST/PUT schema names only `slaves` and the GET returns
|
|
46
|
+
* `"slaves":"enp2s0f0np0 enp2s0f1np1"`. `bond_slaves` appears nowhere in Network.pm here.
|
|
47
|
+
*/
|
|
48
|
+
slaves?: string;
|
|
49
|
+
bond_mode?: string;
|
|
50
|
+
/** ⚠️ Only written when `bond_mode` is `balance-xor` or `802.3ad`; ignored otherwise. */
|
|
51
|
+
bond_xmit_hash_policy?: string;
|
|
52
|
+
/** active-backup only. Kept hyphenated because that is the wire name. */
|
|
53
|
+
'bond-primary'?: string;
|
|
54
|
+
/** ⚠️ DERIVED FROM A DOTTED NAME. `vmbr1.42` reports `vlan-id` 42 with no such line in the file;
|
|
55
|
+
* declaring it there is harmless but adds a line the file did not have. */
|
|
56
|
+
'vlan-id'?: number;
|
|
57
|
+
'vlan-raw-device'?: string;
|
|
58
|
+
}
|
|
59
|
+
export declare const NODE_NETWORK_CREATE = "pve:POST /nodes/{node}/network";
|
|
60
|
+
export declare const NODE_NETWORK_UPDATE = "pve:PUT /nodes/{node}/network/{iface}";
|
|
29
61
|
/**
|
|
30
62
|
* "Not set", for the three integers this family reports.
|
|
31
63
|
*
|
|
@@ -68,7 +100,7 @@ export declare const ifaceList: (value: unknown) => string;
|
|
|
68
100
|
* C1 has none; on an interface that does, leave `comments` undeclared rather than fighting it.
|
|
69
101
|
*/
|
|
70
102
|
export declare const comment: (value: unknown) => string;
|
|
71
|
-
/** Undeclared is unmanaged: neither sent nor compared.
|
|
103
|
+
/** Undeclared is unmanaged: neither sent nor compared. */
|
|
72
104
|
export declare const same: <T>(declared: T | undefined, live: T) => boolean;
|
|
73
105
|
export declare const sameList: (declared: string | undefined, live: string) => boolean;
|
|
74
106
|
export declare const sameComment: (declared: string | undefined, live: string) => boolean;
|
|
@@ -78,12 +110,13 @@ export declare const sameComment: (declared: string | undefined, live: string) =
|
|
|
78
110
|
* ⚠️ UNDECLARED IS UNMANAGED — not sent, and not compared either. PVE's PUT MERGES the form into
|
|
79
111
|
* the existing stanza (`foreach my $k (keys %$param) { $ifaces->{$iface}->{$k} = $param->{$k} }`),
|
|
80
112
|
* so an omitted parameter is left exactly as it was. `cidr` is the one exception and it is not
|
|
81
|
-
* a small one — see
|
|
113
|
+
* a small one — see node-network.ts's own ⛔.
|
|
82
114
|
*
|
|
83
115
|
* ⛔ `type` IS REQUIRED ON THE PUT, NOT ONLY ON THE POST. It carries no `optional` in either
|
|
84
|
-
* schema
|
|
85
|
-
*
|
|
86
|
-
*
|
|
116
|
+
* schema (CONFIRMED again in distilled's generated `PutNodeNetwork2Request`/
|
|
117
|
+
* `CreateNodeNetworkRequest`), and the merge above writes it straight into the stanza — so a PUT
|
|
118
|
+
* naming the wrong type would RETYPE A LIVE INTERFACE in the file. node-network-wire.ts's
|
|
119
|
+
* `attributesOf` refuses that case before it can reach here.
|
|
87
120
|
*
|
|
88
121
|
* ⛔ `bridge_vlan_aware` IS SENT ONLY WHEN TRUE, AND A FALSE ONE IS A `delete=`. MEASURED in the
|
|
89
122
|
* writer: the test is `if (defined($d->{bridge_vlan_aware}))`, not a truth test, so `0` is
|
|
@@ -94,23 +127,31 @@ export declare const sameComment: (declared: string | undefined, live: string) =
|
|
|
94
127
|
* "netmask conflicts with cidr" when both are sent, and the reader rewrites whatever was written
|
|
95
128
|
* into prefix form — MEASURED: node-b's vmbr1.42 reports `"netmask":"24"`, never `255.255.255.0`.
|
|
96
129
|
* A resource accepting both spellings would let a declaration diff against its own value.
|
|
130
|
+
*
|
|
131
|
+
* ★ HYPHENATED, THE WIRE SHAPE — `guardWrite` (node-network.ts) checks this form directly, keyed
|
|
132
|
+
* by PVE's own names (`generated/constraints/pve-nodes-network.ts`). `toDistilledCreate`/
|
|
133
|
+
* `toDistilledUpdate` below translate a COPY for the actual SDK call — storage-form.ts's
|
|
134
|
+
* `underscored` has the fuller measurement of why the wire itself does not need this rename
|
|
135
|
+
* (distilled's own `T.Body()` annotation re-hyphenates on the way out either way), reused here
|
|
136
|
+
* only for the three fields this family's schema renames: `bond-primary`, `vlan-id`,
|
|
137
|
+
* `vlan-raw-device` -> `bond_primary`, `vlan_id`, `vlan_raw_device`.
|
|
97
138
|
*/
|
|
98
139
|
export declare const body: (props: NodeNetworkProps) => Record<string, string>;
|
|
99
140
|
/**
|
|
100
141
|
* The create form: `body` plus the name of the interface being created.
|
|
101
142
|
*
|
|
102
|
-
* 🔴 `iface`
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* it
|
|
107
|
-
*
|
|
108
|
-
*
|
|
143
|
+
* 🔴 `iface` MUST BE IN THE BODY, NOT ONLY THE PATH — `POST /nodes/{node}/network` declares
|
|
144
|
+
* `iface` REQUIRED (`pve-iface`, 2..20) and `{node}` is its only path parameter, CONFIRMED again
|
|
145
|
+
* in distilled's generated `CreateNodeNetworkRequest`, which carries `iface` as a plain body
|
|
146
|
+
* field, never a `T.Label()`. The pre-distilled client.ts version of this file shipped without
|
|
147
|
+
* it for a time (see the git history around 2026-09-22) — every create this family had ever
|
|
148
|
+
* planned would have 400ed, caught only because every C1 interface had been adopted, never
|
|
149
|
+
* created, so nothing exercised the POST until the vendor table was wired in.
|
|
109
150
|
*
|
|
110
151
|
* ⚠️ NOT ADDED TO `body`, BECAUSE THE PUT MUST NOT CARRY IT. `additionalProperties => 0` on the
|
|
111
152
|
* update schema makes a second copy of the name a 400 rather than an ignored hint.
|
|
112
153
|
*/
|
|
113
|
-
export declare const
|
|
154
|
+
export declare const createForm: (props: NodeNetworkProps) => Record<string, string>;
|
|
114
155
|
/**
|
|
115
156
|
* The update form: `body` plus the two fields that can only be cleared explicitly.
|
|
116
157
|
*
|
|
@@ -119,25 +160,16 @@ export declare const createBody: (props: NodeNetworkProps) => Record<string, str
|
|
|
119
160
|
* `$param->{method} = $param->{address} ? 'static' : 'manual'` — so a PUT WITHOUT an address
|
|
120
161
|
* makes the interface manual no matter what the caller intended. Left at that, the merge would
|
|
121
162
|
* leave the old `address` and `netmask` in the hash and the writer would emit an `address` line
|
|
122
|
-
* under `iface … inet manual`: a stanza PVE's own UI cannot produce
|
|
123
|
-
*
|
|
124
|
-
*
|
|
163
|
+
* under `iface … inet manual`: a stanza PVE's own UI cannot produce. Clearing cidr explicitly at
|
|
164
|
+
* least produces a clean, honest manual interface — and `matches` compares `cidr`
|
|
165
|
+
* UNCONDITIONALLY, so the plan says so first.
|
|
125
166
|
*
|
|
126
167
|
* ⛔ ON vmbr1.42 THAT IS THE CEPH TRANSPORT. A declaration of that interface without its `cidr` is
|
|
127
168
|
* a declaration that it should have no address, applied across three nodes. The plan will read
|
|
128
169
|
* `1 to update` rather than `noop`; do not wave it through.
|
|
129
170
|
*
|
|
130
|
-
* ⚠️ `delete` IS A PUT-ONLY PARAMETER. The POST schema
|
|
131
|
-
* `
|
|
132
|
-
*/
|
|
133
|
-
export declare const updateBody: (props: NodeNetworkProps) => Record<string, string>;
|
|
134
|
-
/**
|
|
135
|
-
* One live interface as attributes, or `undefined` — "this is not really there".
|
|
136
|
-
*
|
|
137
|
-
* ⚠️ EVERY FIELD PVE REPORTS IS CARRIED HERE, INCLUDING THE SEVEN NO DECLARATION CAN SET. A plan
|
|
138
|
-
* that cannot show `priority`, `method` or `bond_miimon` cannot explain why it is ignoring them,
|
|
139
|
-
* and the next person to read a forever-diff would start by adding them to `matches`. They are
|
|
140
|
-
* reported precisely so that they are visibly OUT of it — see the ⛔ above the attribute type.
|
|
171
|
+
* ⚠️ `delete` IS A PUT-ONLY PARAMETER. The POST schema has no `delete` at all, which is why
|
|
172
|
+
* `createForm` calls `body` directly rather than going through this function.
|
|
141
173
|
*/
|
|
142
|
-
export declare const
|
|
174
|
+
export declare const updateForm: (props: NodeNetworkProps) => Record<string, string>;
|
|
143
175
|
//# sourceMappingURL=node-network-form.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-network-form.d.ts","sourceRoot":"","sources":["../../src/proxmox/node-network-form.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"node-network-form.d.ts","sourceRoot":"","sources":["../../src/proxmox/node-network-form.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGrD,MAAM,WAAW,gBAAiB,SAAQ,UAAU;IAClD,2FAA2F;IAC3F,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAC;IACd,mGAAmG;IACnG,IAAI,EAAE,eAAe,CAAC;IACtB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;kFAC8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,+FAA+F;IAC/F,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uFAAuF;IACvF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mGAAmG;IACnG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+FAA+F;IAC/F,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yFAAyF;IACzF,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;gFAC4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,mBAAmB,mCAAmC,CAAC;AACpE,eAAO,MAAM,mBAAmB,0CAA0C,CAAC;AAE3E;;;;;;;GAOG;AACH,eAAO,MAAM,KAAK,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,SAAS,UAAW,OAAO,WAK1B,CAAC;AAEf;;;;;;;;;GASG;AACH,eAAO,MAAM,OAAO,UAAW,OAAO,WAAoC,CAAC;AAE3E,0DAA0D;AAC1D,eAAO,MAAM,IAAI,GAAI,CAAC,YAAY,CAAC,GAAG,SAAS,QAAQ,CAAC,YACX,CAAC;AAE9C,eAAO,MAAM,QAAQ,aAAc,MAAM,GAAG,SAAS,QAAQ,MAAM,YACX,CAAC;AAEzD,eAAO,MAAM,WAAW,aAAc,MAAM,GAAG,SAAS,QAAQ,MAAM,YAChB,CAAC;AAKvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,IAAI,UAAW,gBAAgB,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAgBlE,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,UAAU,UAAW,gBAAgB,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAGxE,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,UAAU,UAAW,gBAAgB,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAMzE,CAAC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import * as Effect from 'effect/Effect';
|
|
2
|
+
import { type NodeNetworkProps } from './node-network-form.ts';
|
|
3
|
+
import { UNREADABLE, type Unreadable } from './unreadable-read.ts';
|
|
4
|
+
/**
|
|
5
|
+
* PVE's interface types. `unknown` is what a NIC it does not manage reports — node-b and node-c
|
|
6
|
+
* both report `wlan0` that way, and node-d has no such interface at all.
|
|
7
|
+
*
|
|
8
|
+
* ⚠️ THAT ASYMMETRY IS A TRAP FOR A DECLARATION SHARED ACROSS NODES. On node-d the read answers
|
|
9
|
+
* 400 ("interface does not exist"), which this provider cannot tell from "deleted", so
|
|
10
|
+
* reconcile POSTs — writing a stanza for a card that is not in the machine. Declare per node
|
|
11
|
+
* what each node actually has; a physical interface is discovered, not decided.
|
|
12
|
+
*/
|
|
13
|
+
export type NodeNetworkType = 'OVSBond' | 'OVSBridge' | 'OVSIntPort' | 'OVSPort' | 'alias' | 'bond' | 'bridge' | 'eth' | 'fabric' | 'unknown' | 'vlan' | 'vnet';
|
|
14
|
+
/**
|
|
15
|
+
* ⛔ THE LAST SEVEN ARE REPORTED AND NEVER COMPARED — each is a MEASURED forever-diff, and the
|
|
16
|
+
* evidence for every one of them is below, beside the code that reads them.
|
|
17
|
+
*/
|
|
18
|
+
export interface NodeNetworkAttributes {
|
|
19
|
+
node: string;
|
|
20
|
+
iface: string;
|
|
21
|
+
type: string;
|
|
22
|
+
cidr: string;
|
|
23
|
+
gateway: string;
|
|
24
|
+
autostart: boolean;
|
|
25
|
+
mtu: number;
|
|
26
|
+
comments: string;
|
|
27
|
+
bridge_ports: string;
|
|
28
|
+
bridge_vids: string;
|
|
29
|
+
bridge_vlan_aware: boolean;
|
|
30
|
+
slaves: string;
|
|
31
|
+
bond_mode: string;
|
|
32
|
+
bond_xmit_hash_policy: string;
|
|
33
|
+
'bond-primary': string;
|
|
34
|
+
'vlan-id': number;
|
|
35
|
+
'vlan-raw-device': string;
|
|
36
|
+
priority: number;
|
|
37
|
+
method: string;
|
|
38
|
+
families: string;
|
|
39
|
+
active: boolean;
|
|
40
|
+
exists: boolean;
|
|
41
|
+
bond_miimon: string;
|
|
42
|
+
bridge_stp: string;
|
|
43
|
+
bridge_fd: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* ★ Default `read` role — `Sys.Audit` on `/nodes/{node}` (node-network.ts's header).
|
|
47
|
+
* ⛔ `{ node: props.node, iface: props.iface }`, NEVER THE WHOLE `props` — storage.ts's/user.ts's
|
|
48
|
+
* own ⛔, the kit 0.31.1 regression: `GetNodeNetworkRequest`'s schema declares only `node` and
|
|
49
|
+
* `iface` (both path labels); any OTHER key on the object passed to `nodes.getNodeNetwork` is
|
|
50
|
+
* treated by distilled's `buildRequest` as an "unknown key" and JSON-encoded onto this bodyless
|
|
51
|
+
* GET, which a stricter fetch client refuses outright.
|
|
52
|
+
* ⛔ A MISSING INTERFACE IS A 400, NOT A 500 AND NOT A 404 — MEASURED against the live cluster
|
|
53
|
+
* (TB4 `n2`, read-role, read-only probe, 2026-09-24): `GET /nodes/n2/network/vmbr9` answers
|
|
54
|
+
* `{"errors":{"iface":"interface does not exist"},"data":null,"message":"Parameter verification
|
|
55
|
+
* failed.\n"}` at HTTP 400 — a THIRD shape, different from every other migrated family's
|
|
56
|
+
* measured 500. `@distilled.cloud/proxmox` types this as a genuine typed error,
|
|
57
|
+
* `ParameterVerificationFailed` (`errors.ts`), carrying the per-field `errors` object rather
|
|
58
|
+
* than collapsing it to an opaque `BadRequest` — PVE's OWN 400 is never one shape (it is
|
|
59
|
+
* whatever the endpoint's parameter verification rejected), so `errors.ts`'s own header spells
|
|
60
|
+
* out why this ONE typed error is global rather than per-operation.
|
|
61
|
+
* ⛔ ONLY THIS EXACT SIGNAL MEANS ABSENT, EVEN WITH THE FOLD BELOW. A different 400 reason, a
|
|
62
|
+
* permission gap, a network blip, or ANY OTHER `ParameterVerificationFailed` reason is never
|
|
63
|
+
* read as "this interface is not there" — `diff` calls this directly and propagates every one
|
|
64
|
+
* of them loudly.
|
|
65
|
+
*/
|
|
66
|
+
export declare const readInterfaceOrFail: (props: NodeNetworkProps) => Effect.Effect<typeof UNREADABLE | NodeNetworkAttributes | undefined, import("@distilled.cloud/caddy").BadGateway | import("@distilled.cloud/caddy").BadRequest | import("@distilled.cloud/caddy").ConfigError | Error | import("@distilled.cloud/caddy").GatewayTimeout | import("effect/unstable/http/HttpClientError").HttpClientError | import("@distilled.cloud/caddy").InternalServerError | import("@distilled.cloud/proxmox/Errors").ParameterVerificationFailed | import("@distilled.cloud/proxmox/Errors").ProxmoxParseError | import("./distilled-pve.ts").PveClusterExhausted | import("@distilled.cloud/caddy").ServiceUnavailable | import("effect/Cause").TimeoutError | import("@distilled.cloud/caddy").TooManyRequests | import("@distilled.cloud/caddy").Unauthorized | import("@distilled.cloud/proxmox/Errors").UnknownProxmoxError, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
67
|
+
/**
|
|
68
|
+
* ⛔ FOLDS A GENUINE READ FAILURE TO `undefined` TOO, AND ONLY `read`'s adoption/recovery branch
|
|
69
|
+
* AND `reconcile` MAY USE IT — FOUND BY ADVERSARIAL REVIEW, 2026-09-24: a first draft used
|
|
70
|
+
* `readInterfaceOrFail` (precise, non-folding) for EVERY caller, on the reasoning that a typed,
|
|
71
|
+
* parsed absence signal made folding unnecessary. That reasoning conflated two different
|
|
72
|
+
* questions: HOW PRECISELY a signal means absent (this family's is unusually precise), and
|
|
73
|
+
* WHETHER a given caller can safely treat an UNRELATED failure as "proceed as though absent"
|
|
74
|
+
* (storage.ts's/user.ts's own reasoning: a wrongful fold costs at most a redundant
|
|
75
|
+
* `createNodeNetwork` POST, refused loudly). Plan.ts's cold-start adoption probe and
|
|
76
|
+
* interrupted-create recovery, and Apply.ts's delete recovery, all call `read` with
|
|
77
|
+
* `output: undefined` and NO catch of their own (`Plan.ts` v2.0.0-beta.79 ~line 1308) — and
|
|
78
|
+
* Plan.ts fails the WHOLE PLAN, every other resource included, if that call throws. A malformed
|
|
79
|
+
* `iface` in ONE brand-new declaration (a DIFFERENT `ParameterVerificationFailed` than the
|
|
80
|
+
* absence one) would abort `bun run plan` for the entire stack under the precise-only design,
|
|
81
|
+
* where every other migrated family instead defers that same malformed declaration to a scoped,
|
|
82
|
+
* loud refusal at apply. This restores that same fold for those three call sites.
|
|
83
|
+
*/
|
|
84
|
+
export declare const readInterface: (props: NodeNetworkProps) => Effect.Effect<typeof UNREADABLE | NodeNetworkAttributes | undefined, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
85
|
+
/** `read`/`reconcile` return `Attributes | undefined`; only `diff` tells `UNREADABLE` apart. */
|
|
86
|
+
export declare const dropUnreadable: (live: NodeNetworkAttributes | Unreadable | undefined) => NodeNetworkAttributes | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* ⛔ `cidr` IS THE ONLY FIELD COMPARED WHEN UNDECLARED, AND THAT ASYMMETRY IS THE POINT. Every
|
|
89
|
+
* other field here follows storage.ts: undeclared means unmanaged, so it is neither sent nor
|
|
90
|
+
* compared and a plan cannot report an update no write could satisfy. `cidr` cannot be
|
|
91
|
+
* unmanaged, because PVE derives `method` from the presence of an address IN THE FORM — an
|
|
92
|
+
* omission is an edit. Comparing it unconditionally is what makes that edit visible in `plan`
|
|
93
|
+
* instead of at `ifreload` time.
|
|
94
|
+
*
|
|
95
|
+
* ⚠️ NOTHING IN THE REPORTED-ONLY BLOCK IS HERE, and that is what makes the C1 declaration
|
|
96
|
+
* plan as `noop` on all three nodes at once — the module header lists them and the
|
|
97
|
+
* measurement behind each.
|
|
98
|
+
*/
|
|
99
|
+
export declare const matches: (attributes: NodeNetworkAttributes, props: NodeNetworkProps) => boolean;
|
|
100
|
+
//# sourceMappingURL=node-network-wire.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-network-wire.d.ts","sourceRoot":"","sources":["../../src/proxmox/node-network-wire.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,EACL,KAAK,gBAAgB,EAOtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,KAAK,UAAU,EAAoB,MAAM,sBAAsB,CAAC;AAGrF;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,WAAW,GACX,YAAY,GACZ,SAAS,GACT,OAAO,GACP,MAAM,GACN,QAAQ,GACR,KAAK,GACL,QAAQ,GACR,SAAS,GACT,MAAM,GACN,MAAM,CAAC;AAEX;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AA4DD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,mBAAmB,UAAW,gBAAgB,m3BAmBxD,CAAC;AAEJ;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,aAAa,UAAW,gBAAgB,sIACmB,CAAC;AAEzE,gGAAgG;AAChG,eAAO,MAAM,cAAc,SAAU,qBAAqB,GAAG,UAAU,GAAG,SAAS,sCAC3C,CAAC;AAEzC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,OAAO,eAAgB,qBAAqB,SAAS,gBAAgB,YAcnB,CAAC"}
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
* that apply — this one never calls it, exactly as `Proxmox.SdnZone` never calls
|
|
12
12
|
* `Proxmox.SdnApply`.
|
|
13
13
|
*
|
|
14
|
+
* ⛔ `Proxmox.NetworkApply` STAYS ON `client.ts`, NOT MIGRATED WITH THIS FILE — network-apply-
|
|
15
|
+
* read.ts's own header has the two MEASURED reasons distilled cannot represent yet.
|
|
16
|
+
*
|
|
14
17
|
* ⛔ CEPH ON C1 RIDES TWO NETWORKS AND THIS API ONLY SEES ONE OF THEM. Measured from
|
|
15
18
|
* `/etc/pve/ceph.conf` on 2026-09-13:
|
|
16
19
|
*
|
|
@@ -18,7 +21,8 @@
|
|
|
18
21
|
* cluster_network = 203.0.113.0/24 -> every OSD's cluster_addr: .102 / .103 / .104
|
|
19
22
|
*
|
|
20
23
|
* The cluster network — OSD REPLICATION, the traffic that rebuilds a lost replica — lives on
|
|
21
|
-
* `203.0.113.10X/32` addresses carried by `tb0`, `tb1` and `dummy_c1`: the Thunderbolt mesh
|
|
24
|
+
* `203.0.113.10X/32` addresses carried by `tb0`, `tb1` and `dummy_c1`: the Thunderbolt mesh —
|
|
25
|
+
* TB4's own fabric ports, EXCLUDED from this family by decision 28/29/9 (SdnFabric owns them).
|
|
22
26
|
*
|
|
23
27
|
* ⛔ AND THOSE ADDRESSES ARE INVISIBLE HERE, WHICH IS THE TRAP. They are declared in
|
|
24
28
|
* `/etc/network/interfaces.d/sdn`, and PVE's network API does not parse that directory.
|
|
@@ -34,8 +38,9 @@
|
|
|
34
38
|
* owns them, not this resource.
|
|
35
39
|
*
|
|
36
40
|
* ⚠️ Two ways this file limits the damage on the interfaces it CAN see: `cidr` is compared
|
|
37
|
-
* unconditionally (
|
|
38
|
-
* declaration is reported ABSENT rather than retyped
|
|
41
|
+
* unconditionally (node-network-wire.ts's own ⛔ on `matches`), and a live interface whose
|
|
42
|
+
* `type` disagrees with the declaration is reported ABSENT rather than retyped
|
|
43
|
+
* (node-network-wire.ts's `attributesOf`).
|
|
39
44
|
*
|
|
40
45
|
* ★ THE REASON IT EXISTS IS THAT THE STAGED READ CANNOT TELL YOU THE NETWORK IS RIGHT — it can
|
|
41
46
|
* only tell you the FILE is. Once anything is staged, `GET /nodes/{node}/network/{iface}`
|
|
@@ -48,11 +53,9 @@
|
|
|
48
53
|
* object to scope to, so that also buys the node's services, certificates, DNS and time.
|
|
49
54
|
* Collected with every other family in docs/privileges.md.
|
|
50
55
|
*
|
|
51
|
-
* ★ A RECONCILE OVER A MATCHING INTERFACE IS A NO-WRITE
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* the ⛔ above that line names THIS family as the reason the guard was added — adopting an
|
|
55
|
-
* interface a node already has would otherwise leave it holding a pending network change.
|
|
56
|
+
* ★ A RECONCILE OVER A MATCHING INTERFACE IS A NO-WRITE. `reconcile` below gates the PUT on
|
|
57
|
+
* `matches` first — adopting an interface a node already has must not leave it holding a
|
|
58
|
+
* pending network change, since a PUT under `/nodes/{node}/network/{iface}` STAGES one.
|
|
56
59
|
*
|
|
57
60
|
* ⚠️ WHAT IS STILL TRUE: when `matches` is false the PUT rewrites the whole stanza, and that can
|
|
58
61
|
* ADD lines the file did not have — a `vlan-raw-device vmbr1` under `vmbr1.42`, where PVE had
|
|
@@ -64,110 +67,22 @@
|
|
|
64
67
|
* them would put Alchemy one `destroy` away from staging the removal of a node's uplink — and
|
|
65
68
|
* `delete_network` has NO in-use check at all: MEASURED, it deletes the hash entry and writes
|
|
66
69
|
* the file. Adoption stays explicit.
|
|
70
|
+
*
|
|
71
|
+
* ★ MIGRATED OFF `client.ts`'s generic `pve()`/`pveHandlers` ONTO `@distilled.cloud/proxmox`'s
|
|
72
|
+
* typed `nodes.getNodeNetwork`/`createNodeNetwork`/`putNodeNetwork2`/`deleteNodeNetwork2`
|
|
73
|
+
* (2026-09-24, decision 43). Dual-path read and `read`'s `output`-branching wired like every
|
|
74
|
+
* other migrated family; node-network-wire.ts's `readInterfaceOrFail` has the new nuance.
|
|
67
75
|
*/
|
|
68
76
|
import { Resource } from 'alchemy';
|
|
69
77
|
import * as Provider from 'alchemy/Provider';
|
|
70
|
-
import { type
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
* ⚠️ THAT ASYMMETRY IS A TRAP FOR A DECLARATION SHARED ACROSS NODES. On node-d the read answers 404,
|
|
76
|
-
* which this provider cannot tell from "deleted", so reconcile POSTs — writing a stanza for a
|
|
77
|
-
* card that is not in the machine. Declare per node what each node actually has; a physical
|
|
78
|
-
* interface is discovered, not decided.
|
|
79
|
-
*/
|
|
80
|
-
export type NodeNetworkType = 'OVSBond' | 'OVSBridge' | 'OVSIntPort' | 'OVSPort' | 'alias' | 'bond' | 'bridge' | 'eth' | 'fabric' | 'unknown' | 'vlan' | 'vnet';
|
|
81
|
-
export interface NodeNetworkProps extends WithTarget {
|
|
82
|
-
/** Which node's file this stanza lives in. Interfaces are per node, never cluster-wide. */
|
|
83
|
-
node: string;
|
|
84
|
-
/** `vmbr0`, `bond0`, `vmbr1.42`. 2-20 characters, PVE's `pve-iface` format. */
|
|
85
|
-
iface: string;
|
|
86
|
-
/** ⛔ REQUIRED ON EVERY WRITE, update included, never used to retype — see `readAttributes`. */
|
|
87
|
-
type: NodeNetworkType;
|
|
88
|
-
/**
|
|
89
|
-
* `198.51.100.12/24`. ⛔ THE ONE FIELD WITH NO UNMANAGED MODE: leaving it out is an instruction
|
|
90
|
-
* to make the interface `manual`, not an instruction to leave its address alone. The ⛔ on
|
|
91
|
-
* `updateBody` in node-network-form.ts has the measurement and the consequence.
|
|
92
|
-
*/
|
|
93
|
-
cidr?: string;
|
|
94
|
-
/** ⚠️ PVE allows exactly ONE default gateway per node and refuses a second with "Default
|
|
95
|
-
* gateway already exists on interface '<other>'". On C1 it is vmbr0.41's. */
|
|
96
|
-
gateway?: string;
|
|
97
|
-
/**
|
|
98
|
-
* `auto <iface>` in the file. Absent on read means off, which is why `bool`'s fallback is used.
|
|
99
|
-
* ⚠️ UNDECLARED IS UNMANAGED ON AN UPDATE AND OFF ON A CREATE — the same asymmetry storage.ts
|
|
100
|
-
* has, and it bites harder here: an interface created without `autostart` is one the node
|
|
101
|
-
* will not bring up at boot. Every C1 bridge, bond and vlan carries it.
|
|
102
|
-
*/
|
|
103
|
-
autostart?: boolean;
|
|
104
|
-
/** 1280-65520. Unset leaves the file without an `mtu` line and the kernel default in force. */
|
|
105
|
-
mtu?: number;
|
|
106
|
-
/** ⚠️ Round-trips only after normalisation — see `comment` in node-network-form.ts. */
|
|
107
|
-
comments?: string;
|
|
108
|
-
/** Space-separated, a SET: `enp87s0`, or `bond0`. ⚠️ PVE refuses a port already used elsewhere. */
|
|
109
|
-
bridge_ports?: string;
|
|
110
|
-
/** `2-4094`, or `2 100-200`. Only written when `bridge_vlan_aware` is on. */
|
|
111
|
-
bridge_vids?: string;
|
|
112
|
-
/** ⛔ A `false` here is sent as `delete=`, never as `0` — the ⛔ on `body` says why. */
|
|
113
|
-
bridge_vlan_aware?: boolean;
|
|
114
|
-
/**
|
|
115
|
-
* A bond's members, space separated. ⚠️ THE PARAMETER IS `slaves`, NOT `bond_slaves`, in BOTH
|
|
116
|
-
* directions on this PVE: MEASURED, the POST/PUT schema names only `slaves` and the GET returns
|
|
117
|
-
* `"slaves":"enp2s0f0np0 enp2s0f1np1"`. `bond_slaves` appears nowhere in Network.pm here.
|
|
118
|
-
*/
|
|
119
|
-
slaves?: string;
|
|
120
|
-
bond_mode?: string;
|
|
121
|
-
/** ⚠️ Only written when `bond_mode` is `balance-xor` or `802.3ad`; ignored otherwise. */
|
|
122
|
-
bond_xmit_hash_policy?: string;
|
|
123
|
-
/** active-backup only. Kept hyphenated because that is the wire name. */
|
|
124
|
-
'bond-primary'?: string;
|
|
125
|
-
/** ⚠️ DERIVED FROM A DOTTED NAME. `vmbr1.42` reports `vlan-id` 42 with no such line in the file;
|
|
126
|
-
* declaring it there is harmless but adds a line the file did not have. */
|
|
127
|
-
'vlan-id'?: number;
|
|
128
|
-
'vlan-raw-device'?: string;
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* ⛔ THE LAST SEVEN ARE REPORTED AND NEVER COMPARED — each is a MEASURED forever-diff, and the
|
|
132
|
-
* evidence for every one of them is in node-network-form.ts, beside the code that reads them.
|
|
133
|
-
*/
|
|
134
|
-
export interface NodeNetworkAttributes {
|
|
135
|
-
node: string;
|
|
136
|
-
iface: string;
|
|
137
|
-
type: string;
|
|
138
|
-
cidr: string;
|
|
139
|
-
gateway: string;
|
|
140
|
-
autostart: boolean;
|
|
141
|
-
mtu: number;
|
|
142
|
-
comments: string;
|
|
143
|
-
bridge_ports: string;
|
|
144
|
-
bridge_vids: string;
|
|
145
|
-
bridge_vlan_aware: boolean;
|
|
146
|
-
slaves: string;
|
|
147
|
-
bond_mode: string;
|
|
148
|
-
bond_xmit_hash_policy: string;
|
|
149
|
-
'bond-primary': string;
|
|
150
|
-
'vlan-id': number;
|
|
151
|
-
'vlan-raw-device': string;
|
|
152
|
-
priority: number;
|
|
153
|
-
method: string;
|
|
154
|
-
families: string;
|
|
155
|
-
active: boolean;
|
|
156
|
-
exists: boolean;
|
|
157
|
-
bond_miimon: string;
|
|
158
|
-
bridge_stp: string;
|
|
159
|
-
bridge_fd: string;
|
|
160
|
-
}
|
|
78
|
+
import { type NodeNetworkProps } from './node-network-form.ts';
|
|
79
|
+
import type { NodeNetworkAttributes } from './node-network-wire.ts';
|
|
80
|
+
import { type PveRequirements } from './resource-spec.ts';
|
|
81
|
+
export type { NodeNetworkAttributes, NodeNetworkType } from './node-network-wire.ts';
|
|
82
|
+
export type { NodeNetworkProps } from './node-network-form.ts';
|
|
161
83
|
export interface ProxmoxNodeNetwork extends Resource<'Proxmox.NodeNetwork', NodeNetworkProps, NodeNetworkAttributes, never, PveRequirements> {
|
|
162
84
|
}
|
|
163
85
|
/** ★ `retain` by default — a node's only uplink cannot be rebuilt. See the ★ in resource.ts. */
|
|
164
86
|
export declare const ProxmoxNodeNetwork: import("alchemy").ResourceClass<ProxmoxNodeNetwork>;
|
|
165
|
-
/**
|
|
166
|
-
* ⚠️ THE DESTROY IS STAGED LIKE EVERY OTHER WRITE, WHICH MAKES IT THE ONE RECOVERABLE DESTROY IN
|
|
167
|
-
* THIS PACKAGE. It removes the stanza from `interfaces.new` and nothing else;
|
|
168
|
-
* `DELETE /nodes/{node}/network` puts the node back by unlinking that file. It becomes
|
|
169
|
-
* irreversible the moment somebody applies — and PVE performs NO in-use check before removing
|
|
170
|
-
* an interface other bridges or Ceph still stand on.
|
|
171
|
-
*/
|
|
172
87
|
export declare const ProxmoxNodeNetworkProvider: () => import("effect/Layer").Layer<Provider.Provider<ProxmoxNodeNetwork>, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
173
88
|
//# sourceMappingURL=node-network.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-network.d.ts","sourceRoot":"","sources":["../../src/proxmox/node-network.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"node-network.d.ts","sourceRoot":"","sources":["../../src/proxmox/node-network.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0EG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAI7C,OAAO,EAGL,KAAK,gBAAgB,EAGtB,MAAM,wBAAwB,CAAC;AAOhC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAEpE,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAG1D,YAAY,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAIrF,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,MAAM,WAAW,kBAAmB,SAAQ,QAAQ,CAClD,qBAAqB,EACrB,gBAAgB,EAChB,qBAAqB,EACrB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,gGAAgG;AAChG,eAAO,MAAM,kBAAkB,qDAE7B,CAAC;AAiCH,eAAO,MAAM,0BAA0B,wIAuFpC,CAAC"}
|