@homeflare/alchemy 0.1.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/LICENSE +21 -0
- package/README.md +62 -0
- package/dist/cloudflare/client.d.ts +45 -0
- package/dist/cloudflare/client.d.ts.map +1 -0
- package/dist/cloudflare/index.d.ts +11 -0
- package/dist/cloudflare/index.d.ts.map +1 -0
- package/dist/cloudflare/index.js +129 -0
- package/dist/cloudflare/index.js.map +13 -0
- package/dist/cloudflare/lock-rules.d.ts +50 -0
- package/dist/cloudflare/lock-rules.d.ts.map +1 -0
- package/dist/cloudflare/providers.d.ts +19 -0
- package/dist/cloudflare/providers.d.ts.map +1 -0
- package/dist/cloudflare/r2-bucket-lock.d.ts +100 -0
- package/dist/cloudflare/r2-bucket-lock.d.ts.map +1 -0
- package/dist/forgejo/branch-protection-form.d.ts +19 -0
- package/dist/forgejo/branch-protection-form.d.ts.map +1 -0
- package/dist/forgejo/branch-protection.d.ts +47 -0
- package/dist/forgejo/branch-protection.d.ts.map +1 -0
- package/dist/forgejo/client.d.ts +37 -0
- package/dist/forgejo/client.d.ts.map +1 -0
- package/dist/forgejo/index.d.ts +18 -0
- package/dist/forgejo/index.d.ts.map +1 -0
- package/dist/forgejo/index.js +479 -0
- package/dist/forgejo/index.js.map +21 -0
- package/dist/forgejo/org-actions-secrets.d.ts +45 -0
- package/dist/forgejo/org-actions-secrets.d.ts.map +1 -0
- package/dist/forgejo/org-label.d.ts +46 -0
- package/dist/forgejo/org-label.d.ts.map +1 -0
- package/dist/forgejo/org-team.d.ts +42 -0
- package/dist/forgejo/org-team.d.ts.map +1 -0
- package/dist/forgejo/repo-webhook-form.d.ts +27 -0
- package/dist/forgejo/repo-webhook-form.d.ts.map +1 -0
- package/dist/forgejo/repo-webhook.d.ts +46 -0
- package/dist/forgejo/repo-webhook.d.ts.map +1 -0
- package/dist/forgejo/repository.d.ts +45 -0
- package/dist/forgejo/repository.d.ts.map +1 -0
- package/dist/forgejo/resource.d.ts +83 -0
- package/dist/forgejo/resource.d.ts.map +1 -0
- package/dist/forgejo/team-member.d.ts +39 -0
- package/dist/forgejo/team-member.d.ts.map +1 -0
- package/dist/forgejo/values.d.ts +24 -0
- package/dist/forgejo/values.d.ts.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +9 -0
- package/dist/openbao/auth-role-form.d.ts +38 -0
- package/dist/openbao/auth-role-form.d.ts.map +1 -0
- package/dist/openbao/auth-role.d.ts +20 -0
- package/dist/openbao/auth-role.d.ts.map +1 -0
- package/dist/openbao/bao-address.d.ts +72 -0
- package/dist/openbao/bao-address.d.ts.map +1 -0
- package/dist/openbao/bao-http.d.ts +71 -0
- package/dist/openbao/bao-http.d.ts.map +1 -0
- package/dist/openbao/bao-status.d.ts +74 -0
- package/dist/openbao/bao-status.d.ts.map +1 -0
- package/dist/openbao/cloudflare-group-scope.d.ts +73 -0
- package/dist/openbao/cloudflare-group-scope.d.ts.map +1 -0
- package/dist/openbao/cloudflare-parity-catalog.d.ts +28 -0
- package/dist/openbao/cloudflare-parity-catalog.d.ts.map +1 -0
- package/dist/openbao/cloudflare-parity-snapshot.d.ts +31 -0
- package/dist/openbao/cloudflare-parity-snapshot.d.ts.map +1 -0
- package/dist/openbao/cloudflare-parity.d.ts +14 -0
- package/dist/openbao/cloudflare-parity.d.ts.map +1 -0
- package/dist/openbao/cloudflare-permission-groups-solve.d.ts +85 -0
- package/dist/openbao/cloudflare-permission-groups-solve.d.ts.map +1 -0
- package/dist/openbao/cloudflare-permission-groups.d.ts +87 -0
- package/dist/openbao/cloudflare-permission-groups.d.ts.map +1 -0
- package/dist/openbao/cloudflare-policy.d.ts +65 -0
- package/dist/openbao/cloudflare-policy.d.ts.map +1 -0
- package/dist/openbao/cloudflare-role-form.d.ts +110 -0
- package/dist/openbao/cloudflare-role-form.d.ts.map +1 -0
- package/dist/openbao/cloudflare-role-wire.d.ts +27 -0
- package/dist/openbao/cloudflare-role-wire.d.ts.map +1 -0
- package/dist/openbao/cloudflare-role.d.ts +35 -0
- package/dist/openbao/cloudflare-role.d.ts.map +1 -0
- package/dist/openbao/cloudflare-roles-config.d.ts +67 -0
- package/dist/openbao/cloudflare-roles-config.d.ts.map +1 -0
- package/dist/openbao/cloudflare-roles-expand.d.ts +76 -0
- package/dist/openbao/cloudflare-roles-expand.d.ts.map +1 -0
- package/dist/openbao/digest.d.ts +24 -0
- package/dist/openbao/digest.d.ts.map +1 -0
- package/dist/openbao/fake-bao.d.ts +40 -0
- package/dist/openbao/fake-bao.d.ts.map +1 -0
- package/dist/openbao/forgejo-bootstrap-parse.d.ts +46 -0
- package/dist/openbao/forgejo-bootstrap-parse.d.ts.map +1 -0
- package/dist/openbao/forgejo-bootstrap.d.ts +29 -0
- package/dist/openbao/forgejo-bootstrap.d.ts.map +1 -0
- package/dist/openbao/index.d.ts +18 -0
- package/dist/openbao/index.d.ts.map +1 -0
- package/dist/openbao/index.js +1158 -0
- package/dist/openbao/index.js.map +33 -0
- package/dist/openbao/mount-form.d.ts +60 -0
- package/dist/openbao/mount-form.d.ts.map +1 -0
- package/dist/openbao/mount-wire.d.ts +24 -0
- package/dist/openbao/mount-wire.d.ts.map +1 -0
- package/dist/openbao/mount.d.ts +20 -0
- package/dist/openbao/mount.d.ts.map +1 -0
- package/dist/openbao/pki-role-form.d.ts +118 -0
- package/dist/openbao/pki-role-form.d.ts.map +1 -0
- package/dist/openbao/pki-role.d.ts +50 -0
- package/dist/openbao/pki-role.d.ts.map +1 -0
- package/dist/openbao/policy-assembly.d.ts +13 -0
- package/dist/openbao/policy-assembly.d.ts.map +1 -0
- package/dist/openbao/policy-wire.d.ts +41 -0
- package/dist/openbao/policy-wire.d.ts.map +1 -0
- package/dist/openbao/policy.d.ts +83 -0
- package/dist/openbao/policy.d.ts.map +1 -0
- package/dist/openbao/proxmox-role-form.d.ts +92 -0
- package/dist/openbao/proxmox-role-form.d.ts.map +1 -0
- package/dist/openbao/proxmox-role.d.ts +47 -0
- package/dist/openbao/proxmox-role.d.ts.map +1 -0
- package/dist/openbao/ssh-role-form.d.ts +99 -0
- package/dist/openbao/ssh-role-form.d.ts.map +1 -0
- package/dist/openbao/ssh-role.d.ts +43 -0
- package/dist/openbao/ssh-role.d.ts.map +1 -0
- package/dist/proxmox/acl.d.ts +72 -0
- package/dist/proxmox/acl.d.ts.map +1 -0
- package/dist/proxmox/api-token-form.d.ts +59 -0
- package/dist/proxmox/api-token-form.d.ts.map +1 -0
- package/dist/proxmox/api-token.d.ts +159 -0
- package/dist/proxmox/api-token.d.ts.map +1 -0
- package/dist/proxmox/backup-job.d.ts +112 -0
- package/dist/proxmox/backup-job.d.ts.map +1 -0
- package/dist/proxmox/ceph-daemon-form.d.ts +70 -0
- package/dist/proxmox/ceph-daemon-form.d.ts.map +1 -0
- package/dist/proxmox/ceph-daemon.d.ts +162 -0
- package/dist/proxmox/ceph-daemon.d.ts.map +1 -0
- package/dist/proxmox/ceph-flag.d.ts +131 -0
- package/dist/proxmox/ceph-flag.d.ts.map +1 -0
- package/dist/proxmox/ceph-fs-wire.d.ts +106 -0
- package/dist/proxmox/ceph-fs-wire.d.ts.map +1 -0
- package/dist/proxmox/ceph-fs.d.ts +124 -0
- package/dist/proxmox/ceph-fs.d.ts.map +1 -0
- package/dist/proxmox/ceph-osd-tree.d.ts +154 -0
- package/dist/proxmox/ceph-osd-tree.d.ts.map +1 -0
- package/dist/proxmox/ceph-osd-write.d.ts +36 -0
- package/dist/proxmox/ceph-osd-write.d.ts.map +1 -0
- package/dist/proxmox/ceph-osd.d.ts +102 -0
- package/dist/proxmox/ceph-osd.d.ts.map +1 -0
- package/dist/proxmox/ceph-pool-form.d.ts +217 -0
- package/dist/proxmox/ceph-pool-form.d.ts.map +1 -0
- package/dist/proxmox/ceph-pool-settle.d.ts +39 -0
- package/dist/proxmox/ceph-pool-settle.d.ts.map +1 -0
- package/dist/proxmox/ceph-pool.d.ts +48 -0
- package/dist/proxmox/ceph-pool.d.ts.map +1 -0
- package/dist/proxmox/client.d.ts +64 -0
- package/dist/proxmox/client.d.ts.map +1 -0
- package/dist/proxmox/credentials.d.ts +153 -0
- package/dist/proxmox/credentials.d.ts.map +1 -0
- package/dist/proxmox/firewall-alias-form.d.ts +98 -0
- package/dist/proxmox/firewall-alias-form.d.ts.map +1 -0
- package/dist/proxmox/firewall-alias.d.ts +141 -0
- package/dist/proxmox/firewall-alias.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs.d.ts +388 -0
- package/dist/proxmox/generated/pbs.d.ts.map +1 -0
- package/dist/proxmox/generated/pve.d.ts +4713 -0
- package/dist/proxmox/generated/pve.d.ts.map +1 -0
- package/dist/proxmox/group.d.ts +104 -0
- package/dist/proxmox/group.d.ts.map +1 -0
- package/dist/proxmox/ha-resource.d.ts +104 -0
- package/dist/proxmox/ha-resource.d.ts.map +1 -0
- package/dist/proxmox/ha-rule-form.d.ts +103 -0
- package/dist/proxmox/ha-rule-form.d.ts.map +1 -0
- package/dist/proxmox/ha-rule.d.ts +156 -0
- package/dist/proxmox/ha-rule.d.ts.map +1 -0
- package/dist/proxmox/index.d.ts +43 -0
- package/dist/proxmox/index.d.ts.map +1 -0
- package/dist/proxmox/index.js +2322 -0
- package/dist/proxmox/index.js.map +70 -0
- package/dist/proxmox/lease-cache.d.ts +98 -0
- package/dist/proxmox/lease-cache.d.ts.map +1 -0
- package/dist/proxmox/lxc.d.ts +59 -0
- package/dist/proxmox/lxc.d.ts.map +1 -0
- package/dist/proxmox/members.d.ts +56 -0
- package/dist/proxmox/members.d.ts.map +1 -0
- package/dist/proxmox/metric-server-form.d.ts +53 -0
- package/dist/proxmox/metric-server-form.d.ts.map +1 -0
- package/dist/proxmox/metric-server-otel.d.ts +49 -0
- package/dist/proxmox/metric-server-otel.d.ts.map +1 -0
- package/dist/proxmox/metric-server.d.ts +123 -0
- package/dist/proxmox/metric-server.d.ts.map +1 -0
- package/dist/proxmox/network-apply-read.d.ts +93 -0
- package/dist/proxmox/network-apply-read.d.ts.map +1 -0
- package/dist/proxmox/network-apply.d.ts +102 -0
- package/dist/proxmox/network-apply.d.ts.map +1 -0
- package/dist/proxmox/node-network-form.d.ts +128 -0
- package/dist/proxmox/node-network-form.d.ts.map +1 -0
- package/dist/proxmox/node-network.d.ts +173 -0
- package/dist/proxmox/node-network.d.ts.map +1 -0
- package/dist/proxmox/notification-target-form.d.ts +44 -0
- package/dist/proxmox/notification-target-form.d.ts.map +1 -0
- package/dist/proxmox/notification-target.d.ts +146 -0
- package/dist/proxmox/notification-target.d.ts.map +1 -0
- package/dist/proxmox/pbs-datastore-form.d.ts +147 -0
- package/dist/proxmox/pbs-datastore-form.d.ts.map +1 -0
- package/dist/proxmox/pbs-datastore-guard.d.ts +55 -0
- package/dist/proxmox/pbs-datastore-guard.d.ts.map +1 -0
- package/dist/proxmox/pbs-datastore.d.ts +140 -0
- package/dist/proxmox/pbs-datastore.d.ts.map +1 -0
- package/dist/proxmox/pbs-prune-job-form.d.ts +97 -0
- package/dist/proxmox/pbs-prune-job-form.d.ts.map +1 -0
- package/dist/proxmox/pbs-prune-job.d.ts +168 -0
- package/dist/proxmox/pbs-prune-job.d.ts.map +1 -0
- package/dist/proxmox/pbs-sync-job-form.d.ts +83 -0
- package/dist/proxmox/pbs-sync-job-form.d.ts.map +1 -0
- package/dist/proxmox/pbs-sync-job.d.ts +246 -0
- package/dist/proxmox/pbs-sync-job.d.ts.map +1 -0
- package/dist/proxmox/pbs-verify-job-form.d.ts +114 -0
- package/dist/proxmox/pbs-verify-job-form.d.ts.map +1 -0
- package/dist/proxmox/pbs-verify-job.d.ts +145 -0
- package/dist/proxmox/pbs-verify-job.d.ts.map +1 -0
- package/dist/proxmox/pool.d.ts +42 -0
- package/dist/proxmox/pool.d.ts.map +1 -0
- package/dist/proxmox/qemu.d.ts +46 -0
- package/dist/proxmox/qemu.d.ts.map +1 -0
- package/dist/proxmox/replication-job-form.d.ts +71 -0
- package/dist/proxmox/replication-job-form.d.ts.map +1 -0
- package/dist/proxmox/replication-job.d.ts +162 -0
- package/dist/proxmox/replication-job.d.ts.map +1 -0
- package/dist/proxmox/resource.d.ts +157 -0
- package/dist/proxmox/resource.d.ts.map +1 -0
- package/dist/proxmox/role.d.ts +97 -0
- package/dist/proxmox/role.d.ts.map +1 -0
- package/dist/proxmox/sdn-apply-read.d.ts +80 -0
- package/dist/proxmox/sdn-apply-read.d.ts.map +1 -0
- package/dist/proxmox/sdn-apply.d.ts +63 -0
- package/dist/proxmox/sdn-apply.d.ts.map +1 -0
- package/dist/proxmox/sdn-subnet-form.d.ts +111 -0
- package/dist/proxmox/sdn-subnet-form.d.ts.map +1 -0
- package/dist/proxmox/sdn-subnet.d.ts +161 -0
- package/dist/proxmox/sdn-subnet.d.ts.map +1 -0
- package/dist/proxmox/sdn-vnet.d.ts +113 -0
- package/dist/proxmox/sdn-vnet.d.ts.map +1 -0
- package/dist/proxmox/sdn-zone.d.ts +126 -0
- package/dist/proxmox/sdn-zone.d.ts.map +1 -0
- package/dist/proxmox/storage.d.ts +120 -0
- package/dist/proxmox/storage.d.ts.map +1 -0
- package/dist/proxmox/user.d.ts +107 -0
- package/dist/proxmox/user.d.ts.map +1 -0
- package/dist/proxmox/values.d.ts +134 -0
- package/dist/proxmox/values.d.ts.map +1 -0
- package/dist/proxmox/zfs-pool-write.d.ts +76 -0
- package/dist/proxmox/zfs-pool-write.d.ts.map +1 -0
- package/dist/proxmox/zfs-pool.d.ts +146 -0
- package/dist/proxmox/zfs-pool.d.ts.map +1 -0
- package/dist/talos/credentials.d.ts +30 -0
- package/dist/talos/credentials.d.ts.map +1 -0
- package/dist/talos/index.d.ts +15 -0
- package/dist/talos/index.d.ts.map +1 -0
- package/dist/talos/index.js +392 -0
- package/dist/talos/index.js.map +16 -0
- package/dist/talos/kubeconfig.d.ts +53 -0
- package/dist/talos/kubeconfig.d.ts.map +1 -0
- package/dist/talos/resource.d.ts +32 -0
- package/dist/talos/resource.d.ts.map +1 -0
- package/dist/talos/talos-bootstrap.d.ts +27 -0
- package/dist/talos/talos-bootstrap.d.ts.map +1 -0
- package/dist/talos/talos-cluster-health.d.ts +36 -0
- package/dist/talos/talos-cluster-health.d.ts.map +1 -0
- package/dist/talos/talos-machine-config.d.ts +41 -0
- package/dist/talos/talos-machine-config.d.ts.map +1 -0
- package/dist/talos/talosctl.d.ts +29 -0
- package/dist/talos/talosctl.d.ts.map +1 -0
- package/dist/talos/values.d.ts +20 -0
- package/dist/talos/values.d.ts.map +1 -0
- package/package.json +57 -0
- package/src/cloudflare/client.ts +76 -0
- package/src/cloudflare/index.ts +10 -0
- package/src/cloudflare/lock-rules.ts +60 -0
- package/src/cloudflare/providers.ts +23 -0
- package/src/cloudflare/r2-bucket-lock.ts +220 -0
- package/src/forgejo/branch-protection-form.ts +29 -0
- package/src/forgejo/branch-protection.ts +101 -0
- package/src/forgejo/client.ts +136 -0
- package/src/forgejo/index.ts +17 -0
- package/src/forgejo/org-actions-secrets.ts +99 -0
- package/src/forgejo/org-label.ts +100 -0
- package/src/forgejo/org-team.ts +105 -0
- package/src/forgejo/repo-webhook-form.ts +47 -0
- package/src/forgejo/repo-webhook.ts +118 -0
- package/src/forgejo/repository.ts +126 -0
- package/src/forgejo/resource.ts +171 -0
- package/src/forgejo/team-member.ts +97 -0
- package/src/forgejo/values.test.ts +48 -0
- package/src/forgejo/values.ts +61 -0
- package/src/index.ts +18 -0
- package/src/openbao/auth-role-form.ts +112 -0
- package/src/openbao/auth-role.ts +101 -0
- package/src/openbao/bao-address.test.ts +89 -0
- package/src/openbao/bao-address.ts +97 -0
- package/src/openbao/bao-http-gate.test.ts +88 -0
- package/src/openbao/bao-http-unix.test.ts +70 -0
- package/src/openbao/bao-http.test.ts +161 -0
- package/src/openbao/bao-http.ts +185 -0
- package/src/openbao/bao-status.test.ts +114 -0
- package/src/openbao/bao-status.ts +137 -0
- package/src/openbao/cloudflare-group-scope.test.ts +103 -0
- package/src/openbao/cloudflare-group-scope.ts +83 -0
- package/src/openbao/cloudflare-parity-catalog.ts +104 -0
- package/src/openbao/cloudflare-parity-snapshot.ts +84 -0
- package/src/openbao/cloudflare-parity.ts +152 -0
- package/src/openbao/cloudflare-permission-groups-solve.test.ts +171 -0
- package/src/openbao/cloudflare-permission-groups-solve.ts +197 -0
- package/src/openbao/cloudflare-permission-groups.test.ts +369 -0
- package/src/openbao/cloudflare-permission-groups.ts +299 -0
- package/src/openbao/cloudflare-policy.ts +180 -0
- package/src/openbao/cloudflare-role-form.test.ts +177 -0
- package/src/openbao/cloudflare-role-form.ts +183 -0
- package/src/openbao/cloudflare-role-wire.test.ts +132 -0
- package/src/openbao/cloudflare-role-wire.ts +41 -0
- package/src/openbao/cloudflare-role.ts +189 -0
- package/src/openbao/cloudflare-roles-config.ts +230 -0
- package/src/openbao/cloudflare-roles-expand-bucket.test.ts +134 -0
- package/src/openbao/cloudflare-roles-expand.ts +231 -0
- package/src/openbao/digest.ts +26 -0
- package/src/openbao/fake-bao.ts +84 -0
- package/src/openbao/forgejo-bootstrap-parse.test.ts +76 -0
- package/src/openbao/forgejo-bootstrap-parse.ts +90 -0
- package/src/openbao/forgejo-bootstrap.ts +181 -0
- package/src/openbao/index.ts +17 -0
- package/src/openbao/mount-form.ts +147 -0
- package/src/openbao/mount-wire.test.ts +108 -0
- package/src/openbao/mount-wire.ts +79 -0
- package/src/openbao/mount.ts +132 -0
- package/src/openbao/pki-role-form.ts +248 -0
- package/src/openbao/pki-role.ts +202 -0
- package/src/openbao/policy-assembly.test.ts +28 -0
- package/src/openbao/policy-assembly.ts +13 -0
- package/src/openbao/policy-wire.test.ts +90 -0
- package/src/openbao/policy-wire.ts +53 -0
- package/src/openbao/policy.ts +212 -0
- package/src/openbao/proxmox-role-form.ts +174 -0
- package/src/openbao/proxmox-role.ts +214 -0
- package/src/openbao/ssh-role-form.ts +248 -0
- package/src/openbao/ssh-role.ts +240 -0
- package/src/proxmox/acl.ts +267 -0
- package/src/proxmox/api-token-form.ts +122 -0
- package/src/proxmox/api-token.ts +222 -0
- package/src/proxmox/backup-job.ts +213 -0
- package/src/proxmox/ceph-daemon-form.ts +84 -0
- package/src/proxmox/ceph-daemon.ts +243 -0
- package/src/proxmox/ceph-flag.ts +249 -0
- package/src/proxmox/ceph-fs-wire.ts +246 -0
- package/src/proxmox/ceph-fs.ts +249 -0
- package/src/proxmox/ceph-osd-tree.ts +193 -0
- package/src/proxmox/ceph-osd-write.ts +76 -0
- package/src/proxmox/ceph-osd.ts +244 -0
- package/src/proxmox/ceph-pool-form.ts +249 -0
- package/src/proxmox/ceph-pool-settle.ts +70 -0
- package/src/proxmox/ceph-pool.ts +212 -0
- package/src/proxmox/client.test.ts +185 -0
- package/src/proxmox/client.ts +240 -0
- package/src/proxmox/credentials.test.ts +144 -0
- package/src/proxmox/credentials.ts +254 -0
- package/src/proxmox/firewall-alias-form.ts +112 -0
- package/src/proxmox/firewall-alias.ts +208 -0
- package/src/proxmox/generated/pbs.ts +461 -0
- package/src/proxmox/generated/pve.ts +7344 -0
- package/src/proxmox/group.ts +199 -0
- package/src/proxmox/ha-resource.ts +169 -0
- package/src/proxmox/ha-rule-form.ts +185 -0
- package/src/proxmox/ha-rule.ts +240 -0
- package/src/proxmox/index.ts +42 -0
- package/src/proxmox/lease-cache.test.ts +163 -0
- package/src/proxmox/lease-cache.ts +131 -0
- package/src/proxmox/lxc.ts +117 -0
- package/src/proxmox/members.test.ts +54 -0
- package/src/proxmox/members.ts +174 -0
- package/src/proxmox/metric-server-form.ts +128 -0
- package/src/proxmox/metric-server-otel.ts +99 -0
- package/src/proxmox/metric-server.ts +214 -0
- package/src/proxmox/network-apply-read.ts +228 -0
- package/src/proxmox/network-apply.ts +244 -0
- package/src/proxmox/node-network-form.ts +213 -0
- package/src/proxmox/node-network.ts +243 -0
- package/src/proxmox/notification-target-form.ts +104 -0
- package/src/proxmox/notification-target.ts +222 -0
- package/src/proxmox/pbs-datastore-form.ts +248 -0
- package/src/proxmox/pbs-datastore-guard.ts +105 -0
- package/src/proxmox/pbs-datastore.ts +271 -0
- package/src/proxmox/pbs-prune-job-form.ts +208 -0
- package/src/proxmox/pbs-prune-job.ts +227 -0
- package/src/proxmox/pbs-sync-job-form.ts +234 -0
- package/src/proxmox/pbs-sync-job.ts +258 -0
- package/src/proxmox/pbs-verify-job-form.ts +131 -0
- package/src/proxmox/pbs-verify-job.ts +227 -0
- package/src/proxmox/pool.test.ts +42 -0
- package/src/proxmox/pool.ts +80 -0
- package/src/proxmox/qemu.ts +89 -0
- package/src/proxmox/replication-job-form.ts +89 -0
- package/src/proxmox/replication-job.ts +248 -0
- package/src/proxmox/resource.ts +257 -0
- package/src/proxmox/role.ts +161 -0
- package/src/proxmox/sdn-apply-read.test.ts +152 -0
- package/src/proxmox/sdn-apply-read.ts +189 -0
- package/src/proxmox/sdn-apply.ts +154 -0
- package/src/proxmox/sdn-subnet-form.ts +156 -0
- package/src/proxmox/sdn-subnet.ts +238 -0
- package/src/proxmox/sdn-vnet.ts +228 -0
- package/src/proxmox/sdn-zone.ts +246 -0
- package/src/proxmox/storage.ts +245 -0
- package/src/proxmox/user.ts +214 -0
- package/src/proxmox/values.test.ts +193 -0
- package/src/proxmox/values.ts +203 -0
- package/src/proxmox/zfs-pool-write.ts +130 -0
- package/src/proxmox/zfs-pool.ts +253 -0
- package/src/talos/credentials.ts +136 -0
- package/src/talos/index.ts +14 -0
- package/src/talos/kubeconfig.ts +170 -0
- package/src/talos/resource.ts +31 -0
- package/src/talos/talos-bootstrap.ts +109 -0
- package/src/talos/talos-cluster-health.ts +119 -0
- package/src/talos/talos-machine-config.ts +146 -0
- package/src/talos/talosctl.ts +94 -0
- package/src/talos/values.test.ts +80 -0
- package/src/talos/values.ts +70 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a Ceph daemon lives on the API, and what a declaration of one looks like as a PVE form.
|
|
3
|
+
*
|
|
4
|
+
* ★ SPLIT OUT OF ceph-daemon.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, and the seam is a real
|
|
5
|
+
* one rather than a convenient line number — the same seam metric-server-form.ts cuts on.
|
|
6
|
+
* This file answers "how does a declaration address ONE daemon" — which URL, which body, and
|
|
7
|
+
* which row of the collection's answer is it. ceph-daemon.ts answers "what does the cluster
|
|
8
|
+
* say about that daemon, and when has it changed". Nothing here calls the API and nothing
|
|
9
|
+
* here decides a diff; `findRow` picks a row out of an answer its caller already fetched.
|
|
10
|
+
*
|
|
11
|
+
* ⛔ THE TWO PATHS ARE DIFFERENT AND THAT IS THE WHOLE REASON THIS FILE HAS TWO OF THEM. MEASURED
|
|
12
|
+
* on n2, 2026-09-13: there is no GET on the id path — `pvesh get /nodes/n2/ceph/mon/n2` answers
|
|
13
|
+
* "No 'get' handler defined" — while POST and DELETE are registered ONLY there. So the read uses
|
|
14
|
+
* `collectionPath` and the two writes use `daemonPath`, and swapping either one is a silent
|
|
15
|
+
* 501: a create that never lands, or a destroy that reports failure while the daemon runs on.
|
|
16
|
+
*
|
|
17
|
+
* ⚠️ THE `import type` BACK TO ceph-daemon.ts IS A CYCLE ON PAPER ONLY — type-only, so it is
|
|
18
|
+
* erased before anything runs. `CephDaemonProps` stays the resource's public shape, in the file
|
|
19
|
+
* that declares the resource.
|
|
20
|
+
*
|
|
21
|
+
* ⛔ POST AND DELETE RETURN A UPID, NOT AN OBJECT — both are `protected` task endpoints whose
|
|
22
|
+
* schema `returns` is a bare string. REASONED, NOT MEASURED: this cluster is read-only to me, so
|
|
23
|
+
* no create was ever run. The consequence to expect is that `reconcile` reads back immediately
|
|
24
|
+
* after the POST, and if the worker has not yet written ceph.conf the read-back still says
|
|
25
|
+
* absent and reconcile dies with "the write returned no error but the object is still absent".
|
|
26
|
+
* That refusal is honest — nothing is recorded for an object that is not there — and a second
|
|
27
|
+
* deploy converges once the task has finished, but it is a confusing FIRST-RUN failure. Watch
|
|
28
|
+
* the task in the PVE UI before concluding the create failed.
|
|
29
|
+
*
|
|
30
|
+
*/
|
|
31
|
+
import type { CephDaemonProps } from './ceph-daemon.ts';
|
|
32
|
+
/**
|
|
33
|
+
* PVE defaults the id to the nodename for all three kinds, and that default is resolved HERE
|
|
34
|
+
* rather than left to PVE, because the id is a PATH SEGMENT: create, delete and the row lookup in
|
|
35
|
+
* ceph-daemon.ts all need the same string, and a default resolved on the far side would leave
|
|
36
|
+
* three call sites guessing. Live on this cluster every daemon's name equals its node.
|
|
37
|
+
*/
|
|
38
|
+
export declare const daemonId: (props: CephDaemonProps) => string;
|
|
39
|
+
/**
|
|
40
|
+
* The read path. ⚠️ `{node}` HERE IS ONLY THE NODE BEING ASKED, not a filter. MEASURED:
|
|
41
|
+
* `GET /nodes/n2/ceph/mon` returns n2, n3 AND n4, and `GET /nodes/n3/ceph/mon` returns the same
|
|
42
|
+
* three IN A DIFFERENT ORDER — [n4,n2,n3] from n2 against [n3,n2,n4] from n3. A caller that took
|
|
43
|
+
* row zero, or assumed the list was this node's daemons, would be wrong on both counts.
|
|
44
|
+
*/
|
|
45
|
+
export declare const collectionPath: (props: CephDaemonProps) => string;
|
|
46
|
+
/** The write path: POST creates here, DELETE destroys here. Never read from — see the ⛔ above. */
|
|
47
|
+
export declare const daemonPath: (props: CephDaemonProps) => string;
|
|
48
|
+
/**
|
|
49
|
+
* The create body, which is EMPTY for a mgr and nearly empty for the other two.
|
|
50
|
+
*
|
|
51
|
+
* ⚠️ THE ID IS NOT SENT, because it is already the last segment of the path being POSTed to — the
|
|
52
|
+
* same reasoning as metric-server.ts, where a second copy of the key can only disagree with the
|
|
53
|
+
* first. PVE's schema does accept `monid`/`id`/`name` as body parameters; the path wins, so
|
|
54
|
+
* sending one would be a second source of truth for the object's identity and nothing else.
|
|
55
|
+
*
|
|
56
|
+
* ⚠️ AND THE BRANCHES CANNOT BE ONE SHARED BODY. `hotstandby` is an mds parameter and
|
|
57
|
+
* `mon-address` a mon one; each POST schema lists only its own, so the wrong field is a 400
|
|
58
|
+
* rather than an ignored hint. Both are create-only — the ⛔s on those props say why neither is
|
|
59
|
+
* ever compared afterwards.
|
|
60
|
+
*/
|
|
61
|
+
export declare const createForm: (props: CephDaemonProps) => Record<string, string>;
|
|
62
|
+
/**
|
|
63
|
+
* ⚠️ THE COLLECTION ANSWERS AN ARRAY while the factory types `attributes`' first parameter as the
|
|
64
|
+
* `Record<string, unknown>` every other PVE read is shaped like — the same mismatch acl.ts
|
|
65
|
+
* carries, handled the same way: narrow the rows, trust none of them, and match on `name`.
|
|
66
|
+
* ⚠️ MATCHED ON `name` ALONE, NEVER ON POSITION — the list comes back in a different order
|
|
67
|
+
* depending on which node was asked. See the ⚠️ on `collectionPath` above.
|
|
68
|
+
*/
|
|
69
|
+
export declare const findRow: (live: unknown, props: CephDaemonProps) => Record<string, unknown> | undefined;
|
|
70
|
+
//# sourceMappingURL=ceph-daemon-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ceph-daemon-form.d.ts","sourceRoot":"","sources":["../../src/proxmox/ceph-daemon-form.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGxD;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,UAAW,eAAe,WAA6B,CAAC;AAE7E;;;;;GAKG;AACH,eAAO,MAAM,cAAc,UAAW,eAAe,WAA6C,CAAC;AAEnG,kGAAkG;AAClG,eAAO,MAAM,UAAU,UAAW,eAAe,WAAkD,CAAC;AAEpG;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,UAAU,UAAW,eAAe,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAMxE,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,SAAU,OAAO,SAAS,eAAe,wCAGV,CAAC"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Proxmox.CephDaemon` — a Ceph monitor, manager or metadata server on one node.
|
|
3
|
+
*
|
|
4
|
+
* ★ ONE RESOURCE FOR THREE KINDS, AND THE SCHEMAS WERE CHECKED BEFORE THAT WAS DECIDED. Parsed
|
|
5
|
+
* from `/usr/share/pve-docs/api-viewer/apidoc.js` on n2, 2026-09-13: mon, mgr and mds each
|
|
6
|
+
* expose GET on the collection and POST + DELETE on the id below it, each returns
|
|
7
|
+
* name/host/state/addr/ceph_version*, and the only divergence is the create parameter
|
|
8
|
+
* (`mon-address` / none / `hotstandby`) and the read-only extras (mon: quorum, rank; mds: rank,
|
|
9
|
+
* fs_name, standby_replay). Three near-identical files would have carried the traps below three
|
|
10
|
+
* times over and let them drift — the argument resource.ts already makes for the factory itself.
|
|
11
|
+
*
|
|
12
|
+
* ⛔ THE ONLY GET IS THE COLLECTION'S, AND IT ANSWERS FOR THE WHOLE CLUSTER. MEASURED:
|
|
13
|
+
* `GET /nodes/n2/ceph/mon` returns n2, n3 AND n4, and `GET /nodes/n3/ceph/mon` returns the same
|
|
14
|
+
* three IN A DIFFERENT ORDER — [n4,n2,n3] from n2, [n3,n2,n4] from n3 — so a row is found by
|
|
15
|
+
* `name` and NEVER by position, and the `{node}` in the path is only the node being ASKED.
|
|
16
|
+
* MEASURED too: there is no GET on the id path at all; `pvesh get /nodes/n2/ceph/mon/n2` answers
|
|
17
|
+
* "No 'get' handler defined for '/nodes/n2/ceph/mon/n2'", and mgr and mds answer the same.
|
|
18
|
+
* That is why `path()` below is the COLLECTION, and that in turn is why `delete` is the one
|
|
19
|
+
* handler this file writes by hand: `pveOperations.destroy` sends its DELETE to `spec.path`,
|
|
20
|
+
* which here would be the collection — a 501 reported as a failed destroy, with the daemon still
|
|
21
|
+
* running. Same shape and same rule as the override in acl.ts: do not "restore symmetry" by
|
|
22
|
+
* pointing it back at `ops.destroy`.
|
|
23
|
+
*
|
|
24
|
+
* ⛔ WHAT A DESTROY ACTUALLY DOES, BECAUSE THE PLAN LINE SAYS ONLY "delete". REASONED FROM PVE'S
|
|
25
|
+
* AND CEPH'S DOCUMENTED BEHAVIOUR, NOT MEASURED — nothing on this cluster was destroyed to find
|
|
26
|
+
* out, and nothing should be to check. `mon`: stops ceph-mon@id, removes it from the monmap and
|
|
27
|
+
* strips its section from ceph.conf. THREE MONS TOLERATE ONE LOSS; TWO TOLERATE NONE. Removing
|
|
28
|
+
* two in one deploy — or removing the mon on the node whose API is serving the call — loses
|
|
29
|
+
* quorum, and a cluster without mon quorum BLOCKS every RBD and CephFS I/O rather than erroring
|
|
30
|
+
* it, so on this cluster every guest on `cephtb4` hangs.
|
|
31
|
+
* `mds`: destroying a STANDBY is a non-event; destroying the ACTIVE one fails CephFS over,
|
|
32
|
+
* and with no standby left `cephfs-tb4` goes unavailable. `mgr`: no guest I/O depends on it, but
|
|
33
|
+
* the last one takes the PG autoscaler, the dashboard and PVE's own Ceph status with it.
|
|
34
|
+
* Live on 2026-09-13: mon, mgr AND mds on each of n2, n3, n4.
|
|
35
|
+
*
|
|
36
|
+
* ★ AND THE ONLY WAY TO REACH THAT DESTROY IS TO DELETE THE DECLARATION. `matches` is deliberately
|
|
37
|
+
* total (see its ⛔), so this resource CANNOT plan a `replace` — which is the one action that
|
|
38
|
+
* would tear down a live mon off the back of an edited field rather than a removed line.
|
|
39
|
+
*
|
|
40
|
+
* ⚠️ THE PRIVILEGES ARE ASYMMETRIC AND THE READ SIDE IS THE CHEAP ONE. The GET checks `any` of
|
|
41
|
+
* `Sys.Audit`/`Datastore.Audit` on `/`, and PVEAuditor holds both (MEASURED:
|
|
42
|
+
* `GET /access/roles/PVEAuditor`), so the mount's `read` role can see this family — unlike
|
|
43
|
+
* `Proxmox.Storage`, which 403s under that lease. POST and DELETE both check `Sys.Modify` on
|
|
44
|
+
* `/`, and MEASURED on this cluster `LXCProvisioner` already holds it, so the provision lane
|
|
45
|
+
* needs no widening for once. ⚠️ `Sys.Modify` on the ROOT path is broad — it also buys
|
|
46
|
+
* datacenter options and every other cluster-wide config write — so it is worth knowing that
|
|
47
|
+
* this credential already has it rather than discovering it the next time something is scoped.
|
|
48
|
+
*/
|
|
49
|
+
import { Resource } from 'alchemy';
|
|
50
|
+
import * as Provider from 'alchemy/Provider';
|
|
51
|
+
import { type PveRequirements, type WithTarget } from './resource.ts';
|
|
52
|
+
/**
|
|
53
|
+
* Which Ceph daemon this is. The discriminant picks the collection, the create parameters and
|
|
54
|
+
* which of the read-only extras below will even be populated.
|
|
55
|
+
*
|
|
56
|
+
* ⚠️ `osd` IS NOT HERE AND MUST NOT BE ADDED TO THIS UNION. `/nodes/{node}/ceph/osd` takes a disk
|
|
57
|
+
* device, wipes it, and its DELETE has a `cleanup` flag — a create that destroys data on the way
|
|
58
|
+
* in is a different resource with a different set of refusals, not a fourth case in this one.
|
|
59
|
+
*/
|
|
60
|
+
export type CephDaemonKind = 'mon' | 'mgr' | 'mds';
|
|
61
|
+
export interface CephDaemonProps extends WithTarget {
|
|
62
|
+
kind: CephDaemonKind;
|
|
63
|
+
/**
|
|
64
|
+
* The node the daemon RUNS on. Identity — a daemon cannot be moved, only destroyed and rebuilt.
|
|
65
|
+
* ⚠️ It is also the node whose API is asked, and the read is only as available as that node: a
|
|
66
|
+
* node that is down — or has no Ceph installed, or answers 403 — reads as nothing at all,
|
|
67
|
+
* because `pveOperations.read` folds EVERY failure into "absent" (a 404 is a legitimate answer
|
|
68
|
+
* there and it cannot tell the two apart). Its daemons then plan as `update`, and reconcile
|
|
69
|
+
* POSTs a create for a mon that already exists. PVE refuses that with "monitor already
|
|
70
|
+
* exists", and the read-back refuses again — so it fails loudly rather than damaging anything,
|
|
71
|
+
* but the message will point at the daemon rather than at the node that would not answer.
|
|
72
|
+
*/
|
|
73
|
+
node: string;
|
|
74
|
+
/**
|
|
75
|
+
* The daemon id. PVE defaults it to the nodename, and `daemonId` in ceph-daemon-form.ts resolves
|
|
76
|
+
* that default HERE rather than leaving it to PVE, because the id is a PATH SEGMENT: create,
|
|
77
|
+
* delete and the row lookup all need the same string, and a default resolved on the far side
|
|
78
|
+
* would leave three call sites guessing. Live on this cluster every daemon's name is its node.
|
|
79
|
+
*/
|
|
80
|
+
name?: string;
|
|
81
|
+
/**
|
|
82
|
+
* mon only. Overrides the autodetected monitor IP; must sit in Ceph's public network — on this
|
|
83
|
+
* cluster that is 10.20.11.0/24, carried by `vmbr1.11`.
|
|
84
|
+
* ⛔ CREATE-ONLY AND NEVER COMPARED. PVE takes a bare ip-list on write and hands back `addr` as
|
|
85
|
+
* `10.20.11.12:6789/0` — an address, a port and a nonce. The two are not the same string and
|
|
86
|
+
* never will be, so diffing them would report a mismatch on every plan; with no PUT on this
|
|
87
|
+
* family that mismatch becomes a REPLACE, and a replace of a mon is the quorum loss in the
|
|
88
|
+
* header. Left out of `matches` on purpose.
|
|
89
|
+
*/
|
|
90
|
+
'mon-address'?: string;
|
|
91
|
+
/**
|
|
92
|
+
* mds only. Makes this standby replay the active MDS's journal for a faster failover.
|
|
93
|
+
* ⛔ CREATE-ONLY AND NEVER COMPARED, AND THE FIELD THAT LOOKS LIKE ITS READBACK IS NOT ONE.
|
|
94
|
+
* `standby_replay` in the list is the daemon's CURRENT state, not its configuration: MEASURED
|
|
95
|
+
* 2026-09-13, all three mds report `standby_replay: false`, n3 included — and n3 is the ACTIVE
|
|
96
|
+
* mds, which is not a standby at all and so can never report true however it was created.
|
|
97
|
+
* Comparing `hotstandby` to it would plan a replace against whichever mds Ceph happens to have
|
|
98
|
+
* elected, i.e. against a value no declaration controls.
|
|
99
|
+
* ⚠️ SET ON A mon OR mgr IT IS DROPPED RATHER THAN SENT, and `mon-address` likewise on an mds:
|
|
100
|
+
* each POST schema lists only its own parameter, so the wrong one is a 400 rather than an
|
|
101
|
+
* ignored hint. A field silently dropped is the lesser of the two, but it IS dropped.
|
|
102
|
+
*/
|
|
103
|
+
hotstandby?: boolean;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Everything the cluster says about this daemon — reported, and (bar its identity) none of it
|
|
107
|
+
* compared. Read the ⛔ on `matches` for why that is the only safe split on this family.
|
|
108
|
+
*/
|
|
109
|
+
export interface CephDaemonAttributes {
|
|
110
|
+
kind: CephDaemonKind;
|
|
111
|
+
node: string;
|
|
112
|
+
name: string;
|
|
113
|
+
/**
|
|
114
|
+
* The host Ceph says it runs on.
|
|
115
|
+
* ⚠️ REPORTED, NOT COMPARED, AND THAT IS A DELIBERATE BLIND SPOT. A daemon found under this name
|
|
116
|
+
* on a DIFFERENT node than `node` declares plans as `noop`, which does hide a misdeclaration.
|
|
117
|
+
* The alternative is worse: `host` is `optional` in the schema, so an absent one would read as
|
|
118
|
+
* a mismatch, and with no PUT a mismatch is a replace — i.e. a missing field in one API answer
|
|
119
|
+
* would destroy a live mon. When in doubt, out of `matches`; fix a wrong node by hand.
|
|
120
|
+
*/
|
|
121
|
+
host: string;
|
|
122
|
+
/**
|
|
123
|
+
* mon `running`/`stopped`/`unknown`, mgr `active`/`standby`, mds `up:active`/`up:standby`/…
|
|
124
|
+
* ⛔ CEPH ELECTS THIS AND REWRITES IT WITHOUT ANYONE DECLARING ANYTHING. MEASURED: mgr n2 is
|
|
125
|
+
* `active` while n3 and n4 are `standby`; mds n3 is `up:active` while n2 and n4 are
|
|
126
|
+
* `up:standby`. Restart a daemon and the roles move. It is reported so a plan can show what is
|
|
127
|
+
* live and kept out of `matches` so that an election is never a diff.
|
|
128
|
+
* ⚠️ IT ALSO MEANS THIS RESOURCE DOES NOT MANAGE WHETHER THE DAEMON IS RUNNING. A configured but
|
|
129
|
+
* stopped daemon is still a row in the list, so it reads as present and plans `noop`.
|
|
130
|
+
*/
|
|
131
|
+
state: string;
|
|
132
|
+
/** Ceph-formatted, e.g. `10.20.11.12:6789/0`. Reported; see `mon-address` for why never compared. */
|
|
133
|
+
addr: string;
|
|
134
|
+
/** `ceph_version_short`, e.g. `20.2.2`. Reported: a straggler after an upgrade is worth seeing. */
|
|
135
|
+
version: string;
|
|
136
|
+
/**
|
|
137
|
+
* mon rank in the monmap, mds rank in the filesystem, `-1` when the daemon has neither.
|
|
138
|
+
* ⛔ CEPH ASSIGNS IT AND NOTHING DECLARES IT. MEASURED: the mons are ranked n4=0, n3=1, n2=2 —
|
|
139
|
+
* monmap order, not declaration order — and the mds ranks are 0 for the active n3 and -1 for
|
|
140
|
+
* the two standbys. Rank moves when a daemon is added, removed or restarted.
|
|
141
|
+
*/
|
|
142
|
+
rank: number;
|
|
143
|
+
/** mon only: in the current quorum. Reported. MEASURED: all three mons are in quorum. */
|
|
144
|
+
quorum: boolean;
|
|
145
|
+
/** mds only, and NOT the readback of `hotstandby` — see the ⛔ on that prop. */
|
|
146
|
+
standbyReplay: boolean;
|
|
147
|
+
/**
|
|
148
|
+
* mds only: the CephFS this daemon currently serves — `cephfs-tb4` here.
|
|
149
|
+
* ⛔ PRESENT ONLY ON THE ACTIVE MDS. MEASURED: n3 carries `fs_name`, n2 and n4 have no such key
|
|
150
|
+
* at all. So it is empty for two of three identical declarations, and comparing it would
|
|
151
|
+
* report work on exactly the daemons that are healthy standbys.
|
|
152
|
+
*/
|
|
153
|
+
fsName: string;
|
|
154
|
+
/** A `ceph-<kind>@<id>` systemd unit is enabled on the host. Reported; absent reads as false. */
|
|
155
|
+
service: boolean;
|
|
156
|
+
}
|
|
157
|
+
export interface ProxmoxCephDaemon extends Resource<'Proxmox.CephDaemon', CephDaemonProps, CephDaemonAttributes, never, PveRequirements> {
|
|
158
|
+
}
|
|
159
|
+
/** ★ `retain` by default — a mon carrying quorum cannot be rebuilt. See the ★ in resource.ts. */
|
|
160
|
+
export declare const ProxmoxCephDaemon: import("alchemy").ResourceClass<ProxmoxCephDaemon>;
|
|
161
|
+
export declare const ProxmoxCephDaemonProvider: () => import("effect/Layer").Layer<Provider.Provider<ProxmoxCephDaemon>, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
162
|
+
//# sourceMappingURL=ceph-daemon.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ceph-daemon.d.ts","sourceRoot":"","sources":["../../src/proxmox/ceph-daemon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAI7C,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAe,MAAM,eAAe,CAAC;AAGnF;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;AAEnD,MAAM,WAAW,eAAgB,SAAQ,UAAU;IACjD,IAAI,EAAE,cAAc,CAAC;IACrB;;;;;;;;;OASG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;OAOG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;OAQG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,qGAAqG;IACrG,IAAI,EAAE,MAAM,CAAC;IACb,mGAAmG;IACnG,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,MAAM,EAAE,OAAO,CAAC;IAChB,+EAA+E;IAC/E,aAAa,EAAE,OAAO,CAAC;IACvB;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,iGAAiG;IACjG,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,iBAAkB,SAAQ,QAAQ,CACjD,oBAAoB,EACpB,eAAe,EACf,oBAAoB,EACpB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,iGAAiG;AACjG,eAAO,MAAM,iBAAiB,oDAE5B,CAAC;AA+CH,eAAO,MAAM,yBAAyB,uIAoBnC,CAAC"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Proxmox.CephFlag` — one cluster-wide Ceph OSD flag, declared: `noout`, `pause`, `norebalance`.
|
|
3
|
+
*
|
|
4
|
+
* ★ ONE RESOURCE PER FLAG, NOT ONE CARRYING ALL ELEVEN, AND THE CLUSTER'S OWN API DECIDED IT.
|
|
5
|
+
* MEASURED on TB4 2026-09-13 (pve-manager 9.2.11, ceph tentacle 20.2.2) by reading
|
|
6
|
+
* `/usr/share/perl5/PVE/API2/Cluster/Ceph.pm` and the published schema in
|
|
7
|
+
* `/usr/share/pve-docs/api-viewer/apidoc.js`:
|
|
8
|
+
* · `PUT /cluster/ceph/flags` ends in `fork_worker('cephsetflags', ...)` and returns a STRING
|
|
9
|
+
* — a UPID. The bulk write is ASYNCHRONOUS, and this package has no task-polling machinery.
|
|
10
|
+
* · `PUT /cluster/ceph/flags/{flag}` calls `$rados->mon_command` inline and returns null. The
|
|
11
|
+
* per-flag write is SYNCHRONOUS.
|
|
12
|
+
* `pveOperations.reconcile` READS BACK after every write and refuses to record a value it did
|
|
13
|
+
* not see. Against the bulk endpoint that read-back races a worker which has not run yet, so it
|
|
14
|
+
* would record the OLD value as the new attributes — the one honesty mechanism in this package
|
|
15
|
+
* turned into a lie generator. Per-flag, the read-back means what it says.
|
|
16
|
+
* ★ THE SECOND REASON IS SMALLER AND STILL REAL. The bulk GET answers rows of
|
|
17
|
+
* `{name, description, value}`, and `description` is PVE's own English prose that no write
|
|
18
|
+
* accepts: compare it and you have a forever-update, report it and this provider is pretending
|
|
19
|
+
* to manage prose. `GET /cluster/ceph/flags/{flag}` answers a BARE BOOLEAN, so that trap
|
|
20
|
+
* cannot be written here at all. The brief's plural `Proxmox.CephFlags` became singular for
|
|
21
|
+
* those two reasons; the bulk endpoint is read by nothing in this package.
|
|
22
|
+
*
|
|
23
|
+
* ⛔ THESE ARE NOT CONFIGURATION. THEY ARE A HAND ON THE BRAKE, AND A DECLARATION CAN PULL IT OFF.
|
|
24
|
+
* `noout` is what a person sets before pulling a disk. A stack saying `value: false` reasserts
|
|
25
|
+
* that off on EVERY deploy — including a deploy somebody else runs, for an unrelated resource,
|
|
26
|
+
* twenty minutes into a maintenance window — and the plan line reads `Proxmox.CephFlag noout
|
|
27
|
+
* update`, which nobody reads as "ceph is about to start rebalancing 128 PGs across three nodes
|
|
28
|
+
* while a disk is out of the chassis".
|
|
29
|
+
* ★ SO `value` IS REQUIRED AND HAS NO DEFAULT, AND THAT IS THE MITIGATION. An optional `value`
|
|
30
|
+
* defaulting to false would make `CephFlag('noout', { flag: 'noout', target })` mean "clear
|
|
31
|
+
* it" — the dangerous direction, chosen by OMISSION. Here the dangerous sentence has to be
|
|
32
|
+
* typed out by a person. Do not add a default, and do not declare a flag merely to document
|
|
33
|
+
* that it is off: MEASURED 2026-09-13, all eleven flags on TB4 read 0, so eleven
|
|
34
|
+
* `value: false` lines would plan green forever and do nothing except take the brake off
|
|
35
|
+
* whenever somebody happens to apply them.
|
|
36
|
+
*
|
|
37
|
+
* ⛔ `delete` DOES NOTHING, ON PURPOSE, AND THE ASYMMETRY OF THE TWO FAILURES IS THE ARGUMENT.
|
|
38
|
+
* `flags/{flag}` has exactly two methods, GET and PUT, so the factory's `destroy` would answer
|
|
39
|
+
* "Method 'DELETE /cluster/ceph/flags/noout' not implemented" on every teardown — whatever this
|
|
40
|
+
* handler does is INVENTED. The obvious invention, "clear the flag", is refused: removing a line
|
|
41
|
+
* from a stack file, or Alchemy collecting an old generation after a replace, would silently
|
|
42
|
+
* take the brake off and nothing would warn. Leaving a flag set that nothing declares any more
|
|
43
|
+
* is the opposite kind of failure — ceph reports `HEALTH_WARN ... flag(s) set` for every one of
|
|
44
|
+
* these, on `ceph -s`, in the PVE UI and in the `pve_*` metrics this estate already scrapes.
|
|
45
|
+
* A silent destructive failure loses to a loud inert one.
|
|
46
|
+
* ⚠️ THE RESIDUAL RISK, NAMED RATHER THAN DENIED: deleting a `value: true` declaration LEAVES
|
|
47
|
+
* THE FLAG SET. Retire a maintenance window by flipping the line to `value: false` and
|
|
48
|
+
* deploying BEFORE the line is removed, or a `noout` outlives the window that needed it.
|
|
49
|
+
* ⚠️ REASONED, NOT MEASURED: that health warning was not observed, because observing it means
|
|
50
|
+
* setting a flag on a live cluster and this file was researched read-only. Undeclaring stops
|
|
51
|
+
* MANAGING a flag; to clear one, declare `value: false` and deploy, where a plan says so.
|
|
52
|
+
*
|
|
53
|
+
* ⛔ `pause` IS TWO CEPH FLAGS AND THIS RESOURCE READS ONLY ONE OF THEM. MEASURED in
|
|
54
|
+
* `PVE::Ceph::Tools::get_real_flag_name`, whose own comment reads "the 'pause' flag gets always
|
|
55
|
+
* set to both 'pauserd' and 'pausewr'": PVE writes both and then decides the flag is set by
|
|
56
|
+
* looking at `pauserd` alone. A cluster where somebody ran `ceph osd unset pauserd` by hand
|
|
57
|
+
* still has `pausewr` — writes blocked, guests hung — and this resource reports `value: false`
|
|
58
|
+
* and plans `noop` straight past it. It cannot express a half-pause, so never use it to prove
|
|
59
|
+
* one is gone; `ceph osd dump | head -1` can.
|
|
60
|
+
* ⛔ `pause` AND `noup` ARE ALSO THE TWO THAT TAKE THE CLUSTER DOWN FROM A TYPO. `pause: true`
|
|
61
|
+
* stops all reads and writes, so every guest on `cephtb4` — the cluster's only rbd pool,
|
|
62
|
+
* MEASURED 2026-09-13 — freezes on its root disk, and so does `cephfs-tb4`. `noup: true`
|
|
63
|
+
* stops a rebooted OSD from ever rejoining. Neither is undone by deleting the line.
|
|
64
|
+
*
|
|
65
|
+
* ⚠️ PRIVILEGES, AND THE WRITE ONE IS A BIG ASK. Both GETs check `Sys.Audit` on `/`; both PUTs
|
|
66
|
+
* check `Sys.Modify` on `/`, and all four carry `allowtoken => 1`, so an API token may make the
|
|
67
|
+
* call at all. There is no `/ceph` ACL object to scope to — the check names the ROOT path
|
|
68
|
+
* literally — so granting the provision role `Sys.Modify` here also buys datacenter options,
|
|
69
|
+
* metric servers, notification targets and every other cluster-wide write, exactly as
|
|
70
|
+
* metric-server.ts warns about its own family.
|
|
71
|
+
* ⚠️ REASONED, NOT MEASURED: nothing here was exercised under a minted lease, because a mint
|
|
72
|
+
* writes an API token into the cluster and the brief for this file was reads only. Expect
|
|
73
|
+
* "Permission check failed (/, Sys.Modify)" on the first deploy, and widen deliberately.
|
|
74
|
+
*/
|
|
75
|
+
import { Resource } from 'alchemy';
|
|
76
|
+
import * as Provider from 'alchemy/Provider';
|
|
77
|
+
import { type PveRequirements, type WithTarget } from './resource.ts';
|
|
78
|
+
/**
|
|
79
|
+
* PVE's eleven flags, spelled as `PVE::Ceph::Tools::get_possible_osd_flags` spells them.
|
|
80
|
+
*
|
|
81
|
+
* ⚠️ THE ENUM IS CLOSED AND A TYPO IS A 400, WHICH IS WHY THIS IS A UNION AND NOT `string`. Both
|
|
82
|
+
* the GET and the PUT declare `additionalProperties => 0` over exactly this list.
|
|
83
|
+
* ⚠️ CEPH HAS FLAGS PVE DOES NOT MODEL, and four of them are always on. MEASURED on TB4:
|
|
84
|
+
* `ceph osd dump` reports `flags sortbitwise,recovery_deletes,purged_snapdirs,pglog_hardlimit`.
|
|
85
|
+
* PVE reads that same string and answers only about its own eleven, so the others can neither
|
|
86
|
+
* leak in here nor be set from here — `noautoscale` and `nosnaptrim` included.
|
|
87
|
+
* ⛔ IF SOMEBODY LATER "IMPROVES" THE READ BY PARSING `osd dump` DIRECTLY, those four become
|
|
88
|
+
* permanently-set flags that nothing declares: a forever-diff on a brand new cluster.
|
|
89
|
+
*/
|
|
90
|
+
export type CephFlagName = 'nobackfill' | 'nodeep-scrub' | 'nodown' | 'noin' | 'noout' | 'norebalance' | 'norecover' | 'noscrub' | 'notieragent' | 'noup' | 'pause';
|
|
91
|
+
export interface CephFlagProps extends WithTarget {
|
|
92
|
+
/**
|
|
93
|
+
* Which flag. PVE's primary key here, and the last segment of the path.
|
|
94
|
+
*
|
|
95
|
+
* ⚠️ EDITING IT IN PLACE ORPHANS THE OLD FLAG RATHER THAN MOVING ANYTHING. `diff` reads the NEW
|
|
96
|
+
* path and reconcile writes it, while the old flag keeps whatever this stack last put there.
|
|
97
|
+
* There is deliberately no replace override for it — acl.ts needs one because its `delete`
|
|
98
|
+
* removes a real grant, and `delete` here is inert by design, so a replace would do exactly
|
|
99
|
+
* what an update already does. Declare a second resource and set the old one to `value: false`
|
|
100
|
+
* rather than renaming this one.
|
|
101
|
+
* ⛔ TWO RESOURCES DECLARING THE SAME FLAG ARE ONE CLUSTER OBJECT, and Alchemy sees two ids
|
|
102
|
+
* rather than a collision — the acl.ts hazard exactly. Disagreeing, they take turns winning
|
|
103
|
+
* and BOTH plan `update` for ever; agreeing, deleting either leaves the flag where the other
|
|
104
|
+
* put it. One declaration per flag per cluster.
|
|
105
|
+
*/
|
|
106
|
+
flag: CephFlagName;
|
|
107
|
+
/**
|
|
108
|
+
* Set the flag (`true`) or clear it (`false`).
|
|
109
|
+
*
|
|
110
|
+
* ⛔ REQUIRED, WITH NO DEFAULT — see the second ⛔ in the header, which is the whole safety
|
|
111
|
+
* argument for this family. It is also the only field PVE accepts on this endpoint, and it is
|
|
112
|
+
* not optional in the schema either: omitting it from the form is a 400, not an untouched
|
|
113
|
+
* flag. (The BULK endpoint is the one where omission means "leave it alone"; this is not it.)
|
|
114
|
+
*/
|
|
115
|
+
value: boolean;
|
|
116
|
+
}
|
|
117
|
+
export interface CephFlagAttributes {
|
|
118
|
+
/**
|
|
119
|
+
* ⚠️ REPORTED, NEVER COMPARED. It is the path key the read was made WITH, copied back out of
|
|
120
|
+
* props, so comparing it against props would be true by construction — the same reasoning
|
|
121
|
+
* acl.ts gives for the four identity fields it also declines to diff.
|
|
122
|
+
*/
|
|
123
|
+
flag: CephFlagName;
|
|
124
|
+
/** Whether ceph has the flag set right now. The only field `matches` looks at. */
|
|
125
|
+
value: boolean;
|
|
126
|
+
}
|
|
127
|
+
export interface ProxmoxCephFlag extends Resource<'Proxmox.CephFlag', CephFlagProps, CephFlagAttributes, never, PveRequirements> {
|
|
128
|
+
}
|
|
129
|
+
export declare const ProxmoxCephFlag: import("alchemy").ResourceClass<ProxmoxCephFlag>;
|
|
130
|
+
export declare const ProxmoxCephFlagProvider: () => import("effect/Layer").Layer<Provider.Provider<ProxmoxCephFlag>, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
131
|
+
//# sourceMappingURL=ceph-flag.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ceph-flag.d.ts","sourceRoot":"","sources":["../../src/proxmox/ceph-flag.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyEG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAe,MAAM,eAAe,CAAC;AAGnF;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,YAAY,GACpB,YAAY,GACZ,cAAc,GACd,QAAQ,GACR,MAAM,GACN,OAAO,GACP,aAAa,GACb,WAAW,GACX,SAAS,GACT,aAAa,GACb,MAAM,GACN,OAAO,CAAC;AAEZ,MAAM,WAAW,aAAc,SAAQ,UAAU;IAC/C;;;;;;;;;;;;;OAaG;IACH,IAAI,EAAE,YAAY,CAAC;IACnB;;;;;;;OAOG;IACH,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,IAAI,EAAE,YAAY,CAAC;IACnB,kFAAkF;IAClF,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,eAAgB,SAAQ,QAAQ,CAC/C,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,eAAO,MAAM,eAAe,kDAAgD,CAAC;AAqD7E,eAAO,MAAM,uBAAuB,qIA4CjC,CAAC"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The wire lane of `Proxmox.CephFs`: how a PVE answer becomes attributes, how a declaration becomes
|
|
3
|
+
* a PVE call, and how that call becomes a fact.
|
|
4
|
+
*
|
|
5
|
+
* ★ SPLIT OUT OF ceph-fs.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, and the seam is a real one
|
|
6
|
+
* rather than a convenient line number. ceph-fs.ts answers "what is a CephFS, and when has it
|
|
7
|
+
* changed"; this file answers "what does PVE say, what does it want, and has it finished". On
|
|
8
|
+
* this family that is where all the strangeness lives — the read is an index and not an object,
|
|
9
|
+
* the paths are inverted, the delete takes its arguments in the query string, and both writes
|
|
10
|
+
* fork a worker — so keeping it in one place is what stops the next reader assuming the ordinary
|
|
11
|
+
* shape from the other nineteen resources. ⚠️ NOTHING HERE DECIDES A DIFF.
|
|
12
|
+
*
|
|
13
|
+
* ⚠️ THE `import type` BACK TO ceph-fs.ts IS A CYCLE ON PAPER ONLY. It is type-only and erased
|
|
14
|
+
* before anything runs, exactly as metric-server-form.ts imports `MetricServerProps`. The props
|
|
15
|
+
* stay the resource's public shape, declared in the file that declares the resource.
|
|
16
|
+
*
|
|
17
|
+
* ⛔ POST AND DELETE ANSWER WITH A UPID, NOT A RESULT — THE WRITE IS ASYNCHRONOUS AND A FAILED ONE
|
|
18
|
+
* IS STILL HTTP 200. Read from the cluster's own source, /usr/share/perl5/PVE/API2/Ceph/FS.pm on
|
|
19
|
+
* n2: `createfs` and `destroyfs` both end `return $rpcenv->fork_worker(...)`. Two different lies
|
|
20
|
+
* come out of trusting that status code. A read-back straight after the POST reports "still
|
|
21
|
+
* absent" about a filesystem that is being built as it says so — which is precisely the message
|
|
22
|
+
* `pveOperations.reconcile` would print, pointing at the wrong cause. And a worker that dies
|
|
23
|
+
* INSIDE — the pool create, or the add-storage step, both of which are wrapped in `eval` — is
|
|
24
|
+
* never reported to the caller at all. `settle` below is the answer to both.
|
|
25
|
+
* ⚠️ REASONED FROM THAT SOURCE, NOT MEASURED. Nothing was written to this cluster, so the race
|
|
26
|
+
* has not been observed; only the `fork_worker` that guarantees it is there.
|
|
27
|
+
*
|
|
28
|
+
* ⛔ A DELETE BODY IS SILENTLY DISCARDED BY PVE, SO `remove-pools` GOES IN THE QUERY STRING.
|
|
29
|
+
* MEASURED in /usr/share/perl5/PVE/APIServer/AnyEvent.pm on n2: line 928 reads the request
|
|
30
|
+
* content into params only `if ($method eq 'PUT' || $method eq 'POST')`, and line 1653 sends
|
|
31
|
+
* every other method down a branch that parses `$request->url->query()` and nothing else. So
|
|
32
|
+
* `pve(target, role, 'DELETE', path, form)` would send a body PVE never looks at: the flags would
|
|
33
|
+
* read as unset, the pools would quietly survive a delete that asked for them, and no error would
|
|
34
|
+
* be raised anywhere. sdn-apply.ts already puts `?pending=1` in the path for the same reason.
|
|
35
|
+
*
|
|
36
|
+
* ⚠️ THE TASK POLL RUNS ON THE `read` ROLE THOUGH IT SITS INSIDE reconcile, FOR TWO REASONS. It is
|
|
37
|
+
* a read, and resource.ts already mints `read` for its own read-back inside reconcile. And the
|
|
38
|
+
* leases differ where it matters: `provision` is 300s and explicitly non-renewable, so a create
|
|
39
|
+
* that waits out a slow worker on that lease can expire mid-poll, while `read` is 3600s.
|
|
40
|
+
*
|
|
41
|
+
* ⛔ THE POLLER IS NEVER THE TASK'S OWNER, WHICH COSTS A PRIVILEGE, AND THIS ONE IS MEASURED
|
|
42
|
+
* RATHER THAN FEARED. client.ts mints a fresh credential per call and the mount vends a NEW
|
|
43
|
+
* token id each time, so the token reading the status is never the token recorded in the UPID —
|
|
44
|
+
* and PVE compares them exactly. /usr/share/perl5/PVE/API2/Tasks.pm on n2, `$check_task_user`:
|
|
45
|
+
* `return $user eq $fulltoken || $user eq $task->{user};`, above it the comment "token only sees
|
|
46
|
+
* token tasks, user sees user + token tasks". Token B of the same user matches neither branch.
|
|
47
|
+
* So the fallback is the schema's other clause for `GET /nodes/{node}/tasks/{upid}/status`: "The
|
|
48
|
+
* user needs 'Sys.Audit' permissions on '/nodes/<node>' if they are not the owner of the task."
|
|
49
|
+
* Grant it on the `read` role, or every create burns the full cap below and reports the wrong
|
|
50
|
+
* cause.
|
|
51
|
+
*/
|
|
52
|
+
import * as Effect from 'effect/Effect';
|
|
53
|
+
import type { CephFsAttributes, CephFsProps } from './ceph-fs.ts';
|
|
54
|
+
/**
|
|
55
|
+
* This filesystem's row of the index, as attributes.
|
|
56
|
+
*
|
|
57
|
+
* ⚠️ `GET /nodes/{node}/ceph/fs` ANSWERS AN ARRAY, while the factory hands `attributes` the
|
|
58
|
+
* `Record<string, unknown>` every other PVE read is shaped like. Rows are narrowed rather than
|
|
59
|
+
* trusted, the same way acl.ts narrows its one flat grant list.
|
|
60
|
+
*
|
|
61
|
+
* ⛔ RETURNING undefined IS WHAT MAKES "ABSENT" MEAN ABSENT HERE, unlike acl.ts where the object is
|
|
62
|
+
* always present-but-unbound. A filesystem missing from the index has not been created, and
|
|
63
|
+
* `reconcile` takes the create branch on exactly that signal.
|
|
64
|
+
*/
|
|
65
|
+
export declare const readRow: (live: Record<string, unknown>, props: CephFsProps) => CephFsAttributes | undefined;
|
|
66
|
+
/** ⚠️ `nodes/{n}/ceph/fs/{name}` — the POST and DELETE target, and NOT where a read goes. */
|
|
67
|
+
export declare const objectPath: (props: CephFsProps) => string;
|
|
68
|
+
/**
|
|
69
|
+
* ⚠️ `name` IS DELIBERATELY ABSENT FROM THE FORM. It is already the last segment of the path being
|
|
70
|
+
* POSTed to — the metric-server.ts case again — and a second copy can only ever disagree with it.
|
|
71
|
+
*/
|
|
72
|
+
export declare const createForm: (props: CephFsProps) => Record<string, string>;
|
|
73
|
+
/**
|
|
74
|
+
* POST the create, then wait for the worker. ⛔ The caller still reads back; see ceph-fs.ts.
|
|
75
|
+
*
|
|
76
|
+
* ⚠️ A CREATE NEEDS A RUNNING *AND* A STANDBY MDS, and refuses BEFORE it forks -- "no running
|
|
77
|
+
* Metadata Server (MDS) found!" / "no standby Metadata Server (MDS) found!" are synchronous, so
|
|
78
|
+
* they surface as a failed POST rather than as a silent worker. TB4 has three (MEASURED: n3
|
|
79
|
+
* `up:active` for cephfs-tb4, n2 and n4 `up:standby`), so this is a note for a smaller cluster.
|
|
80
|
+
* ⚠️ BOTH WRITES CHECK `Sys.Modify` ON `/` -- the root, with the breadth metric-server.ts warns
|
|
81
|
+
* about: granting it buys datacenter options and every other cluster-wide config write too.
|
|
82
|
+
*/
|
|
83
|
+
export declare const createFs: (props: CephFsProps) => Effect.Effect<undefined, Error, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
84
|
+
/**
|
|
85
|
+
* DELETE the filesystem, then wait for the worker.
|
|
86
|
+
*
|
|
87
|
+
* ⛔ WHAT THIS TAKES AWAY DEPENDS ENTIRELY ON `remove-pools`, AND THE DEFAULT IS THE SAFE ONE. With
|
|
88
|
+
* it unset PVE removes the filesystem from the MDS map and LEAVES the two pools on disk: every
|
|
89
|
+
* mount breaks, but the bytes are still there for an operator to recover. With it set the pools
|
|
90
|
+
* go too and nothing is recoverable. Leaving it unset also means a later create of the SAME name
|
|
91
|
+
* fails with "ceph pools '<name>_data' and/or '<name>_metadata' already exist" -- that refusal is
|
|
92
|
+
* a BRAKE, not a bug, and it is the last thing standing between a mis-typed rename and the data.
|
|
93
|
+
*/
|
|
94
|
+
export declare const destroyFs: (props: CephFsProps) => Effect.Effect<void, Error, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
95
|
+
/**
|
|
96
|
+
* The two read-back failures, as messages rather than as guesses.
|
|
97
|
+
*
|
|
98
|
+
* ⛔ WAITING PROVES THE TASK ENDED; ONLY A READ PROVES THE FILESYSTEM DID OR DID NOT. `createfs`
|
|
99
|
+
* wraps its pool creates in an `eval` and CLEANS THEM UP on error before dying, so a worker can
|
|
100
|
+
* plausibly end having built and then removed everything it made. `destroyfs` refuses outright
|
|
101
|
+
* while a non-disabled `cephfs` storage still references the filesystem. Neither outcome is
|
|
102
|
+
* visible in a status code, which is why ceph-fs.ts reads back after both and dies with these.
|
|
103
|
+
*/
|
|
104
|
+
export declare const notCreated: (props: CephFsProps) => Error;
|
|
105
|
+
export declare const notDestroyed: (props: CephFsProps) => Error;
|
|
106
|
+
//# sourceMappingURL=ceph-fs-wire.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ceph-fs-wire.d.ts","sourceRoot":"","sources":["../../src/proxmox/ceph-fs-wire.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAKlE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,OAAO,SACZ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,SACtB,WAAW,KACjB,gBAAgB,GAAG,SAkBrB,CAAC;AAaF,6FAA6F;AAC7F,eAAO,MAAM,UAAU,UAAW,WAAW,WAAgD,CAAC;AAM9F;;;GAGG;AACH,eAAO,MAAM,UAAU,UAAW,WAAW,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAGnE,CAAC;AA2DH;;;;;;;;;GASG;AACH,eAAO,MAAM,QAAQ,UAAW,WAAW,0FAiBvC,CAAC;AAEL;;;;;;;;;GASG;AACH,eAAO,MAAM,SAAS,UAAW,WAAW,qFAQxC,CAAC;AAEL;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,UAAW,WAAW,UAK1C,CAAC;AAEJ,eAAO,MAAM,YAAY,UAAW,WAAW,UAK5C,CAAC"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Proxmox.CephFs` — a CephFS on the cluster's own Ceph. The filesystem a `cephfs` storage mounts.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ A REPLACE OF THIS RESOURCE DESTROYS A LIVE FILESYSTEM, AND THERE IS NO PUT TO SAVE YOU FROM
|
|
5
|
+
* IT. MEASURED from the published schema on n2, 2026-09-13: `/nodes/{node}/ceph/fs/{name}` has
|
|
6
|
+
* exactly two methods, POST and DELETE. A CephFS cannot be edited in place, so `updateForm` is
|
|
7
|
+
* left undefined and anything the factory can see as changed becomes a REPLACE — and Alchemy's
|
|
8
|
+
* replace is create-then-delete, where the delete is `destroyfs`: the entry leaves the MDS map,
|
|
9
|
+
* every mount on every node breaks, and whether the bytes survive rests on `remove-pools` alone.
|
|
10
|
+
* ★ WHICH IS THE WHOLE REASON `matches` COMPARES NOTHING. Read its ⛔ before adding a field to
|
|
11
|
+
* it. On every other resource in this package a forever-diff is an annoying plan line; on this
|
|
12
|
+
* one it is a live filesystem destroyed on every deploy, forever, because a background daemon
|
|
13
|
+
* moved a number nobody declared.
|
|
14
|
+
*
|
|
15
|
+
* ⛔ THERE IS NO SINGLE-OBJECT READ, WHICH INVERTS `path` AND `collection`. MEASURED on n2:
|
|
16
|
+
* pvesh get /nodes/n2/ceph/fs/cephfs-tb4
|
|
17
|
+
* -> No 'get' handler defined for '/nodes/n2/ceph/fs/cephfs-tb4'
|
|
18
|
+
* `GET /nodes/{node}/ceph/fs` is a directory index and is the only read there is, so `path()`
|
|
19
|
+
* below returns the INDEX and `attributes` picks this filesystem out of the array client-side —
|
|
20
|
+
* the shape acl.ts uses for the one flat `GET /access/acl`. The POST goes to the `{name}` path,
|
|
21
|
+
* so `collection()` is the LONGER of the two strings, the mirror of metric-server.ts.
|
|
22
|
+
* ⚠️ AND THAT IS WHY `delete` AND `reconcile` ARE HAND-WRITTEN BELOW rather than taken from
|
|
23
|
+
* `pveHandlers`. `pveOperations.destroy` deletes `spec.path`, which here is the index:
|
|
24
|
+
* `DELETE /nodes/{node}/ceph/fs` is not a route, so a factory delete would fail every destroy
|
|
25
|
+
* while the filesystem stayed exactly where it was. `reconcile` is hand-written for a second,
|
|
26
|
+
* independent reason — the forked worker — set out in ceph-fs-wire.ts. acl.ts is the
|
|
27
|
+
* precedent for a family that genuinely does not fit; this is the second.
|
|
28
|
+
*
|
|
29
|
+
* ⛔ CREATING A CephFS CREATES TWO CEPH POOLS, AND A `Proxmox.CephPool` MUST NOT ALSO DECLARE THEM.
|
|
30
|
+
* `createfs` builds `<name>_data` and `<name>_metadata` itself and refuses outright if either
|
|
31
|
+
* exists ("ceph pools '…_data' and/or '…_metadata' already exist"). MEASURED on TB4: the live
|
|
32
|
+
* `cephfs-tb4` owns `cephfs-tb4_data` (id 6) and `cephfs-tb4_metadata` (id 7), both of which
|
|
33
|
+
* `GET /nodes/n2/ceph/pool` lists like any other pool — and `/nodes/{node}/ceph/pool/{name}` DOES
|
|
34
|
+
* have GET, PUT and DELETE, so a pool resource would be a clean `pveHandlers` fit and would
|
|
35
|
+
* adopt them without noticing. Declaring both is double management of one object: whichever
|
|
36
|
+
* reconciles first wins, and the loser either fails its create or PUTs `size`/`pg_num` onto pools
|
|
37
|
+
* this resource believes it owns. Declare the filesystem here and leave its two pools alone.
|
|
38
|
+
*
|
|
39
|
+
* ⚠️ `add-storage` IS A SECOND SIDE EFFECT AND IT LANDS IN storage.cfg. Set, `createfs` also calls
|
|
40
|
+
* `PVE::API2::Storage::Config->create` with type `cephfs`, content `backup,iso,vztmpl` and
|
|
41
|
+
* `fs-name <name>`. MEASURED: /etc/pve/storage.cfg on n2 holds exactly that — `cephfs:
|
|
42
|
+
* cephfs-tb4` / `content backup,vztmpl,iso` / `fs-name cephfs-tb4`. So a `Proxmox.Storage`
|
|
43
|
+
* declaring `cephfs-tb4` would be managing an entry this resource created. Declare ONE of them:
|
|
44
|
+
* either `add-storage` here and no storage resource, or `add-storage` off and a
|
|
45
|
+
* `Proxmox.Storage` that reads this resource's `name` attribute — which also gets the ordering
|
|
46
|
+
* right for free, because Alchemy orders by data flow.
|
|
47
|
+
*
|
|
48
|
+
* ⚠️ `node` IS A ROUTE, NOT IDENTITY, AND MUST NEVER BE DIFFED. A CephFS is cluster-wide; the node
|
|
49
|
+
* in the path only says which node PVE proxies the call to. MEASURED: `GET /nodes/n2/ceph/fs`,
|
|
50
|
+
* `…/n3/…` and `…/n4/…` returned the byte-identical array. So changing `node` plans `noop`, and
|
|
51
|
+
* two resources naming the same `name` on different nodes are the SAME filesystem — the vmid
|
|
52
|
+
* hazard from lxc.ts, with a filesystem on the end of it instead of a container.
|
|
53
|
+
*
|
|
54
|
+
* ⚠️ PRIVILEGES: the read lane needs `Sys.Audit` OR `Datastore.Audit` on `/`, which an auditor
|
|
55
|
+
* role already holds — unlike storage.ts, nothing needs widening before the first plan. The
|
|
56
|
+
* write side is in ceph-fs-wire.ts beside the calls, and in docs/privileges.md with the rest.
|
|
57
|
+
*/
|
|
58
|
+
import { Resource } from 'alchemy';
|
|
59
|
+
import * as Provider from 'alchemy/Provider';
|
|
60
|
+
import { type PveRequirements, type WithTarget } from './resource.ts';
|
|
61
|
+
export interface CephFsProps extends WithTarget {
|
|
62
|
+
/** ⚠️ Which node answers the call, NOT which node holds the filesystem. See the header. */
|
|
63
|
+
node: string;
|
|
64
|
+
/**
|
|
65
|
+
* The filesystem name, and the cluster-wide primary key. PVE's pattern is `^[^:/\s]+$`.
|
|
66
|
+
* ⛔ CHANGING IT IS A REPLACE, WHICH DESTROYS THE OLD FILESYSTEM. `diff` says so explicitly
|
|
67
|
+
* below rather than letting the factory report a create-shaped `update` and orphan the old one.
|
|
68
|
+
*/
|
|
69
|
+
name: string;
|
|
70
|
+
/**
|
|
71
|
+
* Placement groups for the backing data pool; the metadata pool gets a quarter of it, or 8 when
|
|
72
|
+
* this is under 32. PVE's default is 128.
|
|
73
|
+
*
|
|
74
|
+
* ⛔ CREATE-TIME ONLY, UNREADABLE, AND OWNED BY THE AUTOSCALER FROM THE SECOND IT LANDS. It is
|
|
75
|
+
* never compared — see the ⛔ on `matches` — and this is not caution, it is measured. TB4's
|
|
76
|
+
* `cephfs-tb4` was created with the default 128 (the arithmetic proves it: `cephfs-tb4_metadata`
|
|
77
|
+
* sits at 32, which is 128/4), and `cephfs-tb4_data` reads pg_num 32 TODAY. The autoscaler moved
|
|
78
|
+
* it by a factor of four, and it is not finished: `pg_autoscale_mode` is `on` for every pool on
|
|
79
|
+
* this cluster and `cephfs-tb4_metadata` already reports `pg_num_final: 16` against its live 32.
|
|
80
|
+
*/
|
|
81
|
+
pg_num?: number;
|
|
82
|
+
/**
|
|
83
|
+
* Also write a `cephfs` storage entry for this filesystem. ⚠️ Create-time only and never read
|
|
84
|
+
* back — it is an ACTION, not a field. See the header for what it does to `Proxmox.Storage`.
|
|
85
|
+
*/
|
|
86
|
+
'add-storage'?: boolean;
|
|
87
|
+
/** ⛔ DELETE-TIME ONLY, AND IT ERASES THE DATA. Read the ⛔ on `destroyFs` before setting it. */
|
|
88
|
+
'remove-pools'?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Delete the pveceph-managed storage entries too. ⚠️ PVE refuses unless they are already
|
|
91
|
+
* disabled ("storage '…' is not disabled, make sure to disable and unmount the storage first"),
|
|
92
|
+
* so this fails the destroy rather than unmounting anything out from under a running guest.
|
|
93
|
+
*/
|
|
94
|
+
'remove-storages'?: boolean;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* ⚠️ EVERY FIELD HERE IS REPORTED AND NONE IS COMPARED. They exist so a plan, and the state entry
|
|
98
|
+
* behind it, can say what the declaration actually points at — which pools hold the bytes.
|
|
99
|
+
*/
|
|
100
|
+
export interface CephFsAttributes {
|
|
101
|
+
name: string;
|
|
102
|
+
/** The first data pool. PVE keeps it for compatibility; `data_pools` is the real answer. */
|
|
103
|
+
data_pool: string;
|
|
104
|
+
/**
|
|
105
|
+
* ⚠️ SORTED, AND IT IS A SET RATHER THAN A LIST. A CephFS can hold several data pools — added
|
|
106
|
+
* with `ceph fs add_data_pool`, which this resource has no way to express — and PVE reports
|
|
107
|
+
* them in the MDS map's own order. Sorting is what keeps this attribute stable across reads.
|
|
108
|
+
*/
|
|
109
|
+
data_pools: string;
|
|
110
|
+
metadata_pool: string;
|
|
111
|
+
/**
|
|
112
|
+
* ⚠️ AN INTEGER THAT NAMES SOMETHING WRITTEN AS A NAME, AND THEREFORE A CLASSIC FOREVER-DIFF IF
|
|
113
|
+
* IT EVER REACHED `matches`. PVE answers `metadata_pool_id: 7` while every write in this family
|
|
114
|
+
* spells a pool by name. It is here to be read, never to be compared. `-1` means absent: the
|
|
115
|
+
* field is optional in the schema and older PVE omits it.
|
|
116
|
+
*/
|
|
117
|
+
metadata_pool_id: number;
|
|
118
|
+
}
|
|
119
|
+
export interface ProxmoxCephFs extends Resource<'Proxmox.CephFs', CephFsProps, CephFsAttributes, never, PveRequirements> {
|
|
120
|
+
}
|
|
121
|
+
/** ★ `retain` by default — a filesystem holding data cannot be rebuilt. See the ★ in resource.ts. */
|
|
122
|
+
export declare const ProxmoxCephFs: import("alchemy").ResourceClass<ProxmoxCephFs>;
|
|
123
|
+
export declare const ProxmoxCephFsProvider: () => import("effect/Layer").Layer<Provider.Provider<ProxmoxCephFs>, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
124
|
+
//# sourceMappingURL=ceph-fs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ceph-fs.d.ts","sourceRoot":"","sources":["../../src/proxmox/ceph-fs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAW7C,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAiB,MAAM,eAAe,CAAC;AAErF,MAAM,WAAW,WAAY,SAAQ,UAAU;IAC7C,2FAA2F;IAC3F,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;;;OAUG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,+FAA+F;IAC/F,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,4FAA4F;IAC5F,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,aAAc,SAAQ,QAAQ,CAC7C,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,qGAAqG;AACrG,eAAO,MAAM,aAAa,gDAExB,CAAC;AA4BH,eAAO,MAAM,qBAAqB,mIA6E/B,CAAC"}
|