From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 41A851FF136 for ; Mon, 18 May 2026 16:27:49 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id B09871562C; Mon, 18 May 2026 16:27:48 +0200 (CEST) Mime-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=UTF-8 Date: Mon, 18 May 2026 16:27:15 +0200 Message-Id: Subject: Re: [PATCH datacenter-manager v3 04/12] subscription: api: add key pool and node status endpoints From: "Lukas Wagner" To: "Thomas Lamprecht" , X-Mailer: aerc 0.21.0-0-g5549850facc2-dirty References: <20260515074623.766766-1-t.lamprecht@proxmox.com> <20260515074623.766766-5-t.lamprecht@proxmox.com> In-Reply-To: <20260515074623.766766-5-t.lamprecht@proxmox.com> X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1779114422983 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.054 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: XPMH4LGSZHKXAI4IA62Z6FLIXLW7FERA X-Message-ID-Hash: XPMH4LGSZHKXAI4IA62Z6FLIXLW7FERA X-MailFrom: l.wagner@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox Datacenter Manager development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: On Fri May 15, 2026 at 9:43 AM CEST, Thomas Lamprecht wrote: > +/// Compute a proposed mapping of unused pool keys to nodes without an a= ctive subscription. > +/// > +/// Returns the plan plus snapshots of the inputs (pool digest and a has= h of the consulted > +/// node-status). The plan is committed by `bulk_assign` and rejected th= ere if either snapshot no > +/// longer matches the live state, so an operator never silently applies= a plan that drifted > +/// between preview and commit. > +/// > +/// `PRIV_SYS_MODIFY` is required to *preview* the plan; the actual comm= it performed by > +/// `bulk_assign` additionally drops proposals on any remote the caller = cannot > +/// `PRIV_RESOURCE_MODIFY`, so an audit-only-on-a-remote operator can se= e the suggestion but the > +/// write never lands there. > +async fn auto_assign(rpcenv: &mut dyn RpcEnvironment) -> Result { > + let node_statuses =3D collect_node_status(FRESH_NODE_STATUS_MAX_AGE,= rpcenv).await?; > + let (config, keys_digest) =3D pdm_config::subscriptions::config()?; > + let assignments =3D compute_proposals(&config, &node_statuses); > + Ok(AutoAssignProposal { > + assignments, > + keys_digest, > + node_status_digest: hash_node_status(&node_statuses), > + }) > +} > + > +#[api( > + input: { > + properties: { > + proposal: { type: AutoAssignProposal }, > + }, Brief comment, since I spotted this in the API viewer: We should probably document that this API endpoint only works when submitting the payload as `application/json`. Our `application/x-www-form-urlencoded` code paths do not support submitting complex data structures, as far as I know we only support flat key-value pairs and basic arrays. This is a limitation that we really should lift at some point, either by adding support for parsing urlencoded nested objects, or by phasing it out completely, only allowing JSON. As far as I know it is relatively rare to support both, so I can sympathize with dropping support at some point, but I'm well aware that this is a huge breaking change for API users, which would need to be done very carefully after a long deprecation period. Parsing complex data structures from urlencoded bodies is IIRC a bit awkward, since one has to flatten everything into key-value pairs, for which there is no clear standard/convention (different frameworks use different approaches -- so this is also not *that* nice for API users). At some point before the PDM release we briefly talked about (can't remember if you were part of the discussion) not supporting the x-www-form-urlencoded API at all in PDM in the first place, and IIRC there was a consensus that this could be a good idea, but unfortunately it didn't really go anywhere. Maybe a topic for PDM 2.0? > + }, > + returns: { > + type: Array, > + description: "Assignments that were actually persisted.", > + items: { type: ProposedAssignment }, > + }, > + access: { > + permission: &Permission::Privilege(&["system"], PRIV_SYS_MODIFY,= false), > + }, > +)] > +/// Apply a proposal previously returned by `auto_assign`. > +/// > +/// Rejects with 409 if the pool config digest has moved or the live nod= e-status hash differs > +/// from what the proposal was computed against; the caller is expected = to refresh the proposal > +/// and retry. Per-remote `PRIV_RESOURCE_MODIFY` is checked inside the h= andler so an audit-only > +/// caller's previously-rendered preview cannot be applied on their beha= lf. > +async fn bulk_assign( > + proposal: AutoAssignProposal, > + rpcenv: &mut dyn RpcEnvironment, > +) -> Result, Error> { > + let auth_id: Authid =3D rpcenv > + .get_auth_id() > + .context("no authid available")? > + .parse()?; > +