From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [45.144.208.40]) by lore.proxmox.com (Postfix) with ESMTPS id 14FC21FF0B7 for ; Fri, 16 Oct 2026 11:53:08 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 9985B214AB; Fri, 16 Oct 2026 11:53:06 +0200 (CEST) Mime-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=UTF-8 Date: Fri, 16 Oct 2026 11:53:02 +0200 Message-Id: To: "Fiona Ebner" , Subject: Re: [PATCH ha-manager v4 13/27] crm command: support JSON-style migrate command From: "Daniel Kral" X-Mailer: aerc 0.22.0-10-g6373ac9d2179-dirty References: <20261015125112.174954-1-f.ebner@proxmox.com> <20261015125112.174954-14-f.ebner@proxmox.com> In-Reply-To: <20261015125112.174954-14-f.ebner@proxmox.com> X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1792144382386 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.775 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust 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: OXGJ3JWTGWDGDO2U2QHR36WCLPRSJCS4 X-Message-ID-Hash: OXGJ3JWTGWDGDO2U2QHR36WCLPRSJCS4 X-MailFrom: d.kral@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 VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: On Thu Oct 15, 2026 at 2:50 PM CEST, Fiona Ebner wrote: > To pass along opaque migration options from the resource-specific > migration API endpoint to the resource plugin implementation, the > options will be encoded as JSON. Instead of just adding an extra > positional argument with those, support having the full CRM command be > recorded as JSON in the queued commands file. > > Signed-off-by: Fiona Ebner > --- > > Changes in v4: > * Encode JSON with canonical =3D> 1. > * Improve error messages. > > src/PVE/HA/Manager.pm | 57 +++++++++++++++++++++++++++++++++++++------ > 1 file changed, 49 insertions(+), 8 deletions(-) > > diff --git a/src/PVE/HA/Manager.pm b/src/PVE/HA/Manager.pm > index c28d7fa..4e7fab5 100644 > --- a/src/PVE/HA/Manager.pm > +++ b/src/PVE/HA/Manager.pm > @@ -711,6 +711,28 @@ my sub check_resource_motion_valid { > return 1; > } > =20 > +my sub handle_crm_motion_command { > + my ($self, $task, $sid, $node, $options) =3D @_; > + > + my $cmd =3D "$task $sid $node"; > + > + if (defined($options) && scalar(keys($options->%*))) { > + $cmd .=3D ' ' . JSON::to_json($options, { canonical =3D> 1 }); > + } > + > + my ($haenv, $ms, $ns, $sc, $ss) =3D $self->@{qw(haenv ms ns sc ss)}; nit: $ms, $ns, and $sc are unused here > + > + if (my $sd =3D $ss->{$sid}) { > + if (check_resource_motion_valid($self, $node, $sd, "ignore crm c= ommand '$cmd'")) { > + $self->queue_resource_motion($cmd, $task, $sid, $node, $opti= ons); > + } > + } else { > + $haenv->log('err', "crm command error - no such service: $cmd"); > + } > + > + return; > +} > + > # read new crm commands and save them into crm master status > sub update_crm_commands { > my ($self) =3D @_; > @@ -722,16 +744,35 @@ sub update_crm_commands { > foreach my $cmd (split(/\n/, $cmdlist)) { > chomp $cmd; > =20 > - if ($cmd =3D~ m/^(migrate|relocate)\s+(\S+)\s+(\S+)$/) { > - my ($task, $sid, $node) =3D ($1, $2, $3); > - if (my $sd =3D $ss->{$sid}) { > - if (check_resource_motion_valid($self, $node, $sd, "igno= re crm command '$cmd'")) { > - $self->queue_resource_motion($cmd, $task, $sid, $nod= e, {}); > - } > - } else { > - $haenv->log('err', "crm command error - no such service:= $cmd"); > + if ($cmd =3D~ m/^{/) { > + # New-style JSON-encoded command. Currently only 'migrate' i= s supported, allowing for > + # additional options. > + > + my $command_info =3D eval { JSON::decode_json($cmd) }; > + if (my $err =3D $@) { > + $haenv->log('err', "unable to decode JSON command: '$cmd= ' - $err"); > + next; > } > =20 > + my $kind =3D $command_info->{kind}; > + if (!defined($kind)) { > + $haenv->log('err', "unable to handle JSON command withou= t 'kind': '$cmd'"); > + } elsif ($kind eq 'migrate') { > + my ($sid, $node, $options) =3D $command_info->@{qw(sid n= ode options)}; > + if (!defined($sid) || !defined($node)) { > + $haenv->log('err', "'migrate' JSON command without s= id or node"); > + next; > + } > + handle_crm_motion_command($self, $kind, $sid, $node, $op= tions); nit: only noticed now, but 'kind' should probably also be 'task' here as we call it like that everywhere else... Even though with variants 'kind' is more common, so no hard feelings here. > + } else { > + $haenv->log('err', "unable to handle unknown JSON comman= d: '$cmd'"); > + } > + > + next; > + } > + > + if ($cmd =3D~ m/^(migrate|relocate)\s+(\S+)\s+(\S+)$/) { nit: should probably be an elsif here? > + handle_crm_motion_command($self, $1, $2, $3, {}); > } elsif ($cmd =3D~ m/^stop\s+(\S+)\s+(\S+)$/) { > my ($sid, $timeout) =3D ($1, $2); > if (my $sd =3D $ss->{$sid}) {