From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id EA8951FF0AE for ; Tue, 13 Oct 2026 14:32:46 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id D940221457; Tue, 13 Oct 2026 14:32:36 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.10.3 smtp.infra.comaite.com DE6C762BF2 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=comaite.com; s=377A38C6-3A8D-11E5-A0EC-E97757B27CB3; t=1791893672; bh=Vuy+wWOBRh7ZkjJN/VfZp7lQW5Dyx1LYdCWXjnrKozs=; h=From:To:Date:Message-ID:MIME-Version; b=n5Cd2OFeC7dEKBVH4VnaEf13z42oOaXnfGDDk/6U/ktYmUWhBnXfQDs+NDfFV9UtK dIPGVpacuEPuDzlqsaGY5c3OLDgOF8k7SgAXL2nCSaHHxtIKdJEKFD52+qyFhPK6hd gr3kvCBu/5w+oRA7gbaR4oNQRlI27TAhR1h7HyOuXjX8Y95uadHz543FHLvOLsrfvM Szb+iwk5qUK6/by2DOGqKQFsZHnjH2lvD55Dl8EyFrhg7SqX7CVTqyJyvhJXXMMyZA zDDPKZvWECaUwvHb9VXn58St2znreNPv6SdmeItFao6MJ+QGmTVCl4w+OiUumbU/Yl 773f81O0b3TWQ== From: =?UTF-8?q?R=C3=A9mi=20PAETA?= To: pve-devel@lists.proxmox.com subject: SPAM: [RFC PATCH network 02/16] sdn: controllers: add ovn plugin Date: Tue, 13 Oct 2026 14:14:18 +0200 Message-ID: <20261013121432.2642979-3-remi.paeta@comaite.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20261013121432.2642979-1-remi.paeta@comaite.com> References: <1116090294.1714263.1791537779868.JavaMail.zimbra@comaite.com> <20261013121432.2642979-1-remi.paeta@comaite.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 4 DKIM_SIGNED 0.1 Message has a DKIM or DK signature, not necessarily valid DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature DKIM_VALID_AU -0.1 Message has a valid DKIM or DK signature from author's domain DKIM_VALID_EF -0.1 Message has a valid DKIM or DK signature from envelope-from domain DMARC_PASS -0.1 DMARC pass policy SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_DBL_SPAM 5 Contains a spam URL listed in the Spamhaus DBL blocklist [sdn.pm] Message-ID-Hash: FR6YU7T5PF4I7TSMV6PAMIY73KLGMN7M X-Message-ID-Hash: FR6YU7T5PF4I7TSMV6PAMIY73KLGMN7M X-MailFrom: remi.paeta@comaite.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: An OVN controller describes an existing OVN deployment: the connection strings of its northbound and southbound databases and the tunnel encapsulation used between the chassis. The OVN central components are deployed and operated by the administrator, PVE only connects to them, similar to how the external IPAM plugins use an existing service. Only plain TCP connections are supported for now. OVN does not use FRR, so OVN controllers are not taken into account when deciding whether an FRR configuration needs to be generated. Signed-off-by: Rémi PAETA --- src/PVE/Network/SDN.pm | 4 +- src/PVE/Network/SDN/Controllers.pm | 2 + src/PVE/Network/SDN/Controllers/Makefile | 2 +- src/PVE/Network/SDN/Controllers/OvnPlugin.pm | 86 ++++++++++++++++++++ src/test/Makefile | 5 +- src/test/run_test_ovn.pl | 28 +++++++ 6 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 src/PVE/Network/SDN/Controllers/OvnPlugin.pm create mode 100755 src/test/run_test_ovn.pl diff --git a/src/PVE/Network/SDN.pm b/src/PVE/Network/SDN.pm index 33a3cf3..aeb9b4c 100644 --- a/src/PVE/Network/SDN.pm +++ b/src/PVE/Network/SDN.pm @@ -126,13 +126,15 @@ sub running_config_has_frr { # all can be empty if the SDN configuration was never applied my $controllers = $running_config->{controllers}->{ids} // {}; + # OVN controllers do not generate any FRR configuration + my @frr_controllers = grep { $controllers->{$_}->{type} ne 'ovn' } keys %$controllers; my $fabrics = $running_config->{fabrics}->{ids} // {}; my $route_maps = $running_config->{'route-maps'}->{ids} // {}; my $prefix_lists = $running_config->{'prefix-lists'}->{ids} // {}; my $local_frr_config = PVE::Network::SDN::Frr::local_frr_config_exists(); - return %$controllers || %$fabrics || %$route_maps || %$prefix_lists || $local_frr_config; + return @frr_controllers || %$fabrics || %$route_maps || %$prefix_lists || $local_frr_config; } sub pending_config { diff --git a/src/PVE/Network/SDN/Controllers.pm b/src/PVE/Network/SDN/Controllers.pm index b2c2f9d..ac0d70a 100644 --- a/src/PVE/Network/SDN/Controllers.pm +++ b/src/PVE/Network/SDN/Controllers.pm @@ -15,11 +15,13 @@ use PVE::Network::SDN::Controllers::EvpnPlugin; use PVE::Network::SDN::Controllers::BgpPlugin; use PVE::Network::SDN::Controllers::IsisPlugin; use PVE::Network::SDN::Controllers::FaucetPlugin; +use PVE::Network::SDN::Controllers::OvnPlugin; use PVE::Network::SDN::Controllers::Plugin; PVE::Network::SDN::Controllers::EvpnPlugin->register(); PVE::Network::SDN::Controllers::BgpPlugin->register(); PVE::Network::SDN::Controllers::IsisPlugin->register(); PVE::Network::SDN::Controllers::FaucetPlugin->register(); +PVE::Network::SDN::Controllers::OvnPlugin->register(); PVE::Network::SDN::Controllers::Plugin->init(); sub sdn_controllers_config { diff --git a/src/PVE/Network/SDN/Controllers/Makefile b/src/PVE/Network/SDN/Controllers/Makefile index fd9f881..a1cd14e 100644 --- a/src/PVE/Network/SDN/Controllers/Makefile +++ b/src/PVE/Network/SDN/Controllers/Makefile @@ -1,4 +1,4 @@ -SOURCES=Plugin.pm FaucetPlugin.pm EvpnPlugin.pm BgpPlugin.pm IsisPlugin.pm +SOURCES=Plugin.pm FaucetPlugin.pm EvpnPlugin.pm BgpPlugin.pm IsisPlugin.pm OvnPlugin.pm PERL5DIR=${DESTDIR}/usr/share/perl5 diff --git a/src/PVE/Network/SDN/Controllers/OvnPlugin.pm b/src/PVE/Network/SDN/Controllers/OvnPlugin.pm new file mode 100644 index 0000000..f7a9b96 --- /dev/null +++ b/src/PVE/Network/SDN/Controllers/OvnPlugin.pm @@ -0,0 +1,86 @@ +package PVE::Network::SDN::Controllers::OvnPlugin; + +use v5.36; + +use PVE::JSONSchema; +use PVE::Tools; + +use PVE::Network::SDN::Controllers::Plugin; + +use base('PVE::Network::SDN::Controllers::Plugin'); + +PVE::JSONSchema::register_format('pve-sdn-ovn-remotes', \&pve_verify_sdn_ovn_remotes); + +sub pve_verify_sdn_ovn_remotes { + my ($remotes, $noerr) = @_; + + for my $remote (split(/,/, $remotes)) { + next if $remote =~ m/^tcp:$PVE::Tools::IPV4RE:\d+$/; + next if $remote =~ m/^tcp:\[$PVE::Tools::IPV6RE\]:\d+$/; + + return undef if $noerr; + die "invalid OVN database connection '$remote', expected tcp::\n"; + } + + return $remotes; +} + +sub type { + return 'ovn'; +} + +sub properties { + return { + 'nb-connection' => { + description => "Comma separated list of the OVN northbound database servers," + . " for example tcp:10.0.0.1:6641,tcp:10.0.0.2:6641.", + type => 'string', + format => 'pve-sdn-ovn-remotes', + }, + 'sb-connection' => { + description => "Comma separated list of the OVN southbound database servers," + . " for example tcp:10.0.0.1:6642,tcp:10.0.0.2:6642.", + type => 'string', + format => 'pve-sdn-ovn-remotes', + }, + 'encap-type' => { + description => "Tunnel encapsulation used between the chassis.", + type => 'string', + enum => ['geneve', 'vxlan'], + default => 'geneve', + }, + }; +} + +sub options { + return { + 'nb-connection' => { optional => 0 }, + 'sb-connection' => { optional => 0 }, + 'encap-type' => { optional => 1 }, + }; +} + +# OVN does not use FRR +sub generate_frr_config { + my ($class, $plugin_config, $controller_cfg, $id, $uplinks, $config) = @_; + + return; +} + +sub generate_zone_frr_config { + my ($class, $plugin_config, $controller, $controller_cfg, $id, $uplinks, $config) = @_; + + return; +} + +sub on_delete_hook { + my ($class, $controllerid, $zone_cfg) = @_; + + for my $id (sort keys %{ $zone_cfg->{ids} }) { + my $zone = $zone_cfg->{ids}->{$id}; + die "controller $controllerid is used by $id\n" + if defined($zone->{controller}) && $zone->{controller} eq $controllerid; + } +} + +1; diff --git a/src/test/Makefile b/src/test/Makefile index 5a937a4..b3c5d69 100644 --- a/src/test/Makefile +++ b/src/test/Makefile @@ -1,6 +1,6 @@ all: test -test: test_zones test_ipams test_dns test_subnets test_vnets_blackbox +test: test_zones test_ipams test_dns test_subnets test_vnets_blackbox test_ovn test_zones: run_test_zones.pl ./run_test_zones.pl @@ -17,4 +17,7 @@ test_subnets: run_test_subnets.pl test_vnets_blackbox: run_test_vnets_blackbox.pl ./run_test_vnets_blackbox.pl +test_ovn: run_test_ovn.pl + ./run_test_ovn.pl + clean: diff --git a/src/test/run_test_ovn.pl b/src/test/run_test_ovn.pl new file mode 100755 index 0000000..60e752b --- /dev/null +++ b/src/test/run_test_ovn.pl @@ -0,0 +1,28 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use lib qw(..); + +use Test::More; + +use PVE::Network::SDN; +use PVE::Network::SDN::Controllers::OvnPlugin; + +# controller schema + +ok( + PVE::Network::SDN::Controllers::OvnPlugin::pve_verify_sdn_ovn_remotes( + 'tcp:10.0.0.1:6641,tcp:[fd00::1]:6641', 1, + ), + 'remotes format: valid', +); +for my $invalid ('ssl:10.0.0.1:6641', 'tcp:10.0.0.1', 'tcp:ovn.example.com:6641') { + ok( + !PVE::Network::SDN::Controllers::OvnPlugin::pve_verify_sdn_ovn_remotes($invalid, 1), + "remotes format: invalid '$invalid'", + ); +} + +done_testing(); -- 2.47.3