all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Thomas Skinner <thomas@atskinner.net>
To: Daniel Kral <d.kral@proxmox.com>
Cc: Proxmox VE development discussion <pve-devel@lists.proxmox.com>
Subject: Re: [pve-devel] [PATCH ha-manager 2/2] add api getter/setter for node maintenance mode
Date: Tue, 26 Aug 2025 11:00:55 -0500	[thread overview]
Message-ID: <CALn9RMdGbTp1H1cc_3LHhke-ui30J9Vqf9ro3DXYsKt5L04hnw@mail.gmail.com> (raw)
In-Reply-To: <DCC9BWHL0DVV.36JQME5F53EZV@proxmox.com>

On Tue, Aug 26, 2025 at 4:38 AM Daniel Kral <d.kral@proxmox.com> wrote:
>
> Thanks for working on this!
>
> I have added some comments inline. Please run `make tidy` on this patch
> (and other patches for that sake) to automatically reformat the code.
>
> See [0] for more information.
>
> [0] https://git.proxmox.com/?p=proxmox-perltidy.git;a=summary

I thought I did this on all of them, but I will do it again just in case.

>
> On Mon Aug 25, 2025 at 6:11 AM CEST, Thomas Skinner wrote:
> > Signed-off-by: Thomas Skinner <thomas@atskinner.net>
> > ---
> >  debian/pve-ha-manager.install |   1 +
> >  src/PVE/API2/HA/Makefile      |   2 +-
> >  src/PVE/API2/HA/Nodes.pm      | 176 ++++++++++++++++++++++++++++++++++
> >  3 files changed, 178 insertions(+), 1 deletion(-)
> >  create mode 100644 src/PVE/API2/HA/Nodes.pm
> >
> > diff --git a/debian/pve-ha-manager.install b/debian/pve-ha-manager.install
> > index 2e6b7d5..ec85037 100644
> > --- a/debian/pve-ha-manager.install
> > +++ b/debian/pve-ha-manager.install
> > @@ -15,6 +15,7 @@
> >  /usr/share/man/man8/pve-ha-crm.8.gz
> >  /usr/share/man/man8/pve-ha-lrm.8.gz
> >  /usr/share/perl5/PVE/API2/HA/Groups.pm
> > +/usr/share/perl5/PVE/API2/HA/Nodes.pm
> >  /usr/share/perl5/PVE/API2/HA/Resources.pm
> >  /usr/share/perl5/PVE/API2/HA/Rules.pm
> >  /usr/share/perl5/PVE/API2/HA/Status.pm
> > diff --git a/src/PVE/API2/HA/Makefile b/src/PVE/API2/HA/Makefile
> > index 86c1013..45d714b 100644
> > --- a/src/PVE/API2/HA/Makefile
> > +++ b/src/PVE/API2/HA/Makefile
> > @@ -1,4 +1,4 @@
> > -SOURCES=Resources.pm Groups.pm Rules.pm Status.pm
> > +SOURCES=Resources.pm Groups.pm Nodes.pm Rules.pm Status.pm
> >
> >  .PHONY: install
> >  install:
> > diff --git a/src/PVE/API2/HA/Nodes.pm b/src/PVE/API2/HA/Nodes.pm
> > new file mode 100644
> > index 0000000..2afc3a3
> > --- /dev/null
> > +++ b/src/PVE/API2/HA/Nodes.pm
> > @@ -0,0 +1,176 @@
> > +package PVE::API2::HA::Nodes::Nodeinfo;
>
> Why use two packages in this file here? AFAICT this seems like it is
> copy-pasted from PVE::API2::Nodes and could very well be a single
> package as that's much cleaner than having two packages in one file.

Yes, I did use that file as the source for this package. It could very
well be one package, especially if it was placed elsewhere in the API.
I was trying to mirror the behavior of the existing nodes dir in the
API.

> Also it seems like we do that seldomly:
>
> # rg -o -c -g "*.pm" "^package" . | awk -F: '{if ($2 > 1){print $1}}'
> ./pve-guest-common/src/PVE/ReplicationConfig.pm
> ./pve-buildpkg/PVE/BuildPKG/Tools.pm
> ./pve-storage/src/PVE/Storage/ESXiPlugin.pm
> ./pve-manager/PVE/API2/Nodes.pm
> ./pve-manager/PVE/CLI/pve_network_interface_pinning.pm
> ./pve-firewall/src/PVE/API2/Firewall/VM.pm
> ./pve-firewall/src/PVE/API2/Firewall/Rules.pm
> ./pve-firewall/src/PVE/API2/Firewall/IPSet.pm
> ./pve-firewall/src/PVE/API2/Firewall/Aliases.pm
>
> > +
> > +use strict;
> > +use warnings;
> > +
> > +use PVE::SafeSyslog;
> > +use PVE::Tools qw(extract_param);
> > +use PVE::Cluster qw(cfs_read_file cfs_write_file);
> > +use PVE::HA::Config;
> > +use HTTP::Status qw(:constants);
> > +use Storable qw(dclone);
> > +use PVE::JSONSchema qw(get_standard_option);
> > +use PVE::RPCEnvironment;
>
> are extract_param, cfs_{read,write}_file, dclone used?
>
> nit: either way, use statements should follow the dependencies style
> guide [1], i.e. something like the following:
>
>
> use HTTP::Status qw(:constants);
> use Storable qw(dclone);
>
> use PVE::Cluster qw(cfs_read_file cfs_write_file);
> use PVE::JSONSchema qw(get_standard_option);
> use PVE::RPCEnvironment;
> use PVE::SafeSyslog;
> use PVE::Tools qw(extract_param);
>
> use PVE::HA::Config;
>
>
> [1] https://pve.proxmox.com/wiki/Perl_Style_Guide#Dependencies

I will clean these up in a v2 patch.

> > +
> > +use PVE::RESTHandler;
> > +
> > +use base qw(PVE::RESTHandler);
> > +
> > +__PACKAGE__->register_method({
> > +    name => 'index',
> > +    path => '',
> > +    method => 'GET',
> > +    permissions => { user => 'all' },
> > +    description => "Node index.",
> > +    parameters => {
> > +        additionalProperties => 0,
> > +        properties => {
> > +            node => get_standard_option('pve-node'),
> > +        },
> > +    },
> > +    returns => {
> > +        type => 'array',
> > +        items => {
> > +            type => "object",
> > +            properties => {},
> > +        },
> > +        links => [{ rel => 'child', href => "{name}" }],
> > +    },
> > +    code => sub {
> > +        my ($param) = @_;
> > +
> > +        my $result = [
> > +            { name => 'maintenance' },
> > +        ];
> > +
> > +        return $result;
> > +    },
> > +});
>
> Hm, we do that differently around the API endpoints, but at least for
> the HA-related API endpoints we don't list the subdirectories, but
> output the information in the entity itself, e.g. for HA resources:
>
>
> + ha
> |
> +-+ resources     - GET    List HA resources.
>   +               - POST   Create a new HA resource.
>   |
>   +-+ {sid}       - GET    Read resource configuration.
>     +             - PUT    Update resource configuration.
>     +             - DELETE Delete resource configuration.
>     |
>     +-+ migrate   - POST   Request resource migration to another node.
>       + relocate  - POST   Request resource relocation to another node.
>
>
> Of course, it doesn't make sense for a POST/DELETE API endpoint for
> nodes, but it could still be useful to make the
> GET /cluster/ha/nodes/{node} route informational, e.g., output the
> current maintenance state and possibly other information about nodes
> there.
>
> So something like this:
>
> + ha
> |
> +-+ nodes         - GET    List HA nodes.
>   |
>   +-+ {node}      - GET    Read HA node information.
>
> ...
>
> > +
> > +__PACKAGE__->register_method({
> > +    name => 'maintenance_state',
> > +    path => 'maintenance',
> > +    method => 'GET',
> > +    permissions => { user => 'all' },
> > +    description => "Get the node maintenance state.",
> > +    permissions => {
> > + check => ['perm', '/', [ 'Sys.Audit' ]],
> > +    },
> > +    parameters => {
> > +        additionalProperties => 0,
> > +        properties => {
> > +            node => get_standard_option('pve-node'),
> > +        },
> > +    },
> > +    returns => {
> > +        type => "object",
> > +        properties => {},
> > +    },
> > +    code => sub {
> > +        my ($param) = @_;
> > +
> > + my $status = PVE::HA::Config::read_manager_status();
> > +
> > + my $data = {
> > +            request => $status->{node_request}->{$param->{node}},
> > +            status => $status->{node_status}->{$param->{node}},
> > +        };
> > +
> > + return $data;
> > +    },
> > +});
>
> I think the GET /cluster/ha/nodes/{node}/maintenance API endpoint should
> be integrated into the GET /cluster/ha/nodes/{node} instead as putting a
> node in/out of maintenance mode is only an action, but the state could
> be more than just the node being in maintenance mode.
>
> On second thought, is this API endpoint even used at all in the patch
> series?

The API isn't specifically used here, but we have found it useful in
our local branch to have it so that we can query the transition of the
state of a host directly. I figured someone else might find it useful
as well, so I left it in here. If it should be removed, I can remove
it in the v2 series.

> > +
> > +__PACKAGE__->register_method ({
> > +    name => 'maintenance_set',
> > +    protected => 1,
> > +    path => 'maintenance',
> > +    method => 'POST',
> > +    description => "Change the node-maintenance request state.",
> > +    permissions => {
> > + check => ['perm', '/', [ 'Sys.Console' ]],
> > +    },
> > +    parameters => {
> > + additionalProperties => 0,
> > + properties => {
> > +     node => get_standard_option('pve-node'),
> > +     disable => {
> > + description => "Requests disabling or enabling maintenance-mode.",
> > + type => 'boolean',
>
> Hm, I see that that part is from the CLI handler, but there `disable` is
> only used for two different subcommands `enable` and `disable`, so
> wouldn't it be neater to have two API endpoints for that instead? Like
> for example the API endpoints for VM/container start/stop/..., e.g.
>
> - POST enable_maintenance
> - POST disable_maintenance

For this, I was mirroring the existing underlying API calls as listed
below, but I agree that enable/disable would be more clear. I'll put
that in the v2 series.

> Either way, the CLI command should then use this API handler instead of
> the CLI handler to have the logic at one place only, i.e. in
> ha_manager.pm
>
> 'node-maintenance' => {
>     enable => [__PACKAGE__, 'node-maintenance-set', ['node'], { disable => 0 }],
>     disable => [__PACKAGE__, 'node-maintenance-set', ['node'], { disable => 1 }],
> },
>
> should then be changed to:
>
> 'node-maintenance' => {
>     enable => ['PVE::API2::HA::Nodes', 'enable_maintenance', ['node']],
>     disable => ['PVE::API2::HA::Nodes', 'disable_maintenance', ['node']],
> },

Will update this in the v2 series.

>
> > +     },
> > + },
> > +    },
> > +    returns => { type => 'null' },
> > +    code => sub {
> > + my ($param) = @_;
> > +
> > + PVE::Cluster::check_node_exists($param->{node});
> > +
> > + my $cmd = $param->{disable} ? 'disable-node-maintenance' : 'enable-node-maintenance';
> > + PVE::HA::Config::queue_crm_commands("$cmd $param->{node}");
> > +
> > + return undef;
> > +    }
> > +});
> > +
> > +package PVE::API2::HA::Nodes;
> > +
> > +use strict;
> > +use warnings;
> > +
> > +use PVE::RESTHandler;
> > +use PVE::JSONSchema qw(get_standard_option);
> > +use PVE::RPCEnvironment;
> > +use PVE::Cluster;
> > +
> > +use base qw(PVE::RESTHandler);
> > +
> > +__PACKAGE__->register_method({
> > +    subclass => "PVE::API2::HA::Nodes::Nodeinfo",
> > +    path => '{node}',
> > +});
> > +
> > +__PACKAGE__->register_method({
> > +    name => 'index',
> > +    path => '',
> > +    method => 'GET',
> > +    permissions => { user => 'all' },
> > +    description => "Cluster HA node index.",
>
> nit: for consistency's sake with the HA API endpoints, this could e.g.
> be "List HA nodes.".. even though they don't really belong to the HA..
>
> > +    parameters => {
> > +        additionalProperties => 0,
> > +        properties => {},
> > +    },
> > +    returns => {
> > +        type => 'array',
> > +        items => {
> > +            type => "object",
> > +            properties => {
> > +                node => get_standard_option('pve-node'),
> > +            },
> > +        },
> > +        links => [{ rel => 'child', href => "{node}" }],
> > +    },
> > +    code => sub {
> > +        my ($param) = @_;
> > +
> > +        my $rpcenv = PVE::RPCEnvironment::get();
> > +        my $authuser = $rpcenv->get_user();
> > +
> > +        my $clinfo = PVE::Cluster::get_clinfo();
> > +        my $res = [];
> > +
> > +        my $nodelist = PVE::Cluster::get_nodelist();
> > +        my $members = PVE::Cluster::get_members();
> > +        my $rrd = PVE::Cluster::rrd_dump();
> > +
> > +        foreach my $node (@$nodelist) {
> > +            my $can_audit = $rpcenv->check($authuser, "/nodes/$node", ['Sys.Audit'], 1);
>
> $can_audit is never used here and the check has $noerr set, so it
> doesn't do anything..
>
> The more I think about it, I'm somewhat inclined to register the whole
> subclass under /nodes/{node}/ha/... instead of /cluster/ha/nodes/... to
> not duplicate the information of a nodelist, which is provided by
> pve-cluster and there would be something wrong with the HA Manager
> anyway if those are out-of-sync and all the HA-related information can
> then live in a node-specific "ha" subdirectory.
>
> Then this API handler wouldn't be needed anymore at all.

If this is how Proxmox would like it, that makes it much easier IMO. I
wasn't sure which one to include it under, so I put it under the
cluster path since a cluster must be created to even have the HA
featureset. I'm good with either, I just need to know so that I know
what to update for a v2 series.

> > +            my $entry = { node => $node };
> > +
> > +            push @$res, $entry;
> > +        }
> > +
> > +        return $res;
> > +    },
> > +});
> > +
> > +1;
>
>

_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

  reply	other threads:[~2025-08-26 16:02 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-08-25  4:11 [pve-devel] [PATCH SERIES docs/ha-manager/manager] close #6144: add ui button + api " Thomas Skinner
2025-08-25  4:11 ` [pve-devel] [PATCH ha-manager 1/2] add additional api field for lrm_mode in status check Thomas Skinner
2025-08-25  4:11 ` [pve-devel] [PATCH manager 1/2] add api path map for node HA endpoints Thomas Skinner
2025-08-26  9:38   ` Daniel Kral
2025-08-26 10:26     ` Thomas Lamprecht
2025-08-26 11:34       ` Daniel Kral
2025-08-25  4:11 ` [pve-devel] [PATCH docs 1/1] add docs for maintenance mode buttons in UI Thomas Skinner
2025-08-26  9:44   ` Daniel Kral
2025-08-26 16:05     ` Thomas Skinner
2025-08-27  7:50       ` Daniel Kral
2025-08-25  4:11 ` [pve-devel] [PATCH ha-manager 2/2] add api getter/setter for node maintenance mode Thomas Skinner
2025-08-26  9:38   ` Daniel Kral
2025-08-26 16:00     ` Thomas Skinner [this message]
2025-08-27  7:31       ` Thomas Lamprecht
2025-08-27  8:25       ` Daniel Kral
2025-08-27  9:20         ` Thomas Lamprecht
2025-08-29 20:13         ` Thomas Skinner
2025-09-01  8:52           ` Daniel Kral
2025-08-29 20:17     ` Thomas Skinner
2025-08-25  4:11 ` [pve-devel] [PATCH manager 2/2] add UI for node maintenance enable/disable Thomas Skinner
2025-08-26 10:00   ` Daniel Kral
2025-08-26 17:34     ` Thomas Skinner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CALn9RMdGbTp1H1cc_3LHhke-ui30J9Vqf9ro3DXYsKt5L04hnw@mail.gmail.com \
    --to=thomas@atskinner.net \
    --cc=d.kral@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal