@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,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The CRUSH tree, walked down to one OSD leaf.
|
|
3
|
+
*
|
|
4
|
+
* ★ SPLIT OUT OF ceph-osd.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, and the seam is the same
|
|
5
|
+
* one metric-server-form.ts cuts: this file answers "what does the cluster say about osd.N",
|
|
6
|
+
* ceph-osd.ts answers "what is an OSD and what may a plan do about it". Nothing here calls the
|
|
7
|
+
* API and nothing here decides a diff. `CephOsdAttributes` lives on this side of the seam
|
|
8
|
+
* because it is a description of what the TREE says — keeping each field's
|
|
9
|
+
* reported-never-compared reasoning against the line that builds it — and ceph-osd.ts re-exports
|
|
10
|
+
* it so a consumer still imports one name from one place. The `import type` back for
|
|
11
|
+
* `CephOsdProps` is a cycle on paper only: it is erased before anything runs.
|
|
12
|
+
*
|
|
13
|
+
* ⛔ `GET /nodes/{node}/ceph/osd` ANSWERS A TREE, NOT A LIST, AND IT IS THE WHOLE CLUSTER'S TREE.
|
|
14
|
+
* MEASURED 2026-09-13: asked of n2 it answers `{"flags":…,"root":{"children":[…]}}` whose
|
|
15
|
+
* leaves include osd.0 and osd.5 on n4; asked of n3 it answers the identical six leaves. The
|
|
16
|
+
* `{node}` in the path chooses WHO ANSWERS, not what is listed. A provider that read it as "the
|
|
17
|
+
* OSDs on this node" would find osd.0 through n2 and then report it as living there.
|
|
18
|
+
*
|
|
19
|
+
* ⛔ AND THE SINGLE-OBJECT PATH CANNOT BE USED INSTEAD — THIS WALK EXISTS BECAUSE IT LIES.
|
|
20
|
+
* MEASURED: `GET /nodes/n2/ceph/osd/99`, an id with no OSD behind it, answers HTTP 200 with
|
|
21
|
+
* `[{"name":"metadata"},{"name":"lv-info"}]` — byte for byte what `…/osd/2` answers for a real
|
|
22
|
+
* one. It is a directory index (its schema says `permissions: {"user":"all"}`), not a read of
|
|
23
|
+
* the OSD. Point `PveSpec.path` at it and every declared OSD is "present": `diff` reports
|
|
24
|
+
* `noop` over a dead replica forever, and `reconcile`'s read-back guard — the one thing that
|
|
25
|
+
* stops this package recording objects that do not exist — passes on an OSD that was never
|
|
26
|
+
* built. The only endpoint that can tell present from absent is the tree.
|
|
27
|
+
*
|
|
28
|
+
* ★ MEASURED ON TB4 ON 2026-09-13, `GET /nodes/n2/ceph/osd`. Six leaves, all `up` and `in`, Ceph
|
|
29
|
+
* 20.2.2 tentacle:
|
|
30
|
+
*
|
|
31
|
+
* id name host device_class crush_weight reweight pgs status
|
|
32
|
+
* 0 osd.0 n4 ssd 1.86299133300781 1 99 up
|
|
33
|
+
* 5 osd.5 n4 ssd 1.86299133300781 1 94 up
|
|
34
|
+
* 1 osd.1 n3 ssd 1.86299133300781 1 96 up
|
|
35
|
+
* 4 osd.4 n3 ssd 1.81939697265625 1 97 up
|
|
36
|
+
* 2 osd.2 n2 ssd 1.81939697265625 1 88 up
|
|
37
|
+
* 3 osd.3 n2 ssd 1.81939697265625 1 105 up
|
|
38
|
+
*
|
|
39
|
+
* `{ node: 'n2', osdid: 2, host: 'n2', device_class: 'ssd' }` plans `noop` against that row.
|
|
40
|
+
* That is the acceptance test this file was written to pass.
|
|
41
|
+
*
|
|
42
|
+
* ⚠️ EVERY OTHER COLUMN IS REPORTED AND NEVER COMPARED, FOR FOUR DIFFERENT REASONS. `pgs` is the
|
|
43
|
+
* autoscaler's placement rather than anybody's declaration — MEASURED, it spans 88 to 105 across
|
|
44
|
+
* six identical disks, and Ceph moves PGs whenever it rebalances. `crush_weight` is a FLOAT
|
|
45
|
+
* derived from device size (MEASURED: 1.86299133300781), so comparing it is float equality
|
|
46
|
+
* against a number Ceph recomputes on `crush reweight`. `reweight` is what
|
|
47
|
+
* `reweight-by-utilization` writes, unasked. `status` and `in` are daemon state: an OSD that
|
|
48
|
+
* flaps down while a node reboots would otherwise put work in the plan that no write this
|
|
49
|
+
* provider owns could ever clear. Each of those is the forever-update this package has already
|
|
50
|
+
* been bitten by, and each is out of `settled` below.
|
|
51
|
+
*
|
|
52
|
+
* ⚠️ THE CLUSTER'S OSD FLAGS ARE NOT AN ATTRIBUTE EITHER. The read carries
|
|
53
|
+
* `"flags":"sortbitwise,recovery_deletes,purged_snapdirs,pglog_hardlimit"` NEXT TO the root
|
|
54
|
+
* rather than inside any leaf: they describe the OSDMap, not this OSD. Recording them per-OSD
|
|
55
|
+
* would rewrite all six resources' state the moment somebody set `noout` for ten minutes of
|
|
56
|
+
* maintenance — the churn-that-reads-like-drift that keeps `digest` out of storage.ts.
|
|
57
|
+
*
|
|
58
|
+
*/
|
|
59
|
+
import * as Effect from 'effect/Effect';
|
|
60
|
+
import type { CephOsdProps } from './ceph-osd.ts';
|
|
61
|
+
/** A CRUSH node. Buckets carry `children`; an OSD leaf carries `type: 'osd'`. */
|
|
62
|
+
type CrushNode = Record<string, unknown>;
|
|
63
|
+
/**
|
|
64
|
+
* Every OSD leaf under the tree root, at any depth.
|
|
65
|
+
*
|
|
66
|
+
* ⚠️ RECURSIVE BECAUSE THE TREE IS NOT THREE LEVELS DEEP BY LAW. TB4 measures as
|
|
67
|
+
* root(`default`) -> host(n2|n3|n4) -> osd, but CRUSH admits datacenter, rack and chassis
|
|
68
|
+
* buckets between them, and an OSD created outside a host bucket hangs off the root. Reaching
|
|
69
|
+
* in as `root.children[].children[]` would read this cluster correctly and answer "absent" —
|
|
70
|
+
* i.e. "the replica is gone" — on the first cluster that has a rack in it.
|
|
71
|
+
*
|
|
72
|
+
* ⚠️ THE OUTERMOST OBJECT IS NOT ITSELF A BUCKET. MEASURED: `root` is `{"children":[…],"leaf":0}`
|
|
73
|
+
* with no `type` and no `name`; the bucket called `default` is its first child. So the walk
|
|
74
|
+
* starts at `tree.root` and selects on `type === 'osd'` rather than assuming a shape.
|
|
75
|
+
*/
|
|
76
|
+
export declare const osdLeaves: (tree: CrushNode) => CrushNode[];
|
|
77
|
+
/**
|
|
78
|
+
* The leaf for one OSD id, or undefined when nothing in the tree carries it.
|
|
79
|
+
*
|
|
80
|
+
* ⛔ `id` COMES BACK AS A STRING HERE AND AS AN INTEGER EVERYWHERE ELSE, which is the "an id
|
|
81
|
+
* returned one way and written another" forever-diff in its most expensive form. MEASURED: the
|
|
82
|
+
* leaf for osd.2 carries `"id":"2"` (and the host buckets carry `"id":"-7"`), while
|
|
83
|
+
* `GET …/osd/2/metadata` carries `"id":2` and the `osdid` parameter on POST and DELETE is typed
|
|
84
|
+
* integer. `'2' === 2` is false, so a provider comparing the raw value finds NOTHING, calls
|
|
85
|
+
* every declared OSD absent — and, with a create path wired up, offers to build six OSDs over
|
|
86
|
+
* six live disks. `int` from values.ts is the narrowing, and `Number.NaN` is the fallback
|
|
87
|
+
* precisely because it equals nothing, id 0 included.
|
|
88
|
+
*
|
|
89
|
+
* ⚠️ FIRST MATCH WINS. OSD ids are unique cluster-wide, and PVE does not put the per-device-class
|
|
90
|
+
* shadow buckets (`default~ssd`) in this tree — MEASURED: the whole tree is one `default` root,
|
|
91
|
+
* three hosts, six leaves. If a future PVE did include them, taking the first match keeps the
|
|
92
|
+
* answer stable instead of reporting drift depending on which copy was walked into first.
|
|
93
|
+
*/
|
|
94
|
+
export declare const findOsd: (tree: CrushNode, osdid: number) => CrushNode | undefined;
|
|
95
|
+
export interface CephOsdAttributes {
|
|
96
|
+
/** Normalised to an integer here even though the tree spells it `"2"`. */
|
|
97
|
+
osdid: number;
|
|
98
|
+
/** `osd.2` — how Ceph, the UI and every log line name it. */
|
|
99
|
+
name: string;
|
|
100
|
+
/** COMPARED, but only when `props.host` is declared. */
|
|
101
|
+
host: string;
|
|
102
|
+
/** COMPARED, but only when `props.device_class` is declared. */
|
|
103
|
+
device_class: string;
|
|
104
|
+
/**
|
|
105
|
+
* ⚠️ REPORTED, NEVER COMPARED — `bluestore` on every live OSD and filestore has been gone for
|
|
106
|
+
* years, so declaring it would add a field that cannot disagree. It is recorded because a
|
|
107
|
+
* future objectstore change is exactly the kind of thing state should have caught in writing.
|
|
108
|
+
*/
|
|
109
|
+
osdtype: string;
|
|
110
|
+
/** ⚠️ REPORTED, NEVER COMPARED. Daemon state — see the second ⚠️ in the header. */
|
|
111
|
+
status: string;
|
|
112
|
+
/** ⚠️ REPORTED, NEVER COMPARED. Ceph itself sets this to 0 after `mon_osd_down_out_interval`. */
|
|
113
|
+
in: boolean;
|
|
114
|
+
/** ⚠️ REPORTED, NEVER COMPARED. A float Ceph recomputes; comparing it is a permanent diff. */
|
|
115
|
+
crush_weight: number;
|
|
116
|
+
/** ⚠️ REPORTED, NEVER COMPARED. `reweight-by-utilization` owns this number, not a declaration. */
|
|
117
|
+
reweight: number;
|
|
118
|
+
/** ⚠️ REPORTED, NEVER COMPARED. The autoscaler moves PGs between OSDs whenever it likes. */
|
|
119
|
+
pgs: number;
|
|
120
|
+
/** Device capacity in bytes. Stable per disk, and the honest answer to "how big is this OSD". */
|
|
121
|
+
total_space: number;
|
|
122
|
+
/** `20.2.2`. ⚠️ Reported, never compared — an upgrade is not this resource's drift. */
|
|
123
|
+
ceph_version: string;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* One leaf, as the attributes a plan and the state store get to see.
|
|
127
|
+
*
|
|
128
|
+
* ⚠️ THE TELEMETRY IS DELIBERATELY DROPPED, NOT FORGOTTEN. The leaf also carries `bytes_used`,
|
|
129
|
+
* `percent_used`, `apply_latency_ms` and `commit_latency_ms`. MEASURED, AND THE MEASUREMENT SAYS
|
|
130
|
+
* THE OPPOSITE OF WHAT YOU WOULD EXPECT: across two reads minutes apart, all four were identical
|
|
131
|
+
* on every OSD, and both latencies read 0 — TB4 was idle, so nothing was caught in the act. That
|
|
132
|
+
* these move is therefore REASONED, from what they count, not observed. Alchemy persists
|
|
133
|
+
* attributes; on a cluster doing work, keeping them would rewrite all six resources' state on
|
|
134
|
+
* every deploy and record a number that is stale by the time anybody reads it. What is kept is
|
|
135
|
+
* what a human asking "what is this replica, and is it healthy" needs; it is not a metrics feed.
|
|
136
|
+
*
|
|
137
|
+
* ⚠️ `osdid` AND `name` ARE THE NORMALISED ID AND ITS SPELLING, taken from the leaf rather than
|
|
138
|
+
* echoed from props, so that state records what the cluster said. They agree by construction
|
|
139
|
+
* here — the leaf was found BY id — but echoing props into attributes is how a resource ends up
|
|
140
|
+
* recording a declaration as though it were an observation.
|
|
141
|
+
*/
|
|
142
|
+
export declare const osdAttributes: (leaf: CrushNode, props: CephOsdProps) => CephOsdAttributes;
|
|
143
|
+
/**
|
|
144
|
+
* The live OSD as attributes, or undefined.
|
|
145
|
+
*
|
|
146
|
+
* ★ IT LIVES HERE RATHER THAN IN ceph-osd.ts BECAUSE THE READ SIDE IS THIS FILE'S WHOLE JOB —
|
|
147
|
+
* `findOsd` and `osdAttributes` are the two halves it composes, and keeping the caller beside
|
|
148
|
+
* them is what let ceph-osd.ts come back under the 250-line cap when create and delete were
|
|
149
|
+
* added. ⚠️ A 404 or a 403 both arrive here as `undefined`: see the ⛔ on `readRole` in
|
|
150
|
+
* resource.ts for why that distinction is invisible and what it costs.
|
|
151
|
+
*/
|
|
152
|
+
export declare const readOsd: (props: CephOsdProps) => Effect.Effect<CephOsdAttributes | undefined, Error, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
153
|
+
export {};
|
|
154
|
+
//# sourceMappingURL=ceph-osd-tree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ceph-osd-tree.d.ts","sourceRoot":"","sources":["../../src/proxmox/ceph-osd-tree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAIlD,iFAAiF;AACjF,KAAK,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAOzC;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,SAAS,SAAU,SAAS,KAAG,SAAS,EAQpD,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,OAAO,SAAU,SAAS,SAAS,MAAM,0BACiB,CAAC;AAExE,MAAM,WAAW,iBAAiB;IAChC,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,MAAM,EAAE,MAAM,CAAC;IACf,iGAAiG;IACjG,EAAE,EAAE,OAAO,CAAC;IACZ,8FAA8F;IAC9F,YAAY,EAAE,MAAM,CAAC;IACrB,kGAAkG;IAClG,QAAQ,EAAE,MAAM,CAAC;IACjB,4FAA4F;IAC5F,GAAG,EAAE,MAAM,CAAC;IACZ,iGAAiG;IACjG,WAAW,EAAE,MAAM,CAAC;IACpB,uFAAuF;IACvF,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,aAAa,SAAU,SAAS,SAAS,YAAY,KAAG,iBAanE,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,OAAO,UAAW,YAAY,8GAMxC,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two operations that change a disk: building an OSD and removing one.
|
|
3
|
+
*
|
|
4
|
+
* ★ SPLIT OUT OF ceph-osd.ts FOR THE 250-LINE CAP, and the seam is the honest one — this file is
|
|
5
|
+
* everything that WRITES, and ceph-osd.ts is the declaration and the assertion. A reader asking
|
|
6
|
+
* "can this destroy my data" has one file to read.
|
|
7
|
+
*
|
|
8
|
+
* ⛔ BOTH OF THESE ARE IRREVERSIBLE. `create` hands a block device to `ceph-volume lvm create`,
|
|
9
|
+
* which zaps it; `delete` takes a replica out of a live pool and Ceph immediately begins
|
|
10
|
+
* rewriting the missing copies across the remaining nodes. `Proxmox.CephOsd` therefore declares
|
|
11
|
+
* `defaultRemovalPolicy: 'retain'`, so removing a declaration does NOT reach `destroyOsd` — a
|
|
12
|
+
* caller opts in with `.pipe(RemovalPolicy.destroy())`.
|
|
13
|
+
*/
|
|
14
|
+
import * as Effect from 'effect/Effect';
|
|
15
|
+
import type { CephOsdProps } from './ceph-osd.ts';
|
|
16
|
+
export declare const createOsd: (news: CephOsdProps) => Effect.Effect<import("./ceph-osd-tree.ts").CephOsdAttributes, Error, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
17
|
+
/**
|
|
18
|
+
* ⛔ REMOVING THE DECLARATION REMOVES THE ASSERTION, NOT THE REPLICA, AND THAT ASYMMETRY IS THE
|
|
19
|
+
* POINT. `DELETE /nodes/{node}/ceph/osd/{osdid}` takes a replica out of a live pool — with
|
|
20
|
+
* `nobackfill`/`norecover` unset, Ceph immediately starts rewriting the missing copies across
|
|
21
|
+
* the two remaining nodes — and with `cleanup=1` it zaps the LVs as well. A stack file is
|
|
22
|
+
* edited by people in a hurry; a disk is not a thing a deleted line gets to destroy. Take an
|
|
23
|
+
* OSD out by hand, with the cluster healthy and `ceph -s` in front of you: `ceph osd out N`,
|
|
24
|
+
* wait for the backfill to finish, then `pveceph osd destroy N`.
|
|
25
|
+
*
|
|
26
|
+
* ★ SO IT IS IMPLEMENTED, AND THE RESOURCE DEFAULTS TO `retain` INSTEAD. Removing a declaration
|
|
27
|
+
* drops the state row and leaves the replica alone; a caller who really means it opts in with
|
|
28
|
+
* `.pipe(RemovalPolicy.destroy())`. That is the same shape Terraform gives `prevent_destroy`,
|
|
29
|
+
* and it beats a `delete` that silently does nothing — which is a lie to anyone reading the
|
|
30
|
+
* plan.
|
|
31
|
+
*
|
|
32
|
+
* ⚠️ `cleanup` DEFAULTS TO FALSE, so the logical volumes survive and the disk can be re-added
|
|
33
|
+
* without a rebuild. Ceph still starts backfilling the missing copies the moment the OSD goes.
|
|
34
|
+
*/
|
|
35
|
+
export declare const destroyOsd: (olds: CephOsdProps) => Effect.Effect<unknown, Error, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
36
|
+
//# sourceMappingURL=ceph-osd-write.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ceph-osd-write.d.ts","sourceRoot":"","sources":["../../src/proxmox/ceph-osd-write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAKlD,eAAO,MAAM,SAAS,oJA4BpB,CAAC;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,UAAU,SAAU,YAAY,wFAO1C,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Proxmox.CephOsd` — one OSD in the cluster's CRUSH tree.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ CREATE AND DELETE ARE IMPLEMENTED AND `root@pam` IS THE ONLY IDENTITY THAT CAN RUN THEM.
|
|
5
|
+
* That is PVE's restriction, not this package's caution, and it is MEASURED from the cluster's
|
|
6
|
+
* own schema (`/usr/share/pve-docs/api-viewer/apidoc.js`, 2026-09-13):
|
|
7
|
+
*
|
|
8
|
+
* GET /nodes/{node}/ceph/osd -> {"check":["perm","/",["Sys.Audit","Datastore.Audit"],"any",1]}
|
|
9
|
+
* POST /nodes/{node}/ceph/osd -> *** no permissions block at all ***
|
|
10
|
+
* DELETE /nodes/{node}/ceph/osd/{osdid} -> *** no permissions block at all ***
|
|
11
|
+
*
|
|
12
|
+
* A PVE method with no permissions block is refused to every identity except `root@pam`, so an
|
|
13
|
+
* OpenBao-minted `hf-provision@pve!…` token is answered 403 however wide its role — widening
|
|
14
|
+
* `LXCProvisioner` cannot fix it. Compare the sibling families, which DO carry a check and
|
|
15
|
+
* therefore do work with a token: `ceph/fs`, `ceph/mon` and `disks/zfs` all want `Sys.Modify`.
|
|
16
|
+
*
|
|
17
|
+
* ⚠️ SO THE WRITE PATH IS HONEST RATHER THAN USEFUL. It is written out properly in
|
|
18
|
+
* `ceph-osd-write.ts` — read that file for what each verb destroys — and it will work for an
|
|
19
|
+
* operator running as root@pam. Through this package's credential it will 403, and a 403 is a
|
|
20
|
+
* better answer than a `delete` that silently does nothing while the plan claims otherwise.
|
|
21
|
+
*
|
|
22
|
+
* ★ WHAT IT IS ACTUALLY FOR, THEN: ASSERTION. It turns "n2, n3 and n4 each hold two ssd OSDs"
|
|
23
|
+
* into a line a plan checks, and it gives Ceph-backed resources something real to depend on —
|
|
24
|
+
* read `osd.osdid` into an `rbd` storage's props and Alchemy orders that storage after the
|
|
25
|
+
* assertion, so a stack can no longer report a healthy Ceph storage on a cluster whose OSDs are
|
|
26
|
+
* gone. A replica that disappears becomes work in `plan` and a loud failure on the deploy.
|
|
27
|
+
*
|
|
28
|
+
* ⛔ THE WRITE VERBS MAY NOT EVEN BE REACHABLE FOR THIS PACKAGE'S CREDENTIAL. MEASURED from the
|
|
29
|
+
* cluster's own schema (`/usr/share/pve-docs/api-viewer/apidoc.js`, 2026-09-13): `index`,
|
|
30
|
+
* `osdindex`, `metadata` and `lv-info` each declare a `permissions` block, while `createosd`
|
|
31
|
+
* and `destroyosd` declare NONE — the key is absent, not empty. REASONED, NOT MEASURED: PVE
|
|
32
|
+
* refuses a method with no permissions block to everyone but `root@pam`, so an OpenBao-minted
|
|
33
|
+
* `hf-provision@pve!…` token would likely be answered 403 however wide its role. That is a
|
|
34
|
+
* reason to leave the write lane closed, not something to confirm by trying: if the reasoning
|
|
35
|
+
* is wrong, the confirmation costs a disk.
|
|
36
|
+
*/
|
|
37
|
+
import { Resource } from 'alchemy';
|
|
38
|
+
import * as Provider from 'alchemy/Provider';
|
|
39
|
+
import type { CephOsdAttributes } from './ceph-osd-tree.ts';
|
|
40
|
+
import type { PveRequirements } from './resource.ts';
|
|
41
|
+
import type { WithTarget } from './resource.ts';
|
|
42
|
+
/**
|
|
43
|
+
* ⚠️ RE-EXPORTED, NOT DECLARED HERE — it is built in ceph-osd-tree.ts, next to the walk that fills
|
|
44
|
+
* it in and to the reasoning for which of its fields may be compared. One name, one import.
|
|
45
|
+
*/
|
|
46
|
+
export type { CephOsdAttributes };
|
|
47
|
+
export interface CephOsdProps extends WithTarget {
|
|
48
|
+
/**
|
|
49
|
+
* Which node ANSWERS the read — not where the OSD lives. MEASURED: the tree n2 returns and the
|
|
50
|
+
* tree n3 returns are the same six leaves, n4's included. Point this at any node that is up;
|
|
51
|
+
* `host` is the field that says where the disk is.
|
|
52
|
+
*/
|
|
53
|
+
node: string;
|
|
54
|
+
/** `2` for `osd.2`. Identity, and an INTEGER — see the ⛔ on `findOsd` in ceph-osd-tree.ts. */
|
|
55
|
+
osdid: number;
|
|
56
|
+
/**
|
|
57
|
+
* The CRUSH host bucket this OSD must sit in. Undeclared means unmanaged, as in storage.ts.
|
|
58
|
+
*
|
|
59
|
+
* ⚠️ DECLARING IT IS AN ASSERTION WITH NO REPAIR. If osd.2 turns up under n3, the plan reports
|
|
60
|
+
* work every time and the deploy fails, because moving an OSD between failure domains is a
|
|
61
|
+
* `ceph osd crush move` this provider will not perform. That is the intent: a replica in the
|
|
62
|
+
* wrong failure domain is not something to converge quietly past.
|
|
63
|
+
*/
|
|
64
|
+
host?: string;
|
|
65
|
+
/**
|
|
66
|
+
* `ssd` | `hdd` | `nvme`, asserted the same way as `host`.
|
|
67
|
+
*
|
|
68
|
+
* ⚠️ THE READ'S SPELLING IS KEPT rather than the create parameter's (`crush-device-class`).
|
|
69
|
+
* This resource never writes, so the tree's name is the only one it has, and inventing a
|
|
70
|
+
* second name for one field is exactly what values.ts exists to stop.
|
|
71
|
+
*/
|
|
72
|
+
device_class?: string;
|
|
73
|
+
/**
|
|
74
|
+
* The block device to build this OSD from, e.g. `/dev/nvme1n1`. Declaring it makes the resource
|
|
75
|
+
* able to CREATE; leaving it out keeps the resource a pure assertion over an OSD that exists.
|
|
76
|
+
*
|
|
77
|
+
* ⛔ EVERYTHING ON THIS DEVICE IS DESTROYED BY A CREATE. `POST /nodes/{node}/ceph/osd` hands it
|
|
78
|
+
* to `ceph-volume lvm create`, which zaps it. Declare it only for a disk that is genuinely
|
|
79
|
+
* blank, and read the ⛔ about id allocation below before assuming the create will land as the
|
|
80
|
+
* `osdid` written above.
|
|
81
|
+
*/
|
|
82
|
+
dev?: string;
|
|
83
|
+
/**
|
|
84
|
+
* Zap the OSD's logical volumes on destroy (`cleanup=1`). Default false: the LVs are left behind
|
|
85
|
+
* so the disk can be re-added without a rebuild, which is the recoverable choice.
|
|
86
|
+
*/
|
|
87
|
+
cleanup?: boolean;
|
|
88
|
+
}
|
|
89
|
+
export interface ProxmoxCephOsd extends Resource<'Proxmox.CephOsd', CephOsdProps, CephOsdAttributes, never, PveRequirements> {
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* ★ `retain` BY DEFAULT, FOLLOWING ALCHEMY'S OWN PRECEDENT. `GitHub.Repository` and
|
|
93
|
+
* `Cloudflare.Zone` default to retain "because their contents are irreplaceable"; an OSD is a
|
|
94
|
+
* replica of live data and qualifies twice over. Retain means DELETING THE DECLARATION DOES NOT
|
|
95
|
+
* DELETE THE OSD — Alchemy drops its state row and leaves the disk alone. `destroyOsd` is fully
|
|
96
|
+
* implemented in ceph-osd-write.ts and runs when the caller opts in with
|
|
97
|
+
* `.pipe(RemovalPolicy.destroy())`, so this is the Terraform `prevent_destroy` shape rather than
|
|
98
|
+
* a missing operation — and through a minted token it 403s anyway, per the ⛔ at the top.
|
|
99
|
+
*/
|
|
100
|
+
export declare const ProxmoxCephOsd: import("alchemy").ResourceClass<ProxmoxCephOsd>;
|
|
101
|
+
export declare const ProxmoxCephOsdProvider: () => import("effect/Layer").Layer<Provider.Provider<ProxmoxCephOsd>, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
102
|
+
//# sourceMappingURL=ceph-osd.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ceph-osd.d.ts","sourceRoot":"","sources":["../../src/proxmox/ceph-osd.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAG5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD;;;GAGG;AACH,YAAY,EAAE,iBAAiB,EAAE,CAAC;AAElC,MAAM,WAAW,YAAa,SAAQ,UAAU;IAC9C;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,8FAA8F;IAC9F,KAAK,EAAE,MAAM,CAAC;IACd;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;;OAQG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,cAAe,SAAQ,QAAQ,CAC9C,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,iDAEzB,CAAC;AA+HH,eAAO,MAAM,sBAAsB,oIACoD,CAAC"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a Ceph pool IS as a declaration, and the two different forms PVE wants it in.
|
|
3
|
+
*
|
|
4
|
+
* ★ SPLIT OUT OF ceph-pool.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, and the seam is
|
|
5
|
+
* "does this touch the cluster" — the same one metric-server-form.ts draws, just with more on
|
|
6
|
+
* this side because this family needs it. Here: the declared shape, the reported shape, and the
|
|
7
|
+
* coercions that turn one into the other. There: reading TB4, deciding a diff, and the four
|
|
8
|
+
* handlers. Nothing in this file makes a call or decides an action.
|
|
9
|
+
* ⚠️ THE PROPS LIVE HERE RATHER THAN IN THE RESOURCE FILE, which is the one place this pair
|
|
10
|
+
* differs from metric-server / notification-target. There is no `import type` cycle back as a
|
|
11
|
+
* result; ceph-pool.ts re-exports both interfaces so the public surface is unchanged.
|
|
12
|
+
*
|
|
13
|
+
* ⛔ POST AND PUT DO NOT TAKE THE SAME PARAMETERS, AND BOTH EXTRAS ARE DESTRUCTIVE. MEASURED from
|
|
14
|
+
* the cluster's own schema (/usr/share/pve-docs/api-viewer/apidoc.js on n2, pve-manager 9.2.11,
|
|
15
|
+
* 2026-09-13): POST adds `add_storages` and `erasure-coding` to the shared set, PUT takes
|
|
16
|
+
* neither. Both are refused below, so what is left between the two bodies is `name`, which PUT
|
|
17
|
+
* reads from the path, and `pg_num`.
|
|
18
|
+
*
|
|
19
|
+
* ⛔ `pg_num` IS IN THE CREATE BODY AND NOT IN THE UPDATE BODY, AND THAT IS THE MOST IMPORTANT
|
|
20
|
+
* LINE IN THIS FILE. MEASURED in PVE::API2::Ceph::Pool::createpool on n2: a POST carrying no
|
|
21
|
+
* `pg_num` gets 128 substituted (`$param->{pg_num} //= 128`), and PVE::Ceph::Tools::create_pool
|
|
22
|
+
* then runs `osd pool create` — which Ceph answers with SUCCESS for a pool that already exists,
|
|
23
|
+
* before applying the rest of the body to it. So a create fired at a live pool SETS its pg_num,
|
|
24
|
+
* and lowering pg_num starts a PG MERGE: hours of backfill across every OSD, on a cluster whose
|
|
25
|
+
* Ceph traffic shares vmbr1.11 with everything else. ceph-pool.ts guards the create for exactly
|
|
26
|
+
* that reason; this half simply never offers pg_num to an update.
|
|
27
|
+
*/
|
|
28
|
+
import type { WithTarget } from './resource.ts';
|
|
29
|
+
export interface CephPoolProps extends WithTarget {
|
|
30
|
+
/**
|
|
31
|
+
* ⚠️ WHICH NODE THE CALL GOES THROUGH, NOT WHERE THE POOL LIVES. A pool is cluster-wide: n2 and
|
|
32
|
+
* n3 answer byte-identically for `cephtb4` (measured). The endpoint is node-scoped only
|
|
33
|
+
* because PVE talks to the local RADOS socket, so this is a door, not a location — it is never
|
|
34
|
+
* compared, and moving a declaration from n2 to n3 plans noop. Point it at a node you expect
|
|
35
|
+
* to be up: a node that is down makes the read fail, and a failed read reads as "absent".
|
|
36
|
+
*/
|
|
37
|
+
node: string;
|
|
38
|
+
/**
|
|
39
|
+
* Ceph's primary key, cluster-wide.
|
|
40
|
+
* ⛔ CHANGING IT DOES NOT RENAME ANYTHING. There is no rename in this API: the plan reads the new
|
|
41
|
+
* name, finds nothing, and CREATES AN EMPTY POOL, leaving the old one and its data behind,
|
|
42
|
+
* untracked. `replace` was considered and rejected — it would DELETE the old pool and every
|
|
43
|
+
* image in it to satisfy an edited string. Rename by hand, deliberately, or not at all.
|
|
44
|
+
*/
|
|
45
|
+
name: string;
|
|
46
|
+
/** Replicas per object, 1-7. ⚠️ Frozen when the pool carries Ceph's `nosizechange`. */
|
|
47
|
+
size?: number;
|
|
48
|
+
/** Replicas required to accept writes, 1-7. ⚠️ Frozen by `nosizechange` too. */
|
|
49
|
+
min_size?: number;
|
|
50
|
+
/**
|
|
51
|
+
* ⛔ CREATE-TIME ONLY, AND NEVER COMPARED — the single biggest noop hazard in this family. With
|
|
52
|
+
* `pg_autoscale_mode` on or warn the autoscaler owns this number: MEASURED on TB4, `cephtb4`
|
|
53
|
+
* sits at pg_num 128 while the autoscaler's own `pg_num_final` is 256, so a declaration
|
|
54
|
+
* diffing it reports an update the moment Ceph decides to act. A split is gradual even with
|
|
55
|
+
* the autoscaler off, and `nopgchange` can refuse the write outright — which kills the WHOLE
|
|
56
|
+
* PUT, not just this field. Declare it as the birth size; steer it afterwards with
|
|
57
|
+
* `pg_num_min`, or by hand.
|
|
58
|
+
*/
|
|
59
|
+
pg_num?: number;
|
|
60
|
+
/** The floor the autoscaler may not go below. Operator-owned and stable, so it IS compared. */
|
|
61
|
+
pg_num_min?: number;
|
|
62
|
+
/** ⚠️ PVE's create default is `warn`; Ceph's own is `on`, which is what all four TB4 pools have. */
|
|
63
|
+
pg_autoscale_mode?: 'off' | 'on' | 'warn';
|
|
64
|
+
/**
|
|
65
|
+
* The CRUSH rule BY NAME, e.g. `replicated_rule`.
|
|
66
|
+
* ★ THE SUSPECTED INTEGER-VS-NAME TRAP IS REAL BUT NOT ON THE PATH THIS RESOURCE READS, AND THAT
|
|
67
|
+
* IS MEASURED BOTH WAYS. `GET .../ceph/pool` (the collection) reports `crush_rule: 0` with the
|
|
68
|
+
* name beside it in `crush_rule_name`; `GET .../ceph/pool/{name}/status` reports
|
|
69
|
+
* `crush_rule: "replicated_rule"` — the same spelling POST and PUT accept. Reading the
|
|
70
|
+
* collection instead would reintroduce the forever-diff in a single edit.
|
|
71
|
+
*/
|
|
72
|
+
crush_rule?: string;
|
|
73
|
+
/**
|
|
74
|
+
* ⛔ CREATE-TIME ONLY, AND NEVER COMPARED. PVE's PUT turns this into
|
|
75
|
+
* `osd pool application enable` (Tools.pm:250-255), which ADDS a tag and never removes one,
|
|
76
|
+
* and Ceph refuses a second application without `--yes-i-really-mean-it` — so an update would
|
|
77
|
+
* either leave the pool tagged twice or die, and comparing one declared string against the
|
|
78
|
+
* LIST that comes back is the set-in-arbitrary-order forever-diff. The live list is reported
|
|
79
|
+
* as `applications` instead.
|
|
80
|
+
*/
|
|
81
|
+
application?: 'cephfs' | 'rbd' | 'rgw';
|
|
82
|
+
/** Autoscaler size hint, ⚠️ IN BYTES — see the ⚠️ on `updateBody`. A `1T` string is not accepted. */
|
|
83
|
+
target_size?: number;
|
|
84
|
+
/**
|
|
85
|
+
* Autoscaler share of total capacity, 0-1.
|
|
86
|
+
* ⚠️ WRITTEN BUT NEVER DIFFED, because a float is not a safe equality: Ceph stores a double and
|
|
87
|
+
* PVE prints about 15 significant digits, so a ratio that is not exactly representable would
|
|
88
|
+
* report an update on every plan for a pool nobody touched.
|
|
89
|
+
*/
|
|
90
|
+
target_size_ratio?: number;
|
|
91
|
+
/**
|
|
92
|
+
* ⛔ `never` ON PURPOSE, BOTH OF THEM, AND THE COMPILE ERROR IS THE FEATURE.
|
|
93
|
+
* `erasure-coding` does not configure this pool — it makes PVE build TWO pools and name
|
|
94
|
+
* neither of them what you declared: `<name>-data` for the EC data and `<name>-metadata` for
|
|
95
|
+
* the replicated half (Pool.pm:517-530). Nothing would then exist at `{name}`, so every plan
|
|
96
|
+
* would read absent and try to create it again, forever.
|
|
97
|
+
* `add_storages` writes a section into storage.cfg that this graph does not own; declare a
|
|
98
|
+
* `Proxmox.Storage` and pass this pool's `name` into it instead, which makes the link real.
|
|
99
|
+
*/
|
|
100
|
+
'erasure-coding'?: never;
|
|
101
|
+
add_storages?: never;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* ⚠️ THE THREE `no*` FLAGS ARE REPORTED BECAUSE THEY EXPLAIN A REFUSAL. Ceph can freeze a pool's
|
|
105
|
+
* size or its PG shape, and `set_pool` dies on the whole PUT when any one parameter will not
|
|
106
|
+
* apply (Tools.pm:310). `matches` refuses to diff the fields they freeze, so these attributes
|
|
107
|
+
* are the only place a plan can show WHY a declared size is not being enforced. All three read
|
|
108
|
+
* false on all four TB4 pools.
|
|
109
|
+
* ⚠️ `pg_num`, `id` AND `applications` ARE REPORTED AND NEVER COMPARED — see the props above.
|
|
110
|
+
* ⛔ NO `statistics` AND NO `autoscale_status`, though the verbose read returns both. They change
|
|
111
|
+
* every few seconds; persisting them would rewrite this resource's state on every deploy and
|
|
112
|
+
* read like drift. `UNSET` means the pool carries no value for that field.
|
|
113
|
+
*/
|
|
114
|
+
export interface CephPoolAttributes {
|
|
115
|
+
name: string;
|
|
116
|
+
/** The node the read went through. A debugging aid, not a property of the pool. */
|
|
117
|
+
node: string;
|
|
118
|
+
id: number;
|
|
119
|
+
size: number;
|
|
120
|
+
min_size: number;
|
|
121
|
+
pg_num: number;
|
|
122
|
+
pg_num_min: number;
|
|
123
|
+
pg_autoscale_mode: string;
|
|
124
|
+
/** ⚠️ Empty when PVE answered with a rule id instead of a name; `matches` then stops diffing it. */
|
|
125
|
+
crush_rule: string;
|
|
126
|
+
/** Sorted comma list, e.g. `rbd` or `cephfs`. Readable only under `?verbose=1`. */
|
|
127
|
+
applications: string;
|
|
128
|
+
target_size: number;
|
|
129
|
+
target_size_ratio: number;
|
|
130
|
+
nodelete: boolean;
|
|
131
|
+
nopgchange: boolean;
|
|
132
|
+
nosizechange: boolean;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Where one pool is WRITTEN, and where a new one is POSTed.
|
|
136
|
+
*
|
|
137
|
+
* ⚠️ NEITHER IS WHERE IT IS READ — the read path is a segment longer and lives in ceph-pool.ts
|
|
138
|
+
* next to the ⛔ that explains it. These two are here because a path is wire knowledge, and
|
|
139
|
+
* because the create guard needs the collection without needing anything else from the spec.
|
|
140
|
+
*/
|
|
141
|
+
export declare const object: (props: CephPoolProps) => string;
|
|
142
|
+
export declare const collection: (props: CephPoolProps) => string;
|
|
143
|
+
/**
|
|
144
|
+
* ⚠️ "NOT SET" IS -1 AND NOT 0, for the reason metric-server-form.ts gives: `pg_num_min`,
|
|
145
|
+
* `target_size` and `target_size_ratio` each take 0 as a REAL value meaning "no floor, no hint",
|
|
146
|
+
* so a provider using 0 as its absent-marker cannot tell a declared zero from an undeclared
|
|
147
|
+
* field. Every one of these is >= 0, so -1 sits outside all of their ranges.
|
|
148
|
+
*/
|
|
149
|
+
export declare const UNSET = -1;
|
|
150
|
+
/** ⚠️ PVE returns the tags as an ARRAY in Ceph's order, so `csv` sorts before anything sees them. */
|
|
151
|
+
export declare const applications: (value: unknown) => string;
|
|
152
|
+
/**
|
|
153
|
+
* Declared? compare it. Undeclared, or frozen by a flag the pool carries? leave it alone.
|
|
154
|
+
*
|
|
155
|
+
* ⚠️ `frozen` IS THE WHOLE ANSWER TO "A FIELD PVE WILL NOT ACCEPT ON WRITE". Comparing one can only
|
|
156
|
+
* plan an update that no write can satisfy: work reported on every run, forever.
|
|
157
|
+
*/
|
|
158
|
+
export declare const same: <T>(declared: T | undefined, live: T, frozen?: boolean) => boolean;
|
|
159
|
+
/**
|
|
160
|
+
* A declared autoscaler hint, or nothing when it is zero.
|
|
161
|
+
*
|
|
162
|
+
* ⚠️ A DECLARED ZERO IS "NO FLOOR, NO HINT", AND IT IS DELIBERATELY LEFT UNCOMPARED. REASONED, NOT
|
|
163
|
+
* MEASURED: all four TB4 pools carry null for both `pg_num_min` and `target_size`, and finding
|
|
164
|
+
* out for certain would mean writing to the cluster. Ceph CLEARS both when they are set to 0 and
|
|
165
|
+
* `osd pool get all` then omits the key, which PVE reports as null and this pair reads as UNSET
|
|
166
|
+
* — so a declared 0 would compare 0 against -1 on every plan, forever. Mapping 0 to UNSET
|
|
167
|
+
* instead would be a guess in the OTHER direction, and wrong the same way if Ceph does report
|
|
168
|
+
* the zero. Neither guess is made: a zero is still written, and then left unmanaged exactly like
|
|
169
|
+
* an undeclared field.
|
|
170
|
+
*/
|
|
171
|
+
export declare const hint: (value: number | undefined) => number | undefined;
|
|
172
|
+
/**
|
|
173
|
+
* The PUT body: everything POST and PUT both accept, `pg_num` excepted. `createBody` builds on it,
|
|
174
|
+
* the way storage.ts's `mutable` serves both of its forms. `name` is the path's last segment on an
|
|
175
|
+
* update and is not repeated here.
|
|
176
|
+
*
|
|
177
|
+
* ⚠️ SAFE TO RE-APPLY TO A POOL THAT ALREADY MATCHES, which reconcile relies on. MEASURED in
|
|
178
|
+
* PVE::Ceph::Tools::set_pool (Tools.pm:293-300): it reads `osd pool get all` first and SKIPS
|
|
179
|
+
* every setting whose value did not change, so re-sending the declared set costs one mon read.
|
|
180
|
+
* ⛔ BUT A SETTING THAT DID CHANGE AND CANNOT BE APPLIED KILLS THE WHOLE PUT. Tools.pm:310 dies
|
|
181
|
+
* with "Could not set: <fields>" if any parameter is left unapplied, so a pool carrying Ceph's
|
|
182
|
+
* `nosizechange` fails an entire update over one field. That is why the `no*` flags are reported
|
|
183
|
+
* and why `matches` refuses to diff what they freeze.
|
|
184
|
+
*
|
|
185
|
+
* ⚠️ AN UNDECLARED FIELD IS NEITHER SENT NOR COMPARED — storage.ts's rule, and the opposite of
|
|
186
|
+
* Proxmox.Pool's. Undeclared means UNMANAGED here, because a pool has no one set of defaults to
|
|
187
|
+
* fall back on: PVE's POST fills in size 3, min_size 2, application rbd and pg_autoscale_mode
|
|
188
|
+
* 'warn' (MEASURED in createpool), while Ceph's own defaults — which is what every pool on TB4
|
|
189
|
+
* was actually born with — put pg_autoscale_mode at 'on'. Guessing either set would rewrite a
|
|
190
|
+
* pool somebody tuned by hand.
|
|
191
|
+
*
|
|
192
|
+
* ⛔ THERE IS NO `delete=` HERE AND THERE MUST NOT BE ONE. `setpool` is not a SectionConfig write:
|
|
193
|
+
* it turns each parameter into an `osd pool set`, and Ceph has no "unset" for size, min_size or
|
|
194
|
+
* crush_rule. metric-server-form.ts clears an undeclared optional because status.cfg supports
|
|
195
|
+
* it; the same move here would send a parameter PVE has no verb for.
|
|
196
|
+
*
|
|
197
|
+
* ⚠️ `target_size` IS BYTES ON THE WIRE IN BOTH DIRECTIONS, WHICH IS WHY THE PROP IS A NUMBER AND
|
|
198
|
+
* NOT PVE's `1T` STRING. MEASURED: the API parses the string with PVE::JSONSchema::parse_size
|
|
199
|
+
* and stores Ceph's `target_size_bytes` (Pool.pm:474-477 for POST, 683-686 for PUT), and
|
|
200
|
+
* `GET .../status` hands that integer back under the name `target_size` (Pool.pm:878). Run on
|
|
201
|
+
* n2, `parse_size('1099511627776')` returns 1099511627776 — bare digits are bytes, no
|
|
202
|
+
* multiplier — so a byte count survives the round trip exactly, while a declared `1T` would read
|
|
203
|
+
* back as 1099511627776 and diff against itself on every plan, forever.
|
|
204
|
+
*/
|
|
205
|
+
export declare const updateBody: (props: CephPoolProps) => Record<string, string>;
|
|
206
|
+
/**
|
|
207
|
+
* The POST body.
|
|
208
|
+
*
|
|
209
|
+
* ⛔ `add_storages=0` IS SENT EXPLICITLY RATHER THAN LEFT OUT, AND THE DEFAULT IS NOT WHAT IT LOOKS
|
|
210
|
+
* LIKE. MEASURED at Pool.pm:481 — `$add_storages = 1 if $ec && !defined($add_storages)` — so for
|
|
211
|
+
* an erasure-coded pool PVE turns it ON when the parameter is absent. A pool created with it
|
|
212
|
+
* writes a NEW SECTION INTO storage.cfg that this graph does not own: `Proxmox.Storage` would
|
|
213
|
+
* not know about it, `alchemy destroy` would leave it behind, and the first person to notice
|
|
214
|
+
* would be whoever found a storage nobody declared.
|
|
215
|
+
*/
|
|
216
|
+
export declare const createBody: (props: CephPoolProps) => Record<string, string>;
|
|
217
|
+
//# sourceMappingURL=ceph-pool-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ceph-pool-form.d.ts","sourceRoot":"","sources":["../../src/proxmox/ceph-pool-form.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGhD,MAAM,WAAW,aAAc,SAAQ,UAAU;IAC/C;;;;;;OAMG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,uFAAuF;IACvF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+FAA+F;IAC/F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oGAAoG;IACpG,iBAAiB,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,MAAM,CAAC;IAC1C;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,KAAK,CAAC;IACvC,qGAAqG;IACrG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;;;;OAQG;IACH,gBAAgB,CAAC,EAAE,KAAK,CAAC;IACzB,YAAY,CAAC,EAAE,KAAK,CAAC;CACtB;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oGAAoG;IACpG,UAAU,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,MAAM,UAAW,aAAa,WAAkD,CAAC;AAC9F,eAAO,MAAM,UAAU,UAAW,aAAa,WAAoC,CAAC;AAEpF;;;;;GAKG;AACH,eAAO,MAAM,KAAK,KAAK,CAAC;AAExB,qGAAqG;AACrG,eAAO,MAAM,YAAY,UAAW,OAAO,WACoC,CAAC;AAEhF;;;;;GAKG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,YAAY,CAAC,GAAG,SAAS,QAAQ,CAAC,8BACD,CAAC;AAExD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,IAAI,UAAW,MAAM,GAAG,SAAS,uBAAsC,CAAC;AASrF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,UAAU,UAAW,aAAa,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAQrE,CAAC;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,UAAW,aAAa,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAMrE,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Waiting for Ceph to finish, which is a concern of its own.
|
|
3
|
+
*
|
|
4
|
+
* ★ SPLIT OUT OF ceph-pool.ts FOR THE 250-LINE CAP, and the seam is real: every other PVE family
|
|
5
|
+
* in this package writes and reads back in one breath, because PVE's config endpoints are
|
|
6
|
+
* synchronous. Ceph's are not — a pool delete returns before the PGs are gone — so this file is
|
|
7
|
+
* the polling that difference forces, and ceph-pool.ts is the declaration.
|
|
8
|
+
*/
|
|
9
|
+
import * as Effect from 'effect/Effect';
|
|
10
|
+
import type { CephPoolAttributes, CephPoolProps } from './ceph-pool-form.ts';
|
|
11
|
+
import type { PveRequirements } from './resource.ts';
|
|
12
|
+
/**
|
|
13
|
+
* ⚠️ THE DEPENDENCIES ARE PASSED IN RATHER THAN IMPORTED, and that is what made this file
|
|
14
|
+
* separable at all. `collection`, `object` and `ops.read` are module-level bindings in
|
|
15
|
+
* ceph-pool.ts; importing them back would be a runtime cycle, and duplicating them would be two
|
|
16
|
+
* definitions of one path. Passing them is the pattern zfs-pool-write.ts already uses.
|
|
17
|
+
*/
|
|
18
|
+
export type PoolRead = (props: CephPoolProps) => Effect.Effect<CephPoolAttributes | undefined, never, PveRequirements>;
|
|
19
|
+
/**
|
|
20
|
+
* ⛔ THE GUARD ON THE CREATE. `read` folds a 403, a timeout and a node that is down into the same
|
|
21
|
+
* `undefined` as a pool that is genuinely missing — so "absent" is not evidence. This asks a
|
|
22
|
+
* second, different question: does the cluster LIST this pool? A failure is left to propagate
|
|
23
|
+
* rather than folded, so "I could not ask" fails the deploy instead of creating over live data.
|
|
24
|
+
* An empty list is trusted: the collection 403s when the role is too narrow rather than
|
|
25
|
+
* filtering, so `[]` really does mean a cluster with no pools.
|
|
26
|
+
*/
|
|
27
|
+
export declare const confirmAbsent: (props: CephPoolProps, collection: string, describe: string) => Effect.Effect<void, Error, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
28
|
+
/**
|
|
29
|
+
* Wait for the cluster to agree, then answer the last thing it said.
|
|
30
|
+
*
|
|
31
|
+
* ★ IT POLLS `matches`, THE SAME PREDICATE `diff` USES, so "settled" has exactly one definition in
|
|
32
|
+
* this file. Polling `/nodes/{node}/tasks/{upid}/status` instead would read the worker's own
|
|
33
|
+
* exit status, but it needs `Sys.Audit` on the node for a token that is NOT the one that started
|
|
34
|
+
* the task — every `pve` call mints a fresh token, so the task's owner check cannot pass.
|
|
35
|
+
* ⚠️ SHORT POLLS, LOW CAP: 2s apart, 30 tries, about a minute. A pool create takes seconds; one
|
|
36
|
+
* still unsettled after a minute is a fault to surface, not a wait to lengthen.
|
|
37
|
+
*/
|
|
38
|
+
export declare const settle: (props: CephPoolProps, read: PoolRead, done: (live: CephPoolAttributes | undefined) => boolean) => Effect.Effect<CephPoolAttributes | undefined, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
39
|
+
//# sourceMappingURL=ceph-pool-settle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ceph-pool-settle.d.ts","sourceRoot":"","sources":["../../src/proxmox/ceph-pool-settle.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE7E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,CACrB,KAAK,EAAE,aAAa,KACjB,MAAM,CAAC,MAAM,CAAC,kBAAkB,GAAG,SAAS,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC;AAE3E;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,UAAW,aAAa,cAAc,MAAM,YAAY,MAAM,qFAcrF,CAAC;AAEJ;;;;;;;;;GASG;AACH,eAAO,MAAM,MAAM,UACV,aAAa,QACd,QAAQ,QACR,CAAC,IAAI,EAAE,kBAAkB,GAAG,SAAS,KAAK,OAAO,+GASrD,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Proxmox.CephPool` — a Ceph RADOS pool, declared. The richest Ceph family and the one a stack is
|
|
3
|
+
* most likely to reach for, because every RBD disk in the cluster lives inside one.
|
|
4
|
+
*
|
|
5
|
+
* ⛔ IT DOES NOT USE `pveHandlers`, AND THE REASON IS MEASURED RATHER THAN STYLISTIC: THE READ AND
|
|
6
|
+
* THE WRITES SIT AT DIFFERENT PATHS. `GET /nodes/{node}/ceph/pool/{name}` is `poolindex` and
|
|
7
|
+
* answers `[{"name":"status"}]` — an INDEX OF CHILDREN, not the pool. The pool is one level
|
|
8
|
+
* further down, at `.../status` (`getpool`), which accepts no PUT and no DELETE. A `PveSpec`
|
|
9
|
+
* carries ONE `path` for read, update and delete, so this family cannot be spelled in it. `path`
|
|
10
|
+
* below is therefore the READ path and nothing else consumes it: `ops.reconcile` and
|
|
11
|
+
* `ops.destroy` go unused and the two write handlers name their own path. acl.ts is the
|
|
12
|
+
* precedent for hand-writing the five handlers over `pveOperations`.
|
|
13
|
+
*
|
|
14
|
+
* ⛔ A CREATE FIRED AT A POOL THAT ALREADY EXISTS IS NOT A NO-OP, IT IS A PG MERGE — the second ⛔
|
|
15
|
+
* in ceph-pool-form.ts has the evidence, and `confirmAbsent` below is the guard that stops it.
|
|
16
|
+
*
|
|
17
|
+
* ⛔ EVERY WRITE HERE IS A FORKED WORKER. createpool, setpool and destroypool each return a UPID
|
|
18
|
+
* string, so HTTP 200 means "the task started" — an immediate read-back can see a pool that does
|
|
19
|
+
* not exist yet, or values that have not landed. `settle` waits for the cluster to agree.
|
|
20
|
+
*
|
|
21
|
+
* ⛔ THE FOUR POOLS ON TB4 INCLUDE CEPH'S OWN, AND NONE OF THEM MAY BE ADOPTED. Measured
|
|
22
|
+
* 2026-09-13: `.mgr` (application `mgr`, created and owned by the manager daemons),
|
|
23
|
+
* `cephfs-tb4_data` and `cephfs-tb4_metadata` (the two halves of the `cephfs-tb4` filesystem —
|
|
24
|
+
* destroying either destroys the filesystem), and `cephtb4`, the rbd pool the guests sit on.
|
|
25
|
+
* `list` answers empty like every resource in this package, so adoption stays an explicit act;
|
|
26
|
+
* the `applications` attribute is reported so that a plan SHOWS which of those a declaration hit.
|
|
27
|
+
*
|
|
28
|
+
* ⚠️ DELETING A POOL DESTROYS EVERY OBJECT IN IT AND CEPH DOES NOT ASK TWICE. Neither `force` nor
|
|
29
|
+
* `remove_storages` is ever sent, so PVE's refusal while a storage on the pool still lists RBD
|
|
30
|
+
* images is left standing. That refusal does NOT cover an RBD user outside storage.cfg.
|
|
31
|
+
*
|
|
32
|
+
* ⚠️ PRIVILEGES, AND THE WRITE LANE IS A BIG ASK. Read is `Sys.Audit` OR `Datastore.Audit` on `/`
|
|
33
|
+
* (PVEAuditor holds both, so the mount's `read` role covers read, diff and the guard). Create,
|
|
34
|
+
* update and delete each check `Sys.Modify` on `/` — there is no `/ceph` object to scope to, so
|
|
35
|
+
* granting it also buys datacenter options and every other cluster-wide write. Widen
|
|
36
|
+
* deliberately, the way `Pool.Allocate` was widened for `Proxmox.Pool`.
|
|
37
|
+
*/
|
|
38
|
+
import { Resource } from 'alchemy';
|
|
39
|
+
import * as Provider from 'alchemy/Provider';
|
|
40
|
+
import { type CephPoolAttributes, type CephPoolProps } from './ceph-pool-form.ts';
|
|
41
|
+
import { type PveRequirements } from './resource.ts';
|
|
42
|
+
export type { CephPoolAttributes, CephPoolProps };
|
|
43
|
+
export interface ProxmoxCephPool extends Resource<'Proxmox.CephPool', CephPoolProps, CephPoolAttributes, never, PveRequirements> {
|
|
44
|
+
}
|
|
45
|
+
/** ★ `retain` by default — a pool holding objects cannot be rebuilt. See the ★ in resource.ts. */
|
|
46
|
+
export declare const ProxmoxCephPool: import("alchemy").ResourceClass<ProxmoxCephPool>;
|
|
47
|
+
export declare const ProxmoxCephPoolProvider: () => import("effect/Layer").Layer<Provider.Provider<ProxmoxCephPool>, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
48
|
+
//# sourceMappingURL=ceph-pool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ceph-pool.d.ts","sourceRoot":"","sources":["../../src/proxmox/ceph-pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,aAAa,EASnB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,KAAK,eAAe,EAA+B,MAAM,eAAe,CAAC;AAGlF,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,CAAC;AAElD,MAAM,WAAW,eAAgB,SAAQ,QAAQ,CAC/C,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,kGAAkG;AAClG,eAAO,MAAM,eAAe,kDAE1B,CAAC;AAiEH,eAAO,MAAM,uBAAuB,qIA4EjC,CAAC"}
|