@homeflare/alchemy 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/caddy/admin-guard.d.ts +1 -1
- package/dist/caddy/index.js.map +2 -2
- package/dist/cloudflare/client.d.ts +1 -1
- package/dist/cloudflare/index.js.map +2 -2
- package/dist/cloudflare/lock-rules.d.ts +1 -1
- package/dist/forgejo/branch-protection-form.d.ts +1 -1
- package/dist/forgejo/branch-protection.d.ts +1 -1
- package/dist/forgejo/index.js.map +10 -10
- package/dist/forgejo/org-actions-secrets.d.ts +1 -1
- package/dist/forgejo/org-label.d.ts +1 -1
- package/dist/forgejo/org-team.d.ts +1 -1
- package/dist/forgejo/repo-webhook.d.ts +1 -1
- package/dist/forgejo/repository.d.ts +2 -2
- package/dist/forgejo/repository.d.ts.map +1 -1
- package/dist/forgejo/resource.d.ts +2 -2
- package/dist/forgejo/team-member.d.ts +1 -1
- package/dist/openbao/auth-role.d.ts +1 -1
- package/dist/openbao/bao-http.d.ts +4 -4
- package/dist/openbao/cloudflare-permission-groups.d.ts +1 -1
- package/dist/openbao/cloudflare-role-form.d.ts +2 -2
- package/dist/openbao/cloudflare-role-form.d.ts.map +1 -1
- package/dist/openbao/cloudflare-roles-expand.d.ts +1 -1
- package/dist/openbao/digest.d.ts +1 -1
- package/dist/openbao/forgejo-bootstrap-parse.d.ts +2 -2
- package/dist/openbao/forgejo-bootstrap-parse.d.ts.map +1 -1
- package/dist/openbao/forgejo-bootstrap.d.ts +1 -1
- package/dist/openbao/index.js.map +14 -14
- package/dist/openbao/mount.d.ts +1 -1
- package/dist/openbao/pki-role.d.ts +1 -1
- package/dist/openbao/policy.d.ts +2 -2
- package/dist/openbao/proxmox-role-form.d.ts +2 -2
- package/dist/openbao/proxmox-role-form.d.ts.map +1 -1
- package/dist/openbao/proxmox-role.d.ts +10 -10
- package/dist/openbao/ssh-role.d.ts +3 -3
- package/dist/proxmox/acl.d.ts +2 -2
- package/dist/proxmox/acl.d.ts.map +1 -1
- package/dist/proxmox/alertmanager-body.d.ts +54 -0
- package/dist/proxmox/alertmanager-body.d.ts.map +1 -0
- package/dist/proxmox/api-token-form.d.ts +2 -2
- package/dist/proxmox/api-token.d.ts +11 -11
- package/dist/proxmox/ceph-daemon-form.d.ts +3 -3
- package/dist/proxmox/ceph-daemon.d.ts +19 -19
- package/dist/proxmox/ceph-daemon.d.ts.map +1 -1
- package/dist/proxmox/ceph-flag.d.ts +5 -5
- package/dist/proxmox/ceph-fs-wire.d.ts +5 -5
- package/dist/proxmox/ceph-fs.d.ts +16 -16
- package/dist/proxmox/ceph-osd-tree.d.ts +16 -16
- package/dist/proxmox/ceph-osd.d.ts +5 -5
- package/dist/proxmox/ceph-pool-form.d.ts +13 -13
- package/dist/proxmox/ceph-pool-form.d.ts.map +1 -1
- package/dist/proxmox/ceph-pool.d.ts +3 -3
- package/dist/proxmox/client.d.ts +2 -2
- package/dist/proxmox/credential-literals.d.ts +36 -0
- package/dist/proxmox/credential-literals.d.ts.map +1 -0
- package/dist/proxmox/credentials.d.ts +8 -8
- package/dist/proxmox/credentials.d.ts.map +1 -1
- package/dist/proxmox/fake-pbs-notify.d.ts +23 -0
- package/dist/proxmox/fake-pbs-notify.d.ts.map +1 -0
- package/dist/proxmox/fake-pve.d.ts +5 -0
- package/dist/proxmox/fake-pve.d.ts.map +1 -1
- package/dist/proxmox/firewall-alias-form.d.ts +1 -1
- package/dist/proxmox/firewall-alias.d.ts +1 -1
- package/dist/proxmox/generated/pbs.d.ts +279 -1
- package/dist/proxmox/generated/pbs.d.ts.map +1 -1
- package/dist/proxmox/generated/pve.d.ts +58 -1
- package/dist/proxmox/generated/pve.d.ts.map +1 -1
- package/dist/proxmox/group.d.ts +8 -8
- package/dist/proxmox/ha-resource.d.ts +3 -2
- package/dist/proxmox/ha-resource.d.ts.map +1 -1
- package/dist/proxmox/ha-rule-form.d.ts +5 -5
- package/dist/proxmox/ha-rule.d.ts +3 -3
- package/dist/proxmox/index.d.ts +14 -0
- package/dist/proxmox/index.d.ts.map +1 -1
- package/dist/proxmox/index.js +935 -223
- package/dist/proxmox/index.js.map +69 -56
- package/dist/proxmox/lease-cache.d.ts +4 -4
- package/dist/proxmox/lxc-harness.d.ts +1 -1
- package/dist/proxmox/lxc-props.d.ts +2 -2
- package/dist/proxmox/lxc.d.ts +3 -3
- package/dist/proxmox/members.d.ts +3 -3
- package/dist/proxmox/network-apply-read.d.ts +5 -5
- package/dist/proxmox/network-apply.d.ts +9 -9
- package/dist/proxmox/node-network-form.d.ts +6 -6
- package/dist/proxmox/node-network.d.ts +17 -17
- package/dist/proxmox/node-network.d.ts.map +1 -1
- package/dist/proxmox/notification-matcher-form.d.ts +100 -0
- package/dist/proxmox/notification-matcher-form.d.ts.map +1 -0
- package/dist/proxmox/notification-matcher.d.ts +46 -0
- package/dist/proxmox/notification-matcher.d.ts.map +1 -0
- package/dist/proxmox/notification-target.d.ts +3 -2
- package/dist/proxmox/notification-target.d.ts.map +1 -1
- package/dist/proxmox/pbs-datastore-guard.d.ts +1 -1
- package/dist/proxmox/pbs-datastore.d.ts +10 -10
- package/dist/proxmox/pbs-notification-matcher.d.ts +53 -0
- package/dist/proxmox/pbs-notification-matcher.d.ts.map +1 -0
- package/dist/proxmox/pbs-notification-target-form.d.ts +63 -0
- package/dist/proxmox/pbs-notification-target-form.d.ts.map +1 -0
- package/dist/proxmox/pbs-notification-target-lifecycle.d.ts +100 -0
- package/dist/proxmox/pbs-notification-target-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/pbs-notification-target-wire.d.ts +30 -0
- package/dist/proxmox/pbs-notification-target-wire.d.ts.map +1 -0
- package/dist/proxmox/pbs-notification-target.d.ts +123 -0
- package/dist/proxmox/pbs-notification-target.d.ts.map +1 -0
- package/dist/proxmox/pbs-prune-job.d.ts +2 -2
- package/dist/proxmox/pbs-sync-job.d.ts +4 -4
- package/dist/proxmox/pbs-verify-job.d.ts +1 -1
- package/dist/proxmox/pool.d.ts +4 -4
- package/dist/proxmox/provision-baseline.d.ts +74 -0
- package/dist/proxmox/provision-baseline.d.ts.map +1 -0
- package/dist/proxmox/provision-bootstrap.d.ts +33 -0
- package/dist/proxmox/provision-bootstrap.d.ts.map +1 -0
- package/dist/proxmox/provision-cli-fake.d.ts +38 -0
- package/dist/proxmox/provision-cli-fake.d.ts.map +1 -0
- package/dist/proxmox/provision-declare.d.ts +25 -0
- package/dist/proxmox/provision-declare.d.ts.map +1 -0
- package/dist/proxmox/replication-job.d.ts +9 -8
- package/dist/proxmox/replication-job.d.ts.map +1 -1
- package/dist/proxmox/resource.d.ts +6 -5
- package/dist/proxmox/resource.d.ts.map +1 -1
- package/dist/proxmox/sdn-apply-read.d.ts +7 -7
- package/dist/proxmox/sdn-apply.d.ts +1 -1
- package/dist/proxmox/sdn-subnet-form.d.ts +1 -1
- package/dist/proxmox/sdn-subnet.d.ts +5 -5
- package/dist/proxmox/sdn-vnet.d.ts +5 -6
- package/dist/proxmox/sdn-vnet.d.ts.map +1 -1
- package/dist/proxmox/storage.d.ts +4 -3
- package/dist/proxmox/storage.d.ts.map +1 -1
- package/dist/proxmox/user.d.ts +3 -2
- package/dist/proxmox/user.d.ts.map +1 -1
- package/dist/proxmox/values.d.ts +1 -1
- package/dist/proxmox/write-only.d.ts +35 -0
- package/dist/proxmox/write-only.d.ts.map +1 -0
- package/dist/proxmox/zfs-pool-write.d.ts +1 -1
- package/dist/proxmox/zfs-pool.d.ts +5 -5
- package/dist/talos/credentials.d.ts +1 -1
- package/dist/talos/credentials.d.ts.map +1 -1
- package/dist/talos/index.js.map +1 -1
- package/dist/verify/fake-engine.d.ts +5 -0
- package/dist/verify/fake-engine.d.ts.map +1 -1
- package/docs/adopt-verify.md +21 -5
- package/docs/adopted-deploys.md +9 -1
- package/docs/ownership.md +26 -1
- package/docs/pbs-alertmanager-body.md +73 -0
- package/docs/pbs-notifications.md +163 -0
- package/docs/provision-baseline.md +106 -0
- package/docs/proxmox-lxc-adopt.md +2 -2
- package/docs/proxmox-lxc.md +5 -4
- package/docs/proxmox.md +9 -0
- package/package.json +1 -1
- package/src/caddy/admin-guard.ts +1 -1
- package/src/caddy/local-admin.ts +1 -1
- package/src/cloudflare/client.ts +1 -1
- package/src/cloudflare/lock-rules.ts +1 -1
- package/src/forgejo/branch-protection-form.ts +1 -1
- package/src/forgejo/branch-protection.ts +1 -1
- package/src/forgejo/client.ts +1 -1
- package/src/forgejo/org-actions-secrets.ts +1 -1
- package/src/forgejo/org-label.ts +1 -1
- package/src/forgejo/org-team.ts +1 -1
- package/src/forgejo/repo-webhook.ts +1 -1
- package/src/forgejo/repository.ts +2 -2
- package/src/forgejo/resource.ts +2 -2
- package/src/forgejo/team-member.ts +1 -1
- package/src/openbao/README.md +4 -4
- package/src/openbao/auth-role.ts +1 -1
- package/src/openbao/bao-http.test.ts +2 -2
- package/src/openbao/bao-http.ts +6 -6
- package/src/openbao/bao-status.test.ts +1 -1
- package/src/openbao/bao-status.ts +1 -1
- package/src/openbao/cloudflare-group-scope.test.ts +1 -1
- package/src/openbao/cloudflare-permission-groups.test.ts +1 -1
- package/src/openbao/cloudflare-permission-groups.ts +3 -3
- package/src/openbao/cloudflare-policy.ts +1 -1
- package/src/openbao/cloudflare-role-form.ts +2 -2
- package/src/openbao/cloudflare-roles-expand.ts +1 -1
- package/src/openbao/digest.ts +1 -1
- package/src/openbao/forgejo-bootstrap-parse.test.ts +3 -3
- package/src/openbao/forgejo-bootstrap-parse.ts +2 -2
- package/src/openbao/forgejo-bootstrap.ts +4 -4
- package/src/openbao/kubernetes-role-form.test.ts +5 -5
- package/src/openbao/mount.ts +1 -1
- package/src/openbao/pki-role.ts +1 -1
- package/src/openbao/policy-wire.test.ts +3 -3
- package/src/openbao/policy.ts +2 -2
- package/src/openbao/proxmox-role-form.ts +5 -5
- package/src/openbao/proxmox-role.ts +11 -11
- package/src/openbao/ssh-role.ts +3 -3
- package/src/proxmox/acl.ts +4 -4
- package/src/proxmox/adopt-noop.test.ts +6 -6
- package/src/proxmox/alertmanager-body.ts +125 -0
- package/src/proxmox/api-token-form.ts +4 -4
- package/src/proxmox/api-token.ts +13 -13
- package/src/proxmox/ceph-daemon-form.ts +3 -3
- package/src/proxmox/ceph-daemon.ts +20 -20
- package/src/proxmox/ceph-flag.ts +7 -7
- package/src/proxmox/ceph-fs-wire.ts +7 -7
- package/src/proxmox/ceph-fs.ts +18 -18
- package/src/proxmox/ceph-osd-tree.ts +16 -16
- package/src/proxmox/ceph-osd.ts +6 -6
- package/src/proxmox/ceph-pool-adopt.test.ts +14 -14
- package/src/proxmox/ceph-pool-form.ts +13 -13
- package/src/proxmox/ceph-pool.ts +6 -6
- package/src/proxmox/client.test.ts +3 -3
- package/src/proxmox/client.ts +3 -3
- package/src/proxmox/credential-literals.test.ts +148 -0
- package/src/proxmox/credential-literals.ts +74 -0
- package/src/proxmox/credentials.test.ts +6 -8
- package/src/proxmox/credentials.ts +8 -8
- package/src/proxmox/fake-pbs-notify.ts +108 -0
- package/src/proxmox/fake-pve.ts +12 -2
- package/src/proxmox/firewall-alias-form.ts +1 -1
- package/src/proxmox/firewall-alias.ts +1 -1
- package/src/proxmox/generated/pbs.ts +330 -1
- package/src/proxmox/generated/pve.ts +63 -1
- package/src/proxmox/group.ts +10 -10
- package/src/proxmox/ha-resource.ts +3 -2
- package/src/proxmox/ha-rule-form.ts +5 -5
- package/src/proxmox/ha-rule.ts +3 -3
- package/src/proxmox/index.test.ts +10 -0
- package/src/proxmox/index.ts +32 -0
- package/src/proxmox/lease-cache.test.ts +19 -19
- package/src/proxmox/lease-cache.ts +4 -4
- package/src/proxmox/lxc-adopt.test.ts +3 -3
- package/src/proxmox/lxc-create-form.ts +1 -1
- package/src/proxmox/lxc-guards.test.ts +4 -4
- package/src/proxmox/lxc-harness.ts +3 -3
- package/src/proxmox/lxc-judge.test.ts +5 -5
- package/src/proxmox/lxc-lifecycle.ts +1 -1
- package/src/proxmox/lxc-props.ts +2 -2
- package/src/proxmox/lxc-read.test.ts +3 -3
- package/src/proxmox/lxc-strict-adopt.test.ts +1 -1
- package/src/proxmox/lxc-volume.test.ts +9 -9
- package/src/proxmox/lxc-volume.ts +1 -1
- package/src/proxmox/lxc-wire.ts +1 -1
- package/src/proxmox/lxc.ts +3 -3
- package/src/proxmox/members.test.ts +1 -1
- package/src/proxmox/members.ts +3 -3
- package/src/proxmox/metric-server-otel.ts +2 -2
- package/src/proxmox/network-apply-read.ts +10 -10
- package/src/proxmox/network-apply.ts +10 -10
- package/src/proxmox/node-network-form.ts +7 -7
- package/src/proxmox/node-network.ts +18 -18
- package/src/proxmox/notification-matcher-form.ts +221 -0
- package/src/proxmox/notification-matcher.test.ts +234 -0
- package/src/proxmox/notification-matcher.ts +76 -0
- package/src/proxmox/notification-target.ts +3 -2
- package/src/proxmox/pbs-datastore-guard.ts +1 -1
- package/src/proxmox/pbs-datastore.ts +10 -10
- package/src/proxmox/pbs-notification-matcher.ts +81 -0
- package/src/proxmox/pbs-notification-target-drift.test.ts +83 -0
- package/src/proxmox/pbs-notification-target-form.ts +229 -0
- package/src/proxmox/pbs-notification-target-lifecycle.ts +177 -0
- package/src/proxmox/pbs-notification-target-state.test.ts +191 -0
- package/src/proxmox/pbs-notification-target-wire.ts +112 -0
- package/src/proxmox/pbs-notification-target.test.ts +226 -0
- package/src/proxmox/pbs-notification-target.ts +139 -0
- package/src/proxmox/pbs-notifications-stack.test.ts +94 -0
- package/src/proxmox/pbs-prune-job.ts +2 -2
- package/src/proxmox/pbs-sync-job.ts +4 -4
- package/src/proxmox/pbs-verify-job.ts +1 -1
- package/src/proxmox/pool.test.ts +2 -2
- package/src/proxmox/pool.ts +4 -4
- package/src/proxmox/provision-baseline.test.ts +87 -0
- package/src/proxmox/provision-baseline.ts +162 -0
- package/src/proxmox/provision-bootstrap.test.ts +132 -0
- package/src/proxmox/provision-bootstrap.ts +135 -0
- package/src/proxmox/provision-cli-fake.ts +217 -0
- package/src/proxmox/provision-declare.test.ts +111 -0
- package/src/proxmox/provision-declare.ts +103 -0
- package/src/proxmox/replication-job.ts +9 -8
- package/src/proxmox/resource.ts +9 -9
- package/src/proxmox/sdn-apply-read.test.ts +16 -16
- package/src/proxmox/sdn-apply-read.ts +7 -7
- package/src/proxmox/sdn-apply.ts +3 -3
- package/src/proxmox/sdn-subnet-form.ts +1 -1
- package/src/proxmox/sdn-subnet.ts +5 -5
- package/src/proxmox/sdn-vnet.ts +5 -6
- package/src/proxmox/storage.ts +4 -3
- package/src/proxmox/user.ts +3 -2
- package/src/proxmox/values.test.ts +2 -2
- package/src/proxmox/values.ts +1 -1
- package/src/proxmox/write-only.test.ts +75 -0
- package/src/proxmox/write-only.ts +106 -0
- package/src/proxmox/zfs-pool-write.ts +1 -1
- package/src/proxmox/zfs-pool.ts +6 -6
- package/src/talos/credentials.ts +3 -3
- package/src/talos/values.test.ts +9 -9
- package/src/verify/cli.test.ts +1 -1
- package/src/verify/fake-engine.ts +8 -1
|
@@ -14,17 +14,17 @@ import { judge } from './lxc-judge.ts';
|
|
|
14
14
|
import { type LxcProps, storedConfig } from './lxc-props.ts';
|
|
15
15
|
import { judgeVolume } from './lxc-volume.ts';
|
|
16
16
|
|
|
17
|
-
const LIVE_MP = 'tank:subvol-
|
|
17
|
+
const LIVE_MP = 'tank:subvol-900-disk-0,mp=/data,size=200G';
|
|
18
18
|
const props = (over: Record<string, unknown>): LxcProps =>
|
|
19
|
-
({ node: 'pve1', target: TARGET, vmid:
|
|
19
|
+
({ node: 'pve1', target: TARGET, vmid: 900, ...over }) as unknown as LxcProps;
|
|
20
20
|
const live = (config: Record<string, unknown>) => ({ unprivileged: 1, ...config });
|
|
21
21
|
|
|
22
22
|
describe('mount points', () => {
|
|
23
23
|
test('another volume on the same storage is refused, never written', () => {
|
|
24
|
-
const verdict = judgeVolume('mp0', 'tank:subvol-
|
|
24
|
+
const verdict = judgeVolume('mp0', 'tank:subvol-900-disk-7,mp=/data,size=200G', LIVE_MP);
|
|
25
25
|
expect(verdict.refuse).toMatch(/detach the live volume to unusedN/);
|
|
26
26
|
expect(verdict.put).toBeUndefined();
|
|
27
|
-
const change = judge(props({ mp0: 'tank:subvol-
|
|
27
|
+
const change = judge(props({ mp0: 'tank:subvol-900-disk-7,mp=/data' }), live({ mp0: LIVE_MP }));
|
|
28
28
|
expect(change.put).toEqual({});
|
|
29
29
|
expect(change.refuse[0]).toMatch(/unusedN/);
|
|
30
30
|
});
|
|
@@ -32,7 +32,7 @@ describe('mount points', () => {
|
|
|
32
32
|
test('a bind mount is root@pam only, new or changed', () => {
|
|
33
33
|
const added = judge(props({ mp1: '/srv/share,mp=/share' }), live({}));
|
|
34
34
|
expect(added.put).toEqual({});
|
|
35
|
-
expect(added.refuse[0]).toMatch(/pct set
|
|
35
|
+
expect(added.refuse[0]).toMatch(/pct set 900 --mp1/);
|
|
36
36
|
const bind = '/srv/share,mp=/share';
|
|
37
37
|
expect(judgeVolume('mp1', `${bind},ro=1`, bind)).toEqual({ rootOnly: true });
|
|
38
38
|
expect(judgeVolume('mp1', bind, bind)).toEqual({});
|
|
@@ -41,13 +41,13 @@ describe('mount points', () => {
|
|
|
41
41
|
test('replicate defaults ON: an explicit replicate=0 is a value, replicate=1 is not', () => {
|
|
42
42
|
expect(judgeVolume('mp0', `${LIVE_MP},replicate=1`, LIVE_MP)).toEqual({});
|
|
43
43
|
const off = judgeVolume('mp0', LIVE_MP, `${LIVE_MP},replicate=0`);
|
|
44
|
-
expect(off.put).toBe('tank:subvol-
|
|
44
|
+
expect(off.put).toBe('tank:subvol-900-disk-0,mp=/data,size=200G');
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
test('options and growth together: the PUT carries the live size, the resize the new one', () => {
|
|
48
48
|
const verdict = judgeVolume('mp0', 'tank:300,mp=/data,backup=1', LIVE_MP);
|
|
49
49
|
expect(verdict).toEqual({
|
|
50
|
-
put: 'tank:subvol-
|
|
50
|
+
put: 'tank:subvol-900-disk-0,mp=/data,backup=1,size=200G',
|
|
51
51
|
resize: '300G',
|
|
52
52
|
});
|
|
53
53
|
});
|
|
@@ -57,7 +57,7 @@ describe('a create allocates new volumes only', () => {
|
|
|
57
57
|
test('an existing volume id in a create is refused; the new-disk spelling is not', () => {
|
|
58
58
|
const base = { ostemplate: 'local:vztmpl/t.tar.zst' };
|
|
59
59
|
const existing = createRefusals(
|
|
60
|
-
props({ ...base, rootfs: 'local-zfs:subvol-
|
|
60
|
+
props({ ...base, rootfs: 'local-zfs:subvol-900-disk-0,size=8G' }),
|
|
61
61
|
);
|
|
62
62
|
expect(existing.join('\n')).toMatch(/rootfs: .* names an existing volume/);
|
|
63
63
|
expect(
|
|
@@ -84,7 +84,7 @@ describe('keys the resource does not manage', () => {
|
|
|
84
84
|
mp0: LIVE_MP,
|
|
85
85
|
parent: 'snap1',
|
|
86
86
|
unprivileged: 1,
|
|
87
|
-
unused0: 'tank:subvol-
|
|
87
|
+
unused0: 'tank:subvol-900-disk-9',
|
|
88
88
|
};
|
|
89
89
|
expect(storedConfig(read)).toEqual({
|
|
90
90
|
hostname: 'x',
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* ⛔ AN EXISTING VOLUME IS NEVER WRITTEN WITH THE NEW-DISK SPELLING. `tank:100` in a PUT means
|
|
15
15
|
* "allocate a fresh 100 GiB volume", and PVE then moves the volume the guest was using to
|
|
16
16
|
* `unusedN`. So a declared `tank:100,mp=/data` that matches a live
|
|
17
|
-
* `tank:subvol-
|
|
17
|
+
* `tank:subvol-900-disk-1,mp=/data,size=100G` compares EQUAL, and an options change on it is
|
|
18
18
|
* written with the LIVE volume id — the declaration's spelling is never sent back for a volume
|
|
19
19
|
* that exists.
|
|
20
20
|
*/
|
package/src/proxmox/lxc-wire.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* ★ EVERY RULE HERE IS READ OFF pve-container 6.1.14 (the PVE 9 line, `src/PVE/LXC/Config.pm` at
|
|
5
5
|
* git.proxmox.com HEAD, 2026-09-21) or off a live guest's config read with
|
|
6
|
-
* `pvesh get /nodes/<node>/lxc
|
|
6
|
+
* `pvesh get /nodes/<node>/lxc/<vmid>/config`. A rule nobody measured is a forever-diff waiting to
|
|
7
7
|
* happen: the plan reports an update, the deploy writes the same value back, and the next plan
|
|
8
8
|
* reports it again.
|
|
9
9
|
*
|
package/src/proxmox/lxc.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `Proxmox.Lxc` — a container, declared: adopted as it runs, or created from a template.
|
|
3
3
|
*
|
|
4
|
-
* ★
|
|
5
|
-
* VM.
|
|
6
|
-
* Datastore.AllocateSpace, Datastore.Audit, SDN.Use, Sys.Audit. What
|
|
4
|
+
* ★ THE PROVISIONING BASELINE HOLDS WHAT THIS NEEDS (provision-baseline.ts): on `/`, VM.Allocate,
|
|
5
|
+
* VM.Audit, VM.Config.{CPU,Disk,HWType,Memory,Network,Options}, VM.PowerMgmt,
|
|
6
|
+
* Datastore.AllocateSpace, Datastore.Audit, SDN.Use, Sys.Audit. What no token can do — device
|
|
7
7
|
* passthrough, bind mounts, features beyond nesting — PVE keeps for root@pam, and lxc-judge.ts
|
|
8
8
|
* refuses at plan with the `pct set` to run instead.
|
|
9
9
|
*
|
|
@@ -15,7 +15,7 @@ const transport = (cause: Error) =>
|
|
|
15
15
|
new HttpClientError.HttpClientError({
|
|
16
16
|
reason: new HttpClientError.TransportError({
|
|
17
17
|
cause,
|
|
18
|
-
request: { method: 'PUT', url: 'https://
|
|
18
|
+
request: { method: 'PUT', url: 'https://node-b.mgmt.example.com:8006/api2/json/x' } as never,
|
|
19
19
|
}),
|
|
20
20
|
});
|
|
21
21
|
|
package/src/proxmox/members.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* C1 cluster members: ordered failover, last-good preference, transport classification.
|
|
3
3
|
*
|
|
4
4
|
* ★ ANY MEMBER'S :8006 API MANAGES THE WHOLE CLUSTER — PVE proxies node paths itself — so the
|
|
5
5
|
* provider reaches the cluster through whichever member is up, not through one pinned node.
|
|
6
6
|
*
|
|
7
|
-
* ⛔ NEVER cluster-
|
|
7
|
+
* ⛔ NEVER cluster-c1.example.com: it resolves to 192.0.2.250, which nothing serves.
|
|
8
8
|
*/
|
|
9
9
|
import * as Effect from 'effect/Effect';
|
|
10
10
|
import * as Result from 'effect/Result';
|
|
@@ -120,7 +120,7 @@ export type PveMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
|
120
120
|
* elsewhere can repeat a write.
|
|
121
121
|
*
|
|
122
122
|
* ★ Reads fail over on any transport failure, including timeout — no side effect to duplicate.
|
|
123
|
-
* MEASURED 2026-09-14: full
|
|
123
|
+
* MEASURED 2026-09-14: full C1 plans stayed all-noop with the first member refused (127.0.0.1)
|
|
124
124
|
* and with it unresolvable (n9.invalid).
|
|
125
125
|
*/
|
|
126
126
|
export const executeOnCluster = (
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* `Content-Type: application/json` (pve-manager PVE/Status/OpenTelemetry.pm:643) and gzips by
|
|
13
13
|
* default; there is no protobuf option. vector's opentelemetry source and VictoriaMetrics both
|
|
14
14
|
* take protobuf only — measured 2026-09-14 — so a server pointed straight at either fails every
|
|
15
|
-
* push. That is why
|
|
16
|
-
* (
|
|
15
|
+
* push. That is why C1's declaration points at each node's own vector bridge
|
|
16
|
+
* (the estate's per-node vector config) rather than at the estate's intake.
|
|
17
17
|
*
|
|
18
18
|
* ⛔ `otel-headers` IS `never`, FOR THE REASON `token` IS. It is where an OTLP bearer token goes —
|
|
19
19
|
* base64 of a JSON object of arbitrary HTTP headers — and Alchemy persists attributes UNENCRYPTED.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* diff, and the only write in either file is the apply itself.
|
|
8
8
|
*
|
|
9
9
|
* ⛔ THE PENDING STATE IS NOT IN `data`, WHICH IS WHY THIS FILE DOES ITS OWN FETCH. MEASURED in the
|
|
10
|
-
* cluster's own source on
|
|
10
|
+
* cluster's own source on node-b, 2026-09-13 — not inferred from documentation:
|
|
11
11
|
* PVE/API2/Network.pm my $tmp = PVE::INotify::read_file('interfaces', 1);
|
|
12
12
|
* $rpcenv->set_result_attrib('changes', $changes) if $changes;
|
|
13
13
|
* PVE/HTTPServer.pm if (my $diff = $rpcenv->get_result_attrib('changes')) {
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
* `GET /nodes/{node}/network` answers with the STAGED config and a node about to be reconfigured
|
|
25
25
|
* looks identical to one already running it. Same trap `sdn-apply.ts` documents for `?running=1`.
|
|
26
26
|
*
|
|
27
|
-
* ⚠️ `active` IS NOT A PENDING SIGNAL, AND THE OBVIOUS SHORTCUT IS MEASURABLY WRONG. On
|
|
28
|
-
* with nothing staged anywhere (VERIFIED 2026-09-13: `/etc/network/interfaces.new` absent on
|
|
29
|
-
*
|
|
27
|
+
* ⚠️ `active` IS NOT A PENDING SIGNAL, AND THE OBVIOUS SHORTCUT IS MEASURABLY WRONG. On node-b and node-c,
|
|
28
|
+
* with nothing staged anywhere (VERIFIED 2026-09-13: `/etc/network/interfaces.new` absent on node-b,
|
|
29
|
+
* node-c and node-d), `wlan0` already comes back with no `active` key at all. Counting interfaces that
|
|
30
30
|
* are not up as "pending" would report an update on two of the three nodes forever.
|
|
31
31
|
*
|
|
32
32
|
* ⚠️ ONE THING GENUINELY CANNOT BE SEEN FROM HERE, AND IT IS SMALL. The diff is computed as
|
|
@@ -58,7 +58,7 @@ export type ClusterHealth = { quorate: boolean; offline: readonly string[] };
|
|
|
58
58
|
*
|
|
59
59
|
* ⛔ THIS STRING NEVER LEAVES THIS MODULE, AND THAT IS A SECRET RULE, NOT A STYLE ONE. Alchemy
|
|
60
60
|
* persists attributes UNENCRYPTED (credentials.ts spells out why), and `/etc/network/interfaces`
|
|
61
|
-
* is a file that can legally contain `wpa-psk` —
|
|
61
|
+
* is a file that can legally contain `wpa-psk` — node-b and node-c both carry a `wlan0`, MEASURED — as
|
|
62
62
|
* well as any `pre-up` command somebody wrote. A diff of that file in a state store is a secret
|
|
63
63
|
* in a state store. Only the COUNT below ever escapes.
|
|
64
64
|
*/
|
|
@@ -70,7 +70,7 @@ const stagedDiff = (target: PveTarget, node: string) =>
|
|
|
70
70
|
// provisioning lease, and a plan stays a plan even if the write role is missing entirely.
|
|
71
71
|
// ⛔ THROUGH THE LEASE CACHE, NOT `mint`. This read needs no particular identity — only the
|
|
72
72
|
// apply and its task poll do, and network-apply.ts mints for those itself. Minting here left
|
|
73
|
-
// SIX extra `hf-read@pve` tokens on
|
|
73
|
+
// SIX extra `hf-read@pve` tokens on node-b per C1 plan (one per node, for read and for diff),
|
|
74
74
|
// MEASURED 2026-09-14 while the cache itself made one read mint for the whole run.
|
|
75
75
|
const credential = yield* leased(target, 'read');
|
|
76
76
|
const envelope = (yield* pveEnvelopeWith(target, credential, 'GET', path)) as NetworkEnvelope;
|
|
@@ -108,9 +108,9 @@ export const pendingCount = (target: PveTarget, node: string) =>
|
|
|
108
108
|
* Quorum, and every member that is not online.
|
|
109
109
|
*
|
|
110
110
|
* ⛔ QUORUM ALONE IS NOT THE CHECK, AND ON A THREE-NODE CLUSTER IT WOULD MISS THE ACCIDENT THIS
|
|
111
|
-
* RESOURCE IS AFRAID OF. Losing ONE of three leaves `quorate: 1` — MEASURED shape on
|
|
112
|
-
* `{"id":"cluster","name":"HF-
|
|
113
|
-
* `online: 1`. So a reload that drops
|
|
111
|
+
* RESOURCE IS AFRAID OF. Losing ONE of three leaves `quorate: 1` — MEASURED shape on C1:
|
|
112
|
+
* `{"id":"cluster","name":"HF-C1","nodes":3,"quorate":1,…}` plus one row per node with
|
|
113
|
+
* `online: 1`. So a reload that drops node-b off the network is invisible to `quorate` and visible
|
|
114
114
|
* only as `online: 0` on that row. Both are checked, and the node names come back so the failure
|
|
115
115
|
* message can say WHICH member went away.
|
|
116
116
|
*
|
|
@@ -140,7 +140,7 @@ const clusterHealth = (target: PveTarget) =>
|
|
|
140
140
|
*
|
|
141
141
|
* ★ A REASON RATHER THAN A BOOLEAN, BECAUSE THE CALLER'S ONLY USE FOR IT IS A REFUSAL MESSAGE.
|
|
142
142
|
* "The deploy stopped because the cluster was degraded" is not actionable at three in the
|
|
143
|
-
* morning; "quorate=true offline=[
|
|
143
|
+
* morning; "quorate=true offline=[node-c]" is. The two callers in network-apply.ts ask this before
|
|
144
144
|
* the reload and again after it, and the second answer is the one that stops the chain from
|
|
145
145
|
* reaching the next node.
|
|
146
146
|
*
|
|
@@ -4,23 +4,23 @@
|
|
|
4
4
|
* ⛔ WITHOUT IT EVERY NETWORK DECLARATION IS A GREEN PLAN THAT CHANGES NO NETWORK; WITH IT A GREEN
|
|
5
5
|
* PLAN CAN TAKE A NODE OFF THE NETWORK. Writes under `nodes/{node}/network/{iface}` edit
|
|
6
6
|
* `/etc/network/interfaces.new` and touch no interface. `PUT /nodes/{node}/network` is the apply.
|
|
7
|
-
* MEASURED in the cluster's own source on
|
|
7
|
+
* MEASURED in the cluster's own source on node-b, 2026-09-13 — `PVE/API2/Network.pm`:
|
|
8
8
|
* rename($new_config_file, $current_config_file) if -e $new_config_file;
|
|
9
9
|
* PVE::Tools::run_command(['ifreload', '-a'], errfunc => $err);
|
|
10
10
|
* return $rpcenv->fork_worker('srvreload', 'networking', $authuser, $worker);
|
|
11
11
|
*
|
|
12
|
-
* ⛔ THIS IS THE MOST DANGEROUS RESOURCE IN THE PACKAGE, AND ON
|
|
13
|
-
* Ceph runs over `vmbr1.
|
|
12
|
+
* ⛔ THIS IS THE MOST DANGEROUS RESOURCE IN THE PACKAGE, AND ON C1 THE DANGER IS NOT ABSTRACT.
|
|
13
|
+
* Ceph runs over `vmbr1.42` on node-b, node-c AND node-d (MEASURED 2026-09-13: four pools, size 3 /
|
|
14
14
|
* min_size 2, mon+mgr+mds on all three). `ifreload -a` takes that node's OSDs, mons and MDS off
|
|
15
15
|
* the cluster network for the length of the reload; two nodes at once puts every pool below
|
|
16
16
|
* min_size and blocks IO for every guest with an RBD disk. The management address the API is
|
|
17
|
-
* reached on, `vmbr0.
|
|
17
|
+
* reached on, `vmbr0.41`, is reloaded by the same call.
|
|
18
18
|
*
|
|
19
19
|
* ⛔ AND UNLIKE `SdnApply` IT IS PER NODE, SO A HALF-RECONFIGURED CLUSTER IS THE DEFAULT FAILURE
|
|
20
20
|
* MODE, NOT AN EDGE CASE. `PUT /cluster/sdn` publishes the whole cluster in one call; this
|
|
21
|
-
* publishes exactly one node, so a stack holding three of these can apply
|
|
22
|
-
* and
|
|
23
|
-
* 1. `after` SERIALISES THEM — chain
|
|
21
|
+
* publishes exactly one node, so a stack holding three of these can apply node-b, fail, and leave node-c
|
|
22
|
+
* and node-d on the old layout with Ceph spanning all three. Four things here exist only for that:
|
|
23
|
+
* 1. `after` SERIALISES THEM — chain node-c after node-b and node-d after node-c, so at most one node is ever
|
|
24
24
|
* mid-reload. Three declared in parallel is the outage, and nothing in Alchemy stops you.
|
|
25
25
|
* 2. `reconcile` REFUSES TO START on a cluster that is already degraded.
|
|
26
26
|
* 3. `reconcile` RE-CHECKS quorum and every member's `online` flag AFTER the reload and dies if
|
|
@@ -58,7 +58,7 @@ import type { WithTarget } from './resource.ts';
|
|
|
58
58
|
|
|
59
59
|
export interface NetworkApplyProps extends WithTarget {
|
|
60
60
|
/**
|
|
61
|
-
* The node whose staged interfaces file this applies — `
|
|
61
|
+
* The node whose staged interfaces file this applies — `node-b`.
|
|
62
62
|
*
|
|
63
63
|
* ⚠️ ONE APPLY PER NODE, AND NEVER TWO FOR THE SAME NODE. Two would each publish the other's
|
|
64
64
|
* staged half, and both would run `ifreload` on a file the other had not finished writing.
|
|
@@ -99,7 +99,7 @@ export interface NetworkApplyAttributes {
|
|
|
99
99
|
* `active`, `exists`, `families`, `method`, `method6`, `options`, `priority`, `link-type` and
|
|
100
100
|
* ten more; `altnames` is returned and is not even in the schema. `priority` is the worst of
|
|
101
101
|
* them: PVE ASSIGNS it from the order of the interfaces file, so the same logical layout
|
|
102
|
-
* carries different numbers on different nodes (MEASURED:
|
|
102
|
+
* carries different numbers on different nodes (MEASURED: node-d's differ from node-b/node-c's by one).
|
|
103
103
|
* Every one of those is a forever-diff waiting for whoever writes `Proxmox.NetworkInterface`.
|
|
104
104
|
* This resource compares a single integer against zero and never looks at a row at all.
|
|
105
105
|
*/
|
|
@@ -128,7 +128,7 @@ const read = (props: NetworkApplyProps) =>
|
|
|
128
128
|
* decides it is whether the node is carrying a staged change right now.
|
|
129
129
|
*
|
|
130
130
|
* ★ THIS IS ALSO WHY DECLARING THE LIVE CLUSTER PLANS AS `noop`. VERIFIED 2026-09-13:
|
|
131
|
-
* `/etc/network/interfaces.new` is absent on
|
|
131
|
+
* `/etc/network/interfaces.new` is absent on node-b, node-c and node-d, so `changes` is absent from all three
|
|
132
132
|
* answers, so `pending` is 0 on all three and every one of them plans `noop`.
|
|
133
133
|
*/
|
|
134
134
|
const diff = (news: Input<NetworkApplyProps>) =>
|
|
@@ -14,14 +14,14 @@
|
|
|
14
14
|
*
|
|
15
15
|
* * ⛔ THE LAST SEVEN ARE REPORTED AND NEVER COMPARED, AND EACH ONE IS A MEASURED FOREVER-DIFF.
|
|
16
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
|
|
18
|
-
* priority 16 on
|
|
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
19
|
* reused across three nodes would diff on exactly one of them, forever, over nothing.
|
|
20
20
|
* `method` is RECOMPUTED on every write from whether the form carried an address, `families` is
|
|
21
21
|
* recomputed the same way and never written to the file, `active` and `exists` describe the
|
|
22
22
|
* kernel rather than the config, and `bond_miimon`, `bridge_stp` and `bridge_fd` are emitted by
|
|
23
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:
|
|
24
|
+
* `additionalProperties => 0` means sending one is a 400. Live proof of all three: node-b's bond0
|
|
25
25
|
* returns `bond_miimon "100"` and vmbr1 returns `bridge_stp "off"`, `bridge_fd "0"`, none of
|
|
26
26
|
* which any declaration can set.
|
|
27
27
|
*/
|
|
@@ -74,7 +74,7 @@ export const ifaceList = (value: unknown) =>
|
|
|
74
74
|
* `ceph transport` comes back as `ceph transport\n` and the two never compare equal.
|
|
75
75
|
* ⚠️ AND THE READER FOLDS `comments6` INTO `comments` (`$d->{comments} .= $comments6`), so on an
|
|
76
76
|
* interface that carries an IPv6 comment a declared `comments` CANNOT match what comes back.
|
|
77
|
-
*
|
|
77
|
+
* C1 has none; on an interface that does, leave `comments` undeclared rather than fighting it.
|
|
78
78
|
*/
|
|
79
79
|
export const comment = (value: unknown) => text(value).replace(/\s+$/, '');
|
|
80
80
|
|
|
@@ -111,7 +111,7 @@ const field = (name: string, value: string | undefined): Record<string, string>
|
|
|
111
111
|
*
|
|
112
112
|
* ⚠️ `netmask` IS A WRITABLE PARAMETER AND IS DELIBERATELY NOT OFFERED. PVE raises
|
|
113
113
|
* "netmask conflicts with cidr" when both are sent, and the reader rewrites whatever was written
|
|
114
|
-
* into prefix form — MEASURED:
|
|
114
|
+
* into prefix form — MEASURED: node-b's vmbr1.42 reports `"netmask":"24"`, never `255.255.255.0`.
|
|
115
115
|
* A resource accepting both spellings would let a declaration diff against its own value.
|
|
116
116
|
*/
|
|
117
117
|
export const body = (props: NodeNetworkProps): Record<string, string> => ({
|
|
@@ -144,7 +144,7 @@ export const body = (props: NodeNetworkProps): Record<string, string> => ({
|
|
|
144
144
|
* `ifreload -a` I did NOT measure. Clearing cidr explicitly at least produces a clean, honest
|
|
145
145
|
* manual interface — and `matches` compares `cidr` UNCONDITIONALLY, so the plan says so first.
|
|
146
146
|
*
|
|
147
|
-
* ⛔ ON vmbr1.
|
|
147
|
+
* ⛔ ON vmbr1.42 THAT IS THE CEPH TRANSPORT. A declaration of that interface without its `cidr` is
|
|
148
148
|
* a declaration that it should have no address, applied across three nodes. The plan will read
|
|
149
149
|
* `1 to update` rather than `noop`; do not wave it through.
|
|
150
150
|
*
|
|
@@ -177,7 +177,7 @@ export const readAttributes = (
|
|
|
177
177
|
* same call metric-server.ts makes, for a worse reason. `type` is required on the PUT and
|
|
178
178
|
* the PUT MERGES, so a declaration naming `bridge` for what is really a vlan would write
|
|
179
179
|
* `type bridge` into the stanza and PVE's writer would then emit bridge-ports and
|
|
180
|
-
* bridge-stp lines for vmbr1.
|
|
180
|
+
* bridge-stp lines for vmbr1.42. Reporting absent instead makes reconcile POST a create,
|
|
181
181
|
* which PVE refuses with "interface already exists": loud, and it changes nothing.
|
|
182
182
|
*/
|
|
183
183
|
const liveType = text(live['type']);
|
|
@@ -11,23 +11,23 @@
|
|
|
11
11
|
* that apply — this one never calls it, exactly as `Proxmox.SdnZone` never calls
|
|
12
12
|
* `Proxmox.SdnApply`.
|
|
13
13
|
*
|
|
14
|
-
* ⛔ CEPH ON
|
|
14
|
+
* ⛔ CEPH ON C1 RIDES TWO NETWORKS AND THIS API ONLY SEES ONE OF THEM. Measured from
|
|
15
15
|
* `/etc/pve/ceph.conf` on 2026-09-13:
|
|
16
16
|
*
|
|
17
|
-
* public_network =
|
|
18
|
-
* cluster_network =
|
|
17
|
+
* public_network = 198.51.100.0/24 -> vmbr1.42, which this resource DOES see
|
|
18
|
+
* cluster_network = 203.0.113.0/24 -> every OSD's cluster_addr: .102 / .103 / .104
|
|
19
19
|
*
|
|
20
20
|
* The cluster network — OSD REPLICATION, the traffic that rebuilds a lost replica — lives on
|
|
21
|
-
* `
|
|
21
|
+
* `203.0.113.10X/32` addresses carried by `tb0`, `tb1` and `dummy_c1`: the Thunderbolt mesh.
|
|
22
22
|
*
|
|
23
23
|
* ⛔ AND THOSE ADDRESSES ARE INVISIBLE HERE, WHICH IS THE TRAP. They are declared in
|
|
24
24
|
* `/etc/network/interfaces.d/sdn`, and PVE's network API does not parse that directory.
|
|
25
|
-
* MEASURED: `GET /nodes/
|
|
26
|
-
* address, while `ip -br -4 addr` shows `
|
|
27
|
-
*
|
|
25
|
+
* MEASURED: `GET /nodes/node-b/network/tb0` answers `{"method":"manual","type":"eth",…}` with NO
|
|
26
|
+
* address, while `ip -br -4 addr` shows `tb0 UP 203.0.113.102/32`. So this resource reports
|
|
27
|
+
* tb0 and tb1 as address-less manual ports and will plan `noop` over them — implicitly
|
|
28
28
|
* asserting they carry no address, which is FALSE.
|
|
29
29
|
*
|
|
30
|
-
* The consequence, stated plainly: declaring
|
|
30
|
+
* The consequence, stated plainly: declaring tb0/tb1 from what this API reports and then
|
|
31
31
|
* DESTROYING or applying over them takes out OSD replication on a cluster whose pools are
|
|
32
32
|
* size 3 / min_size 2. They read as the most boring interfaces on the node and are the two most
|
|
33
33
|
* dangerous. Leave them undeclared; if they must be managed, manage the file that actually
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
* interface a node already has would otherwise leave it holding a pending network change.
|
|
56
56
|
*
|
|
57
57
|
* ⚠️ WHAT IS STILL TRUE: when `matches` is false the PUT rewrites the whole stanza, and that can
|
|
58
|
-
* ADD lines the file did not have — a `vlan-raw-device vmbr1` under `vmbr1.
|
|
58
|
+
* ADD lines the file did not have — a `vlan-raw-device vmbr1` under `vmbr1.42`, where PVE had
|
|
59
59
|
* been deriving it from the name. Harmless in content, and it costs nothing until somebody
|
|
60
60
|
* applies; one more reason the apply is a separate resource.
|
|
61
61
|
*
|
|
@@ -79,10 +79,10 @@ import {
|
|
|
79
79
|
import { type PveRequirements, type WithTarget, pveHandlers } from './resource.ts';
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
|
-
* PVE's interface types. `unknown` is what a NIC it does not manage reports —
|
|
83
|
-
* report `
|
|
82
|
+
* PVE's interface types. `unknown` is what a NIC it does not manage reports — node-b and node-c both
|
|
83
|
+
* report `wlan0` that way, and node-d has no such interface at all.
|
|
84
84
|
*
|
|
85
|
-
* ⚠️ THAT ASYMMETRY IS A TRAP FOR A DECLARATION SHARED ACROSS NODES. On
|
|
85
|
+
* ⚠️ THAT ASYMMETRY IS A TRAP FOR A DECLARATION SHARED ACROSS NODES. On node-d the read answers 404,
|
|
86
86
|
* which this provider cannot tell from "deleted", so reconcile POSTs — writing a stanza for a
|
|
87
87
|
* card that is not in the machine. Declare per node what each node actually has; a physical
|
|
88
88
|
* interface is discovered, not decided.
|
|
@@ -104,24 +104,24 @@ export type NodeNetworkType =
|
|
|
104
104
|
export interface NodeNetworkProps extends WithTarget {
|
|
105
105
|
/** Which node's file this stanza lives in. Interfaces are per node, never cluster-wide. */
|
|
106
106
|
node: string;
|
|
107
|
-
/** `vmbr0`, `bond0`, `vmbr1.
|
|
107
|
+
/** `vmbr0`, `bond0`, `vmbr1.42`. 2-20 characters, PVE's `pve-iface` format. */
|
|
108
108
|
iface: string;
|
|
109
109
|
/** ⛔ REQUIRED ON EVERY WRITE, update included, never used to retype — see `readAttributes`. */
|
|
110
110
|
type: NodeNetworkType;
|
|
111
111
|
/**
|
|
112
|
-
* `
|
|
112
|
+
* `198.51.100.12/24`. ⛔ THE ONE FIELD WITH NO UNMANAGED MODE: leaving it out is an instruction
|
|
113
113
|
* to make the interface `manual`, not an instruction to leave its address alone. The ⛔ on
|
|
114
114
|
* `updateBody` in node-network-form.ts has the measurement and the consequence.
|
|
115
115
|
*/
|
|
116
116
|
cidr?: string;
|
|
117
117
|
/** ⚠️ PVE allows exactly ONE default gateway per node and refuses a second with "Default
|
|
118
|
-
* gateway already exists on interface '<other>'". On
|
|
118
|
+
* gateway already exists on interface '<other>'". On C1 it is vmbr0.41's. */
|
|
119
119
|
gateway?: string;
|
|
120
120
|
/**
|
|
121
121
|
* `auto <iface>` in the file. Absent on read means off, which is why `bool`'s fallback is used.
|
|
122
122
|
* ⚠️ UNDECLARED IS UNMANAGED ON AN UPDATE AND OFF ON A CREATE — the same asymmetry storage.ts
|
|
123
123
|
* has, and it bites harder here: an interface created without `autostart` is one the node
|
|
124
|
-
* will not bring up at boot. Every
|
|
124
|
+
* will not bring up at boot. Every C1 bridge, bond and vlan carries it.
|
|
125
125
|
*/
|
|
126
126
|
autostart?: boolean;
|
|
127
127
|
/** 1280-65520. Unset leaves the file without an `mtu` line and the kernel default in force. */
|
|
@@ -145,7 +145,7 @@ export interface NodeNetworkProps extends WithTarget {
|
|
|
145
145
|
bond_xmit_hash_policy?: string;
|
|
146
146
|
/** active-backup only. Kept hyphenated because that is the wire name. */
|
|
147
147
|
'bond-primary'?: string;
|
|
148
|
-
/** ⚠️ DERIVED FROM A DOTTED NAME. `vmbr1.
|
|
148
|
+
/** ⚠️ DERIVED FROM A DOTTED NAME. `vmbr1.42` reports `vlan-id` 42 with no such line in the file;
|
|
149
149
|
* declaring it there is harmless but adds a line the file did not have. */
|
|
150
150
|
'vlan-id'?: number;
|
|
151
151
|
'vlan-raw-device'?: string;
|
|
@@ -209,7 +209,7 @@ const handlers = pveHandlers<NodeNetworkProps, NodeNetworkAttributes>({
|
|
|
209
209
|
* omission is an edit. Comparing it unconditionally is what makes that edit visible in `plan`
|
|
210
210
|
* instead of at `ifreload` time.
|
|
211
211
|
*
|
|
212
|
-
* ⚠️ NOTHING IN THE REPORTED-ONLY BLOCK IS HERE, and that is what makes the
|
|
212
|
+
* ⚠️ NOTHING IN THE REPORTED-ONLY BLOCK IS HERE, and that is what makes the C1 declaration
|
|
213
213
|
* plan as `noop` on all three nodes at once — the ⛔ above `NodeNetworkAttributes` lists them
|
|
214
214
|
* and the measurement behind each.
|
|
215
215
|
*/
|