On Tue, Aug 26, 2025 at 4:38 AM Daniel Kral <d.k...@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 <tho...@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 via email to