From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 179861FF14F for ; Fri, 08 May 2026 18:34:38 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id B699F1F889; Fri, 8 May 2026 18:32:45 +0200 (CEST) From: Stefan Hanreich To: pve-devel@lists.proxmox.com Subject: [PATCH pve-network v6 07/24] api: refactor route map api structure Date: Fri, 8 May 2026 18:31:16 +0200 Message-ID: <20260508163134.481912-8-s.hanreich@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260508163134.481912-1-s.hanreich@proxmox.com> References: <20260508163134.481912-1-s.hanreich@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1778257794750 X-SPAM-LEVEL: Spam detection results: 0 AWL -0.518 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 KAM_MAILER 2 Automated Mailer Tag Left in Email POISEN_SPAM_PILL 0.1 Meta: its spam POISEN_SPAM_PILL_1 0.1 random spam to be learned in bayes POISEN_SPAM_PILL_3 0.1 random spam to be learned in bayes 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: M73SBIAKVRIZWLEBENBRO6TVAY6MLVDW X-Message-ID-Hash: M73SBIAKVRIZWLEBENBRO6TVAY6MLVDW X-MailFrom: s.hanreich@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: Reworks the existing rotue map API structure to look as follows: GET /route-maps Returns a list of all route maps that exist in the configuration, the returned data only contains the ID of the route maps. It does not provide the usual pending parameter, since different entries could have different pending states. This could potentially be added in the future by looking at the pending state of all entries, and treating any pending state of any entry as changed state for the route map. For now this hasn't been implemented, since the endpoint is only used by the route map selector component, which doesn't require knowledge about the pending state of route maps. GET /route-maps/entries Returns a list of all entries of all route maps. POST /route-maps/entries Creates a new route map entry. GET /route-maps/entries/{route-map-id} Returns a list of all entries of a specific route map. GET/PUT/DELETE /route-maps/entries/{route-map-id}/entry/{order} Returns / updates / deletes a specific route map entry. Signed-off-by: Stefan Hanreich --- src/PVE/API2/Network/SDN/RouteMaps.pm | 93 ++---------- src/PVE/API2/Network/SDN/RouteMaps/Makefile | 2 +- .../API2/Network/SDN/RouteMaps/RouteMap.pm | 7 +- .../Network/SDN/RouteMaps/RouteMapEntries.pm | 142 ++++++++++++++++++ .../Network/SDN/RouteMaps/RouteMapEntry.pm | 9 +- 5 files changed, 164 insertions(+), 89 deletions(-) create mode 100644 src/PVE/API2/Network/SDN/RouteMaps/RouteMapEntries.pm diff --git a/src/PVE/API2/Network/SDN/RouteMaps.pm b/src/PVE/API2/Network/SDN/RouteMaps.pm index 8a7936d9..4baffe31 100644 --- a/src/PVE/API2/Network/SDN/RouteMaps.pm +++ b/src/PVE/API2/Network/SDN/RouteMaps.pm @@ -3,7 +3,7 @@ package PVE::API2::Network::SDN::RouteMaps; use strict; use warnings; -use PVE::API2::Network::SDN::RouteMaps::RouteMap; +use PVE::API2::Network::SDN::RouteMaps::RouteMapEntries; use PVE::Exception qw(raise_param_exc); use PVE::JSONSchema qw(get_standard_option); use PVE::Network::SDN::RouteMaps; @@ -13,8 +13,8 @@ use PVE::RESTHandler; use base qw(PVE::RESTHandler); __PACKAGE__->register_method({ - subclass => "PVE::API2::Network::SDN::RouteMaps::RouteMap", - path => '{route-map-id}', + subclass => "PVE::API2::Network::SDN::RouteMaps::RouteMapEntries", + path => 'entries', }); __PACKAGE__->register_method({ @@ -23,7 +23,7 @@ __PACKAGE__->register_method({ method => 'GET', permissions => { description => - "Only returns route map entries where you have 'SDN.Audit' or 'SDN.Allocate' permissions.", + "Only returns route maps where you have 'SDN.Audit' or 'SDN.Allocate' permissions.", user => 'all', }, description => "List Route Maps", @@ -34,108 +34,41 @@ __PACKAGE__->register_method({ optional => 1, description => "Display running config.", }, - pending => { - type => 'boolean', - optional => 1, - description => "Display pending config.", - }, }, }, returns => { type => 'array', items => { type => "object", - properties => PVE::Network::SDN::RouteMaps::route_map_properties(0), + properties => { + id => get_standard_option('pve-sdn-route-map-id'), + } }, - links => [{ rel => 'child', href => "{route-map-id}" }], + links => [{ rel => 'child', href => "entries/{id}" }], }, code => sub { my ($param) = @_; - my $pending = extract_param($param, 'pending'); my $running = extract_param($param, 'running'); - - my $digest; - my $route_maps; - - if ($pending) { - my $current_config = PVE::Network::SDN::RouteMaps::config(); - my $running_config = PVE::Network::SDN::RouteMaps::config(1); - - my $pending_route_maps = PVE::Network::SDN::pending_config( - { 'route-maps' => { ids => $running_config->list() } }, - { ids => $current_config->list() }, - 'route-maps', - ); - - $digest = $current_config->digest(); - $route_maps = $pending_route_maps->{ids}; - } elsif ($running) { - $route_maps = PVE::Network::SDN::RouteMaps::config(1)->list(); - } else { - my $current_config = PVE::Network::SDN::RouteMaps::config(); - - $digest = $current_config->digest(); - $route_maps = $current_config->list(); - } + my $route_maps = PVE::Network::SDN::RouteMaps::config($running)->list_route_maps(); my $rpcenv = PVE::RPCEnvironment::get(); my $authuser = $rpcenv->get_user(); my $route_map_privs = ['SDN.Audit', 'SDN.Allocate']; my @res; - for my $route_map_id (sort keys $route_maps->%*) { + for my $route_map ($route_maps->@*) { next - if !$rpcenv->check_any($authuser, "/sdn/route-maps/$route_map_id", + if !$rpcenv->check_any($authuser, "/sdn/route-maps/$route_map->{id}", $route_map_privs, 1); - $route_maps->{$route_map_id}->{digest} = $digest if $digest; - push @res, $route_maps->{$route_map_id}; + + push @res, $route_map; } return \@res; }, }); -__PACKAGE__->register_method({ - name => 'create_route_map_entry', - path => '', - method => 'POST', - protected => 1, - permissions => { - check => ['perm', '/sdn/route-maps', ['SDN.Allocate']], - }, - description => "Create Route Map entry", - parameters => { - properties => { - digest => get_standard_option('pve-config-digest'), - 'lock-token' => get_standard_option('pve-sdn-lock-token'), - PVE::Network::SDN::RouteMaps::route_map_properties(0)->%*, - }, - }, - returns => { - type => "null", - }, - code => sub { - my ($param) = @_; - my $lock_token = extract_param($param, 'lock-token'); - - PVE::Network::SDN::lock_sdn_config( - sub { - my $config = PVE::Network::SDN::RouteMaps::config(); - - my $digest = extract_param($param, 'digest'); - PVE::Tools::assert_if_modified($config->digest(), $digest) if $digest; - - $config->create($param); - PVE::Network::SDN::RouteMaps::write_config($config); - }, - "creating route map entry failed", - $lock_token, - ); - - return; - }, -}); 1; diff --git a/src/PVE/API2/Network/SDN/RouteMaps/Makefile b/src/PVE/API2/Network/SDN/RouteMaps/Makefile index 07b45e91..46ed800f 100644 --- a/src/PVE/API2/Network/SDN/RouteMaps/Makefile +++ b/src/PVE/API2/Network/SDN/RouteMaps/Makefile @@ -1,7 +1,7 @@ SOURCES=RouteMap.pm\ + RouteMapEntries.pm\ RouteMapEntry.pm - PERL5DIR=${DESTDIR}/usr/share/perl5 .PHONY: install diff --git a/src/PVE/API2/Network/SDN/RouteMaps/RouteMap.pm b/src/PVE/API2/Network/SDN/RouteMaps/RouteMap.pm index 3b027a0b..7565c377 100644 --- a/src/PVE/API2/Network/SDN/RouteMaps/RouteMap.pm +++ b/src/PVE/API2/Network/SDN/RouteMaps/RouteMap.pm @@ -13,11 +13,11 @@ use base qw(PVE::RESTHandler); __PACKAGE__->register_method({ subclass => "PVE::API2::Network::SDN::RouteMaps::RouteMapEntry", - path => '{order}', + path => 'entry', }); __PACKAGE__->register_method({ - name => 'list_route_map_entries', + name => 'list_route_map_entries_for_route_map', path => '', method => 'GET', permissions => { @@ -46,13 +46,14 @@ __PACKAGE__->register_method({ type => "object", properties => PVE::Network::SDN::RouteMaps::route_map_properties(0), }, - links => [{ rel => 'child', href => "{order}" }], + links => [{ rel => 'child', href => "entry/{route-map-id}" }], }, code => sub { my ($param) = @_; my $pending = extract_param($param, 'pending'); my $running = extract_param($param, 'running'); + my $route_map_id = extract_param($param, 'route-map-id'); my $digest; diff --git a/src/PVE/API2/Network/SDN/RouteMaps/RouteMapEntries.pm b/src/PVE/API2/Network/SDN/RouteMaps/RouteMapEntries.pm new file mode 100644 index 00000000..220e5a7b --- /dev/null +++ b/src/PVE/API2/Network/SDN/RouteMaps/RouteMapEntries.pm @@ -0,0 +1,142 @@ +package PVE::API2::Network::SDN::RouteMaps::RouteMapEntries; + +use strict; +use warnings; + +use PVE::API2::Network::SDN::RouteMaps::RouteMap; +use PVE::Exception qw(raise_param_exc); +use PVE::JSONSchema qw(get_standard_option); +use PVE::Network::SDN::RouteMaps; +use PVE::Tools qw(extract_param); + +use PVE::RESTHandler; +use base qw(PVE::RESTHandler); + +__PACKAGE__->register_method({ + subclass => "PVE::API2::Network::SDN::RouteMaps::RouteMap", + path => '{route-map-id}', +}); + +__PACKAGE__->register_method({ + name => 'list_route_map_entries', + path => '', + method => 'GET', + permissions => { + description => + "Only returns route map entries where you have 'SDN.Audit' or 'SDN.Allocate' permissions.", + user => 'all', + }, + description => "Lists all route map entries.", + parameters => { + properties => { + running => { + type => 'boolean', + optional => 1, + description => "Display running config.", + }, + pending => { + type => 'boolean', + optional => 1, + description => "Display pending config.", + }, + }, + }, + returns => { + type => 'array', + items => { + type => "object", + properties => PVE::Network::SDN::RouteMaps::route_map_properties(0), + }, + links => [{ rel => 'child', href => "{route-map-id}" }], + }, + code => sub { + my ($param) = @_; + + my $pending = extract_param($param, 'pending'); + my $running = extract_param($param, 'running'); + + my $digest; + my $route_maps; + + if ($pending) { + my $current_config = PVE::Network::SDN::RouteMaps::config(); + my $running_config = PVE::Network::SDN::RouteMaps::config(1); + + my $pending_route_maps = PVE::Network::SDN::pending_config( + { 'route-maps' => { ids => $running_config->list() } }, + { ids => $current_config->list() }, + 'route-maps', + ); + + $digest = $current_config->digest(); + $route_maps = $pending_route_maps->{ids}; + } elsif ($running) { + $route_maps = PVE::Network::SDN::RouteMaps::config(1)->list(); + } else { + my $current_config = PVE::Network::SDN::RouteMaps::config(); + + $digest = $current_config->digest(); + $route_maps = $current_config->list(); + } + + my $rpcenv = PVE::RPCEnvironment::get(); + my $authuser = $rpcenv->get_user(); + my $route_map_privs = ['SDN.Audit', 'SDN.Allocate']; + + my @res; + for my $route_map_id (sort keys $route_maps->%*) { + next + if !$rpcenv->check_any($authuser, "/sdn/route-maps/$route_map_id", + $route_map_privs, 1); + + $route_maps->{$route_map_id}->{digest} = $digest if $digest; + push @res, $route_maps->{$route_map_id}; + } + + return \@res; + }, +}); + +__PACKAGE__->register_method({ + name => 'create_route_map_entry', + path => '', + method => 'POST', + protected => 1, + permissions => { + check => ['perm', '/sdn/route-maps', ['SDN.Allocate']], + }, + description => "Create Route Map entry", + parameters => { + properties => { + digest => get_standard_option('pve-config-digest'), + 'lock-token' => get_standard_option('pve-sdn-lock-token'), + PVE::Network::SDN::RouteMaps::route_map_properties(0)->%*, + }, + }, + returns => { + type => "null", + }, + code => sub { + my ($param) = @_; + + my $lock_token = extract_param($param, 'lock-token'); + + PVE::Network::SDN::lock_sdn_config( + sub { + my $config = PVE::Network::SDN::RouteMaps::config(); + + my $digest = extract_param($param, 'digest'); + PVE::Tools::assert_if_modified($config->digest(), $digest) if $digest; + + $config->create($param); + PVE::Network::SDN::RouteMaps::write_config($config); + }, + "creating route map entry failed", + $lock_token, + ); + + return; + }, +}); + + diff --git a/src/PVE/API2/Network/SDN/RouteMaps/RouteMapEntry.pm b/src/PVE/API2/Network/SDN/RouteMaps/RouteMapEntry.pm index f53cfad3..a7561f44 100644 --- a/src/PVE/API2/Network/SDN/RouteMaps/RouteMapEntry.pm +++ b/src/PVE/API2/Network/SDN/RouteMaps/RouteMapEntry.pm @@ -3,9 +3,8 @@ package PVE::API2::Network::SDN::RouteMaps::RouteMapEntry; use strict; use warnings; -use PVE::Exception qw(raise_param_exc); use PVE::JSONSchema qw(get_standard_option); -use PVE::Network::SDN::RouteMaps; +use PVE::Exception qw(raise_param_exc); use PVE::Tools qw(extract_param); use PVE::RESTHandler; @@ -13,7 +12,7 @@ use base qw(PVE::RESTHandler); __PACKAGE__->register_method({ name => 'get_route_map_entry', - path => '', + path => '{order}', method => 'GET', permissions => { check => @@ -48,7 +47,7 @@ __PACKAGE__->register_method({ __PACKAGE__->register_method({ name => 'update_route_map_entry', - path => '', + path => '{order}', method => 'PUT', protected => 1, permissions => { @@ -94,7 +93,7 @@ __PACKAGE__->register_method({ __PACKAGE__->register_method({ name => 'delete_route_map_entry', - path => '', + path => '{order}', method => 'DELETE', protected => 1, permissions => { -- 2.47.3