summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Hanreich <s.hanreich@proxmox.com>2025-07-16 15:08:12 +0200
committerThomas Lamprecht <t.lamprecht@proxmox.com>2025-07-17 00:10:46 +0200
commitf9c774e8b1a2ebde0207bfb826ee9c7afa47b2be (patch)
treea03f6649b81c1d5aa79d65f78f5cc5cd83daba55
parentfeaf9363e03e2bed4c8b233ecdbc123db5ebefa4 (diff)
api: fabrics: add fabricnode submodule
Provides CRUD functionality for editing nodes inside a fabric, as well as an endpoint for listing all the nodes. The URL structure is modeled after the fact that a node can only be uniquely identified by its ID as well as the ID of the fabric that contains the node. Since fabrics can be used to edit the network configuration, we require addtional Sys.Modify permissions on the node itself, since that is the permission that is currently required by other endpoints that allow modifiying the network configuration. Co-authored-by: Gabriel Goller <g.goller@proxmox.com> Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com> Link: https://lore.proxmox.com/20250716130837.585796-52-g.goller@proxmox.com
-rw-r--r--src/PVE/API2/Network/SDN/Fabrics/FabricNode.pm254
-rw-r--r--src/PVE/API2/Network/SDN/Fabrics/Makefile2
-rw-r--r--src/PVE/API2/Network/SDN/Fabrics/Node.pm6
3 files changed, 261 insertions, 1 deletions
diff --git a/src/PVE/API2/Network/SDN/Fabrics/FabricNode.pm b/src/PVE/API2/Network/SDN/Fabrics/FabricNode.pm
new file mode 100644
index 0000000..b288844
--- /dev/null
+++ b/src/PVE/API2/Network/SDN/Fabrics/FabricNode.pm
@@ -0,0 +1,254 @@
+package PVE::API2::Network::SDN::Fabrics::FabricNode;
+
+use strict;
+use warnings;
+
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::Tools qw(extract_param);
+
+use PVE::Network::SDN;
+use PVE::Network::SDN::Fabrics;
+
+use PVE::RESTHandler;
+use base qw(PVE::RESTHandler);
+
+__PACKAGE__->register_method({
+ name => 'list_nodes_fabric',
+ path => '',
+ method => 'GET',
+ permissions => {
+ description =>
+ "Only returns nodes where you have 'Sys.Audit' or 'Sys.Modify' permissions.",
+ check => ['perm', '/sdn/fabrics/{fabric_id}', ['SDN.Audit']],
+ },
+ description => "SDN Fabrics Index",
+ parameters => {
+ properties => {
+ running => {
+ type => 'boolean',
+ optional => 1,
+ description => "Display running config.",
+ },
+ pending => {
+ type => 'boolean',
+ optional => 1,
+ description => "Display pending config.",
+ },
+ fabric_id => get_standard_option('pve-sdn-fabric-id'),
+ },
+ },
+ returns => {
+ type => 'array',
+ items => {
+ type => "object",
+ properties => PVE::Network::SDN::Fabrics::node_properties(0),
+ },
+ links => [{ rel => 'child', href => "{node_id}" }],
+ },
+ code => sub {
+ my ($param) = @_;
+
+ my $fabric_id = extract_param($param, 'fabric_id');
+ my $pending = extract_param($param, 'pending');
+ my $running = extract_param($param, 'running');
+
+ my $digest;
+ my $nodes;
+
+ if ($pending) {
+ my $current_config = PVE::Network::SDN::Fabrics::config();
+ my $running_config = PVE::Network::SDN::Fabrics::config(1);
+
+ my $running_nodes = $running_config->list_nodes_fabric($fabric_id);
+
+ my $current_nodes = $current_config->list_nodes_fabric($fabric_id);
+
+ my $pending_nodes = PVE::Network::SDN::pending_config(
+ { nodes => { ids => $running_nodes } },
+ { ids => $current_nodes },
+ 'nodes',
+ );
+
+ $digest = $current_config->digest();
+ $nodes = $pending_nodes->{ids};
+ } elsif ($running) {
+ $nodes = PVE::Network::SDN::Fabrics::config(1)->list_nodes_fabric($fabric_id);
+ } else {
+ my $current_config = PVE::Network::SDN::Fabrics::config();
+
+ $digest = $current_config->digest();
+ $nodes = $current_config->list_nodes_fabric($fabric_id);
+ }
+
+ my $rpcenv = PVE::RPCEnvironment::get();
+ my $authuser = $rpcenv->get_user();
+ my $node_privs = ['Sys.Audit', 'Sys.Modify'];
+
+ my @res;
+ for my $node_id (sort keys %$nodes) {
+ next if !$rpcenv->check_any($authuser, "/nodes/$node_id", $node_privs, 1);
+ $nodes->{$node_id}->{digest} = $digest if $digest;
+ push @res, $nodes->{$node_id};
+ }
+
+ return \@res;
+ },
+});
+
+__PACKAGE__->register_method({
+ name => 'get_node',
+ path => '{node_id}',
+ method => 'GET',
+ description => 'Get a node',
+ permissions => {
+ check => [
+ 'and',
+ ['perm', '/sdn/fabrics/{fabric_id}', ['SDN.Audit', 'SDN.Allocate'], any => 1],
+ ['perm', '/nodes/{node_id}', ['Sys.Audit', 'Sys.Modify'], any => 1],
+ ],
+ },
+ parameters => {
+ properties => {
+ fabric_id => get_standard_option('pve-sdn-fabric-id'),
+ node_id => get_standard_option('pve-sdn-fabric-node-id'),
+ },
+ },
+ returns => {
+ properties => PVE::Network::SDN::Fabrics::node_properties(0),
+ },
+ code => sub {
+ my ($param) = @_;
+
+ my $fabric_id = extract_param($param, 'fabric_id');
+ my $node_id = extract_param($param, 'node_id');
+
+ my $config = PVE::Network::SDN::Fabrics::config();
+
+ my $node = $config->get_node($fabric_id, $node_id);
+ $node->{digest} = $config->digest();
+
+ return $node;
+ },
+});
+
+__PACKAGE__->register_method({
+ name => 'add_node',
+ path => '',
+ method => 'POST',
+ description => 'Add a node',
+ protected => 1,
+ permissions => {
+ check => [
+ 'and',
+ ['perm', '/sdn/fabrics/{fabric_id}', ['SDN.Allocate']],
+ ['perm', '/nodes/{node_id}', ['Sys.Modify']],
+ ],
+ },
+ parameters => {
+ properties => PVE::Network::SDN::Fabrics::node_properties(0),
+ },
+ returns => {
+ type => 'null',
+ },
+ code => sub {
+ my ($param) = @_;
+
+ PVE::Network::SDN::lock_sdn_config(
+ sub {
+ my $config = PVE::Network::SDN::Fabrics::config();
+
+ my $digest = extract_param($param, 'digest');
+ PVE::Tools::assert_if_modified($config->digest(), $digest) if $digest;
+
+ $config->add_node($param);
+ PVE::Network::SDN::Fabrics::write_config($config);
+ },
+ "adding node failed",
+ );
+ },
+});
+
+__PACKAGE__->register_method({
+ name => 'update_node',
+ path => '{node_id}',
+ method => 'PUT',
+ description => 'Update a node',
+ protected => 1,
+ permissions => {
+ check => [
+ 'and',
+ ['perm', '/sdn/fabrics/{fabric_id}', ['SDN.Allocate']],
+ ['perm', '/nodes/{node_id}', ['Sys.Modify']],
+ ],
+ },
+ parameters => {
+ properties => PVE::Network::SDN::Fabrics::node_properties(1),
+ },
+ returns => {
+ type => 'null',
+ },
+ code => sub {
+ my ($param) = @_;
+
+ PVE::Network::SDN::lock_sdn_config(
+ sub {
+ my $fabric_id = extract_param($param, 'fabric_id');
+ my $node_id = extract_param($param, 'node_id');
+
+ my $config = PVE::Network::SDN::Fabrics::config();
+
+ my $digest = extract_param($param, 'digest');
+ PVE::Tools::assert_if_modified($config->digest(), $digest) if $digest;
+
+ $config->update_node($fabric_id, $node_id, $param);
+ PVE::Network::SDN::Fabrics::write_config($config);
+ },
+ "updating node failed",
+ );
+ },
+});
+
+__PACKAGE__->register_method({
+ name => 'delete_node',
+ path => '{node_id}',
+ method => 'DELETE',
+ description => 'Add a node',
+ protected => 1,
+ permissions => {
+ check => [
+ 'and',
+ ['perm', '/sdn/fabrics/{fabric_id}', ['SDN.Allocate']],
+ ['perm', '/nodes/{node_id}', ['Sys.Modify']],
+ ],
+ },
+ parameters => {
+ properties => {
+ fabric_id => get_standard_option('pve-sdn-fabric-id'),
+ node_id => get_standard_option('pve-sdn-fabric-node-id'),
+ },
+ },
+ returns => {
+ type => 'null',
+ },
+ code => sub {
+ my ($param) = @_;
+
+ PVE::Network::SDN::lock_sdn_config(
+ sub {
+ my $fabric_id = extract_param($param, 'fabric_id');
+ my $node_id = extract_param($param, 'node_id');
+
+ my $config = PVE::Network::SDN::Fabrics::config();
+
+ my $digest = extract_param($param, 'digest');
+ PVE::Tools::assert_if_modified($config->digest(), $digest) if $digest;
+
+ $config->delete_node($fabric_id, $node_id);
+ PVE::Network::SDN::Fabrics::write_config($config);
+ },
+ "deleting node failed",
+ );
+ },
+});
+
+1;
diff --git a/src/PVE/API2/Network/SDN/Fabrics/Makefile b/src/PVE/API2/Network/SDN/Fabrics/Makefile
index 169ebbe..9c4bda8 100644
--- a/src/PVE/API2/Network/SDN/Fabrics/Makefile
+++ b/src/PVE/API2/Network/SDN/Fabrics/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Fabric.pm Node.pm
+SOURCES=Fabric.pm FabricNode.pm Node.pm
PERL5DIR=${DESTDIR}/usr/share/perl5
diff --git a/src/PVE/API2/Network/SDN/Fabrics/Node.pm b/src/PVE/API2/Network/SDN/Fabrics/Node.pm
index 785e1ec..851c8b1 100644
--- a/src/PVE/API2/Network/SDN/Fabrics/Node.pm
+++ b/src/PVE/API2/Network/SDN/Fabrics/Node.pm
@@ -7,6 +7,7 @@ use PVE::Tools qw(extract_param);
use PVE::Network::SDN;
use PVE::Network::SDN::Fabrics;
+use PVE::API2::Network::SDN::Fabrics::FabricNode;
use PVE::JSONSchema qw(get_standard_option);
@@ -14,6 +15,11 @@ use PVE::RESTHandler;
use base qw(PVE::RESTHandler);
__PACKAGE__->register_method({
+ subclass => "PVE::API2::Network::SDN::Fabrics::FabricNode",
+ path => '{fabric_id}',
+});
+
+__PACKAGE__->register_method({
name => 'list_nodes',
path => '',
method => 'GET',