From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: <pve-devel-bounces@lists.proxmox.com> Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id DCC361FF164 for <inbox@lore.proxmox.com>; Fri, 28 Mar 2025 18:16:25 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 8CE508D5E; Fri, 28 Mar 2025 18:14:12 +0100 (CET) From: Gabriel Goller <g.goller@proxmox.com> To: pve-devel@lists.proxmox.com Date: Fri, 28 Mar 2025 18:13:26 +0100 Message-Id: <20250328171340.885413-39-g.goller@proxmox.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250328171340.885413-1-g.goller@proxmox.com> References: <20250328171340.885413-1-g.goller@proxmox.com> MIME-Version: 1.0 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.025 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 Subject: [pve-devel] [PATCH pve-network 11/17] api: add fabrics subfolder X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion <pve-devel.lists.proxmox.com> List-Unsubscribe: <https://lists.proxmox.com/cgi-bin/mailman/options/pve-devel>, <mailto:pve-devel-request@lists.proxmox.com?subject=unsubscribe> List-Archive: <http://lists.proxmox.com/pipermail/pve-devel/> List-Post: <mailto:pve-devel@lists.proxmox.com> List-Help: <mailto:pve-devel-request@lists.proxmox.com?subject=help> List-Subscribe: <https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel>, <mailto:pve-devel-request@lists.proxmox.com?subject=subscribe> Reply-To: Proxmox VE development discussion <pve-devel@lists.proxmox.com> Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" <pve-devel-bounces@lists.proxmox.com> From: Stefan Hanreich <s.hanreich@proxmox.com> Add a new subfolder that contains the API methods for the sdn fabrics. We also add a method for listing all fabrics of all types as a GET endpoint, with the respective schemas. It supports the same filtering options as the other SDN GET endpoints (pending / running). We also need to add a special case in encode_value for the interface key of nodes, since they require special handling when encoding because they are arrays. Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com> Co-authored-by: Gabriel Goller <g.goller@proxmox.com> Signed-off-by: Gabriel Goller <g.goller@proxmox.com> --- src/PVE/API2/Network/SDN.pm | 7 + src/PVE/API2/Network/SDN/Fabrics.pm | 294 ++++++++++++++++++++++++++++ src/PVE/API2/Network/SDN/Makefile | 2 +- src/PVE/Network/SDN.pm | 2 +- 4 files changed, 303 insertions(+), 2 deletions(-) create mode 100644 src/PVE/API2/Network/SDN/Fabrics.pm diff --git a/src/PVE/API2/Network/SDN.pm b/src/PVE/API2/Network/SDN.pm index d216e4878b61..ccbf0777e3d4 100644 --- a/src/PVE/API2/Network/SDN.pm +++ b/src/PVE/API2/Network/SDN.pm @@ -17,6 +17,7 @@ use PVE::API2::Network::SDN::Vnets; use PVE::API2::Network::SDN::Zones; use PVE::API2::Network::SDN::Ipams; use PVE::API2::Network::SDN::Dns; +use PVE::API2::Network::SDN::Fabrics; use base qw(PVE::RESTHandler); @@ -45,6 +46,11 @@ __PACKAGE__->register_method ({ path => 'dns', }); +__PACKAGE__->register_method ({ + subclass => "PVE::API2::Network::SDN::Fabrics", + path => 'fabrics', +}); + __PACKAGE__->register_method({ name => 'index', path => '', @@ -76,6 +82,7 @@ __PACKAGE__->register_method({ { id => 'controllers' }, { id => 'ipams' }, { id => 'dns' }, + { id => 'fabrics' }, ]; return $res; diff --git a/src/PVE/API2/Network/SDN/Fabrics.pm b/src/PVE/API2/Network/SDN/Fabrics.pm new file mode 100644 index 000000000000..c9064b0ea05b --- /dev/null +++ b/src/PVE/API2/Network/SDN/Fabrics.pm @@ -0,0 +1,294 @@ +package PVE::API2::Network::SDN::Fabrics; + +use strict; +use warnings; + +use Storable qw(dclone); + +use PVE::RPCEnvironment; +use PVE::Tools qw(extract_param); + +use PVE::API2::Network::SDN::Fabrics::OpenFabric; +use PVE::API2::Network::SDN::Fabrics::Ospf; + +use PVE::Network::SDN::Fabrics; + +use PVE::RESTHandler; +use base qw(PVE::RESTHandler); + +__PACKAGE__->register_method ({ + subclass => "PVE::API2::Network::SDN::Fabrics::OpenFabric", + path => 'openfabric', +}); +__PACKAGE__->register_method ({ + subclass => "PVE::API2::Network::SDN::Fabrics::Ospf", + path => 'ospf', +}); + +my $openfabric_interface_fmt = { + name => { + type => 'string', + description => 'Name of the interface', + }, + ip => { + type => 'string', + description => 'The IPv4 address of the interface', + optional => 1, + }, + ipv6 => { + type => 'string', + description => 'The IPv6 address of the interface', + optional => 1, + }, + passive => { + type => 'boolean', + description => 'The passive property of the interface', + optional => 1, + }, + hello_interval => { + type => 'number', + description => 'The hello_interval property of the interface', + optional => 1, + }, + csnp_interval => { + type => 'number', + description => 'The csnp_interval property of the interface', + optional => 1, + }, + hello_multiplier => { + type => 'number', + description => 'The hello_multiplier property of the interface', + optional => 1, + }, +}; + +PVE::JSONSchema::register_format('pve-sdn-openfabric-interface', $openfabric_interface_fmt); + +my $ospf_interface_fmt = { + name => { + type => 'string', + description => 'Name of the interface', + }, + passive => { + type => 'boolean', + description => 'The passive property of the interface', + optional => 1, + }, + ip => { + type => 'string', + description => 'The IPv4 address of the interface', + optional => 1, + }, + unnumbered => { + type => 'boolean', + description => 'If the interface is unnumbered', + optional => 1, + }, +}; + +PVE::JSONSchema::register_format('pve-sdn-ospf-interface', $ospf_interface_fmt); + +__PACKAGE__->register_method({ + name => 'index', + path => '', + method => 'GET', + description => 'Index of SDN Fabrics', + permissions => { + description => "Only list entries where you have 'SDN.Audit' or 'SDN.Allocate' permissions on '/sdn/fabrics/<protocol>/<fabric>'", + user => 'all', + }, + parameters => { + additionalProperties => 0, + properties => { + running => { + type => 'boolean', + optional => 1, + description => "Display running config.", + }, + pending => { + type => 'boolean', + optional => 1, + description => "Display pending config.", + }, + }, + }, + returns => { + type => 'object', + properties => { + openfabric => { + type => 'array', + items => { + type => 'object', + properties => { + name => { + type => 'string', + description => 'Id of the fabric' + }, + 'type' => { + type => 'string', + description => 'What type of config is this', + }, + 'config' => { + type => 'object', + 'type-property' => 'type', + oneOf => [ + { + 'instance-types' => ['node'], + type => 'object', + description => 'Node config', + properties => { + node => { + type => 'object', + properties => { + net => { + type => 'string', + description => 'The NET (Network Entity Title) of this node', + }, + loopback_prefix => { + type => 'string', + description => 'The IP prefix for Loopback IPs', + }, + interface => { + type => 'array', + description => 'The OpenFabric interfaces on this node', + items => { + type => 'string', + description => 'OpenFabric interface', + format => 'pve-sdn-openfabric-interface' + }, + }, + }, + }, + }, + }, + { + 'instance-types' => ['fabric'], + type => 'object', + description => 'Fabric config', + properties => { + fabric => { + type => 'object', + properties => { + loopback_prefix => { + type => 'string', + description => 'The IP prefix for Loopback IPs', + }, + hello_interval => { + type => 'integer', + optional => 1, + description => 'The global hello_interval parameter in seconds that will be set on every interface', + }, + }, + }, + }, + } + ], + }, + }, + }, + }, + ospf => { + type => 'array', + items => { + type => 'object', + properties => { + name => { + type => 'string', + description => 'Id of the fabric' + }, + config => { + type => 'object', + 'type-property' => 'type', + oneOf => [ + { + 'instance-types' => [ 'node' ], + type => 'object', + description => 'Node config', + properties => { + node => { + type => 'object', + properties => { + router_id => { + type => 'string', + description => 'The Router ID of this node', + }, + interface => { + type => 'array', + description => 'The OSPF interfaces on this node', + items => { + type => 'string', + description => 'OSPF interface', + format => 'pve-sdn-ospf-interface', + }, + }, + }, + }, + }, + }, + { + 'instance-types' => [ 'fabric' ], + type => 'object', + description => 'Fabric config', + properties => { + fabric => { + type => 'object', + }, + }, + } + ] + }, + }, + }, + }, + }, + }, + code => sub { + my ($param) = @_; + my $rpcenv = PVE::RPCEnvironment::get(); + + my $running = extract_param($param, 'running'); + my $pending = extract_param($param, 'pending'); + + my $fabric_config = PVE::Network::SDN::Fabrics::config(); + my $running_config = PVE::Network::SDN::running_config(); + my $config; + + my $authuser = $rpcenv->get_user(); + my $privs = [ 'SDN.Audit', 'SDN.Allocate' ]; + + my $res = {}; + foreach my $protocol (keys %$fabric_config) { + $res->{$protocol} = []; + + if ($pending) { + # pending_config expects the section config to be under the ids + # key, but get_inner() returns it without that key + my $section_config = { + ids => $fabric_config->{$protocol}->get_inner(), + }; + + $config = PVE::Network::SDN::pending_config( + $running_config, + $section_config, + $protocol + ); + + $config = $config->{ids}; + } elsif ($running) { + $config = $running_config->{$protocol}->{ids}; + } else { + $config = $fabric_config->{$protocol}->get_inner(); + } + + foreach my $id (sort keys %$config) { + my $entry = $config->{$id}; + next if !$rpcenv->check_any($authuser, "/sdn/fabrics/$protocol/$entry->{name}", $privs, 1); + + push @{$res->{$protocol}}, dclone($entry); + } + } + return $res; + }, +}); + +1; diff --git a/src/PVE/API2/Network/SDN/Makefile b/src/PVE/API2/Network/SDN/Makefile index abd1bfae020e..4dbb6c92fd82 100644 --- a/src/PVE/API2/Network/SDN/Makefile +++ b/src/PVE/API2/Network/SDN/Makefile @@ -1,4 +1,4 @@ -SOURCES=Vnets.pm Zones.pm Controllers.pm Subnets.pm Ipams.pm Dns.pm Ips.pm +SOURCES=Vnets.pm Zones.pm Controllers.pm Subnets.pm Ipams.pm Dns.pm Ips.pm Fabrics.pm PERL5DIR=${DESTDIR}/usr/share/perl5 diff --git a/src/PVE/Network/SDN.pm b/src/PVE/Network/SDN.pm index 24879dc0e76a..b35767b667b4 100644 --- a/src/PVE/Network/SDN.pm +++ b/src/PVE/Network/SDN.pm @@ -344,7 +344,7 @@ sub generate_dhcp_config { sub encode_value { my ($type, $key, $value) = @_; - if ($key eq 'nodes' || $key eq 'exitnodes' || $key eq 'dhcp-range') { + if ($key eq 'nodes' || $key eq 'exitnodes' || $key eq 'dhcp-range' || $key eq 'interface') { if (ref($value) eq 'HASH') { return join(',', sort keys(%$value)); } elsif (ref($value) eq 'ARRAY') { -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel