all lists on lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH V2 pve-network 0/4] add ebgp-evpn support
@ 2020-11-25  9:01 Alexandre Derumier
  2020-11-25  9:01 ` [pve-devel] [PATCH V2 pve-network 1/4] controllers: improve bgp-evpn Alexandre Derumier
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Alexandre Derumier @ 2020-11-25  9:01 UTC (permalink / raw)
  To: pve-devel

This add support for a new controller plugin "bgp",
to manage specific bgp section by host.
This is allowing ebgp

(I'll send doc soon)

Also some fixes on pending parser

Changelog V2:

- fix some bug in bgp plugin

Alexandre Derumier (4):
  controllers: improve bgp-evpn
  zones: evpn : add support for loopback
  update test documentation
  sdn: fix : pending parser

 PVE/API2/Network/SDN/Controllers.pm         |   1 +
 PVE/API2/Network/SDN/Zones.pm               |  12 +-
 PVE/Network/SDN.pm                          |  45 +++++-
 PVE/Network/SDN/Controllers.pm              |  12 +-
 PVE/Network/SDN/Controllers/BgpPlugin.pm    | 153 ++++++++++++++++++++
 PVE/Network/SDN/Controllers/EvpnPlugin.pm   | 122 ++++++++++------
 PVE/Network/SDN/Controllers/FaucetPlugin.pm |   4 +-
 PVE/Network/SDN/Controllers/Makefile        |   2 +-
 PVE/Network/SDN/Controllers/Plugin.pm       |   9 +-
 PVE/Network/SDN/Zones.pm                    |   2 +-
 PVE/Network/SDN/Zones/EvpnPlugin.pm         |  12 +-
 PVE/Network/SDN/Zones/Plugin.pm             |  39 ++---
 PVE/Network/SDN/Zones/QinQPlugin.pm         |   2 +-
 PVE/Network/SDN/Zones/SimplePlugin.pm       |   2 +-
 PVE/Network/SDN/Zones/VlanPlugin.pm         |   2 +-
 PVE/Network/SDN/Zones/VxlanPlugin.pm        |   2 +-
 test/documentation.txt                      |  14 +-
 17 files changed, 336 insertions(+), 99 deletions(-)
 create mode 100644 PVE/Network/SDN/Controllers/BgpPlugin.pm

-- 
2.20.1




^ permalink raw reply	[flat|nested] 6+ messages in thread

* [pve-devel] [PATCH V2 pve-network 1/4] controllers: improve bgp-evpn
  2020-11-25  9:01 [pve-devel] [PATCH V2 pve-network 0/4] add ebgp-evpn support Alexandre Derumier
@ 2020-11-25  9:01 ` Alexandre Derumier
  2020-11-25  9:01 ` [pve-devel] [PATCH V2 pve-network 2/4] zones: evpn : add support for loopback Alexandre Derumier
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Alexandre Derumier @ 2020-11-25  9:01 UTC (permalink / raw)
  To: pve-devel

- add new bgp plugin
- add ebgp support
- add loopback support
- move gateway-nodes option to zone as 'exitnodes'
- move external-peers to bgp plugin
---
 PVE/API2/Network/SDN/Controllers.pm         |   1 +
 PVE/Network/SDN/Controllers.pm              |  12 +-
 PVE/Network/SDN/Controllers/BgpPlugin.pm    | 153 ++++++++++++++++++++
 PVE/Network/SDN/Controllers/EvpnPlugin.pm   | 122 ++++++++++------
 PVE/Network/SDN/Controllers/FaucetPlugin.pm |   4 +-
 PVE/Network/SDN/Controllers/Makefile        |   2 +-
 PVE/Network/SDN/Controllers/Plugin.pm       |   9 +-
 PVE/Network/SDN/Zones/EvpnPlugin.pm         |  11 +-
 PVE/Network/SDN/Zones/Plugin.pm             |   9 +-
 9 files changed, 269 insertions(+), 54 deletions(-)
 create mode 100644 PVE/Network/SDN/Controllers/BgpPlugin.pm

diff --git a/PVE/API2/Network/SDN/Controllers.pm b/PVE/API2/Network/SDN/Controllers.pm
index 75beb6b..e761b6c 100644
--- a/PVE/API2/Network/SDN/Controllers.pm
+++ b/PVE/API2/Network/SDN/Controllers.pm
@@ -11,6 +11,7 @@ use PVE::Network::SDN::Zones;
 use PVE::Network::SDN::Controllers;
 use PVE::Network::SDN::Controllers::Plugin;
 use PVE::Network::SDN::Controllers::EvpnPlugin;
+use PVE::Network::SDN::Controllers::BgpPlugin;
 use PVE::Network::SDN::Controllers::FaucetPlugin;
 
 use Storable qw(dclone);
diff --git a/PVE/Network/SDN/Controllers.pm b/PVE/Network/SDN/Controllers.pm
index f652d7f..9937755 100644
--- a/PVE/Network/SDN/Controllers.pm
+++ b/PVE/Network/SDN/Controllers.pm
@@ -13,9 +13,11 @@ use PVE::Network::SDN::Vnets;
 use PVE::Network::SDN::Zones;
 
 use PVE::Network::SDN::Controllers::EvpnPlugin;
+use PVE::Network::SDN::Controllers::BgpPlugin;
 use PVE::Network::SDN::Controllers::FaucetPlugin;
 use PVE::Network::SDN::Controllers::Plugin;
 PVE::Network::SDN::Controllers::EvpnPlugin->register();
+PVE::Network::SDN::Controllers::BgpPlugin->register();
 PVE::Network::SDN::Controllers::FaucetPlugin->register();
 PVE::Network::SDN::Controllers::Plugin->init();
 
@@ -95,24 +97,24 @@ sub generate_controller_config {
     #generate configuration
     my $config = {};
 
-    foreach my $id (keys %{$controller_cfg->{ids}}) {
+    foreach my $id (sort keys %{$controller_cfg->{ids}}) {
 	my $plugin_config = $controller_cfg->{ids}->{$id};
 	my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($plugin_config->{type});
-	$plugin->generate_controller_config($plugin_config, $plugin_config, $id, $uplinks, $config);
+	$plugin->generate_controller_config($plugin_config, $controller_cfg, $id, $uplinks, $config);
     }
 
-    foreach my $id (keys %{$zone_cfg->{ids}}) {
+    foreach my $id (sort keys %{$zone_cfg->{ids}}) {
 	my $plugin_config = $zone_cfg->{ids}->{$id};
 	my $controllerid = $plugin_config->{controller};
 	next if !$controllerid;
 	my $controller = $controller_cfg->{ids}->{$controllerid};
 	if ($controller) {
 	    my $controller_plugin = PVE::Network::SDN::Controllers::Plugin->lookup($controller->{type});
-	    $controller_plugin->generate_controller_zone_config($plugin_config, $controller, $id, $uplinks, $config);
+	    $controller_plugin->generate_controller_zone_config($plugin_config, $controller, $controller_cfg, $id, $uplinks, $config);
 	}
     }
 
-    foreach my $id (keys %{$vnet_cfg->{ids}}) {
+    foreach my $id (sort keys %{$vnet_cfg->{ids}}) {
 	my $plugin_config = $vnet_cfg->{ids}->{$id};
 	my $zoneid = $plugin_config->{zone};
 	next if !$zoneid;
diff --git a/PVE/Network/SDN/Controllers/BgpPlugin.pm b/PVE/Network/SDN/Controllers/BgpPlugin.pm
new file mode 100644
index 0000000..ccc06a8
--- /dev/null
+++ b/PVE/Network/SDN/Controllers/BgpPlugin.pm
@@ -0,0 +1,153 @@
+package PVE::Network::SDN::Controllers::BgpPlugin;
+
+use strict;
+use warnings;
+
+use PVE::INotify;
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::Tools qw(run_command file_set_contents file_get_contents);
+
+use PVE::Network::SDN::Controllers::Plugin;
+use PVE::Network::SDN::Zones::Plugin;
+use Net::IP;
+
+use base('PVE::Network::SDN::Controllers::Plugin');
+
+sub type {
+    return 'bgp';
+}
+
+sub properties {
+    return {
+	ebgp => {
+	    type => 'boolean',
+	    optional => 1,
+	    description => "Enable ebgp. (remote-as external)",
+	},
+	loopback => {
+	    description => "source loopback interface.",
+	    type => 'string'
+	},
+        node => get_standard_option('pve-node'),
+    };
+}
+
+sub options {
+    return {
+	'node' => { optional => 0 },
+	'asn' => { optional => 0 },
+	'peers' => { optional => 0 },
+	'ebgp' => { optional => 1 },
+	'loopback' => { optional => 1 },
+    };
+}
+
+# Plugin implementation
+sub generate_controller_config {
+    my ($class, $plugin_config, $controller, $id, $uplinks, $config) = @_;
+
+    my @peers;
+    @peers = PVE::Tools::split_list($plugin_config->{'peers'}) if $plugin_config->{'peers'};
+
+    my $asn = $plugin_config->{asn};
+    my $ebgp = $plugin_config->{ebgp};
+    my $loopback = $plugin_config->{loopback};
+    my $local_node = PVE::INotify::nodename();
+
+
+    return if !$asn;
+    return if $local_node ne $plugin_config->{node};
+
+    my $bgp = $config->{frr}->{router}->{"bgp $asn"} //= {};
+
+    my ($ifaceip, $interface) = PVE::Network::SDN::Zones::Plugin::find_local_ip_interface_peers(\@peers, $loopback);
+
+    my $remoteas = $ebgp ? "external" : $asn;
+
+    #global options
+    my @controller_config = (
+        "bgp router-id $ifaceip",
+        "no bgp default ipv4-unicast",
+        "coalesce-time 1000",
+        "bgp network import-check"
+    );
+
+    push(@{$bgp->{""}}, @controller_config) if keys %{$bgp} == 0;
+
+    @controller_config = ();
+    if($ebgp) {
+	push @controller_config, "no bgp ebgp-requires-policy";
+	push @controller_config, "bgp disable-ebgp-connected-route-check" if $loopback;
+    }
+
+    #BGP neighbors
+    if(@peers) {
+	push @controller_config, "neighbor BGP peer-group";
+	push @controller_config, "neighbor BGP remote-as $remoteas";
+	push @controller_config, "neighbor BGP bfd";
+    }
+
+    # BGP peers
+    foreach my $address (@peers) {
+	push @controller_config, "neighbor $address peer-group BGP";
+    }
+    push(@{$bgp->{""}}, @controller_config);
+
+    # address-family unicast
+    if (@peers) {
+	my $ipversion = Net::IP::ip_is_ipv6($ifaceip) ? "ipv6" : "ipv4";
+	my $mask = Net::IP::ip_is_ipv6($ifaceip) ? "/128" : "32";
+
+	push(@{$bgp->{"address-family"}->{"$ipversion unicast"}}, "network $ifaceip/$mask") if $loopback;
+	push(@{$bgp->{"address-family"}->{"$ipversion unicast"}}, "neighbor BGP activate");
+	push(@{$bgp->{"address-family"}->{"$ipversion unicast"}}, "neighbor BGP soft-reconfiguration inbound");
+    }
+
+    return $config;
+}
+
+sub generate_controller_zone_config {
+    my ($class, $plugin_config, $controller, $controller_cfg, $id, $uplinks, $config) = @_;
+
+}
+
+sub on_delete_hook {
+    my ($class, $controllerid, $zone_cfg) = @_;
+
+    # verify that zone is associated to this controller
+    foreach my $id (keys %{$zone_cfg->{ids}}) {
+	my $zone = $zone_cfg->{ids}->{$id};
+	die "controller $controllerid is used by $id"
+	    if (defined($zone->{controller}) && $zone->{controller} eq $controllerid);
+    }
+}
+
+sub on_update_hook {
+    my ($class, $controllerid, $controller_cfg) = @_;
+
+    # we can only have 1 bgp controller by node
+    my $local_node = PVE::INotify::nodename();
+    my $controllernb = 0;
+    foreach my $id (keys %{$controller_cfg->{ids}}) {
+        next if $id eq $controllerid;
+        my $controller = $controller_cfg->{ids}->{$id};
+        next if $controller->{type} ne "bgp";
+        next if $controller->{node} ne $local_node;
+        $controllernb++;
+        die "only 1 bgp controller can be defined" if $controllernb > 1;
+    }
+}
+
+sub write_controller_config {
+    my ($class, $plugin_config, $config) = @_;
+    return;
+}
+
+sub reload_controller {
+    my ($class) = @_;
+    return;
+}
+
+1;
+
+
diff --git a/PVE/Network/SDN/Controllers/EvpnPlugin.pm b/PVE/Network/SDN/Controllers/EvpnPlugin.pm
index d82de2a..e59c142 100644
--- a/PVE/Network/SDN/Controllers/EvpnPlugin.pm
+++ b/PVE/Network/SDN/Controllers/EvpnPlugin.pm
@@ -9,6 +9,7 @@ use PVE::Tools qw(run_command file_set_contents file_get_contents);
 
 use PVE::Network::SDN::Controllers::Plugin;
 use PVE::Network::SDN::Zones::Plugin;
+use Net::IP;
 
 use base('PVE::Network::SDN::Controllers::Plugin');
 
@@ -26,11 +27,6 @@ sub properties {
 	    description => "peers address list.",
 	    type => 'string', format => 'ip-list'
 	},
-	'gateway-nodes' => get_standard_option('pve-node-list'),
-	'gateway-external-peers' => {
-	    description => "upstream bgp peers address list.",
-	    type => 'string', format => 'ip-list'
-	},
     };
 }
 
@@ -38,80 +34,97 @@ sub options {
     return {
 	'asn' => { optional => 0 },
 	'peers' => { optional => 0 },
-	'gateway-nodes' => { optional => 1 },
-	'gateway-external-peers' => { optional => 1 },
     };
 }
 
 # Plugin implementation
 sub generate_controller_config {
-    my ($class, $plugin_config, $controller, $id, $uplinks, $config) = @_;
+    my ($class, $plugin_config, $controller_cfg, $id, $uplinks, $config) = @_;
 
     my @peers;
     @peers = PVE::Tools::split_list($plugin_config->{'peers'}) if $plugin_config->{'peers'};
 
+    my $local_node = PVE::INotify::nodename();
+
     my $asn = $plugin_config->{asn};
-    my $gatewaynodes = $plugin_config->{'gateway-nodes'};
-    my @gatewaypeers;
-    @gatewaypeers = PVE::Tools::split_list($plugin_config->{'gateway-external-peers'}) if $plugin_config->{'gateway-external-peers'};
+    my $ebgp = undef;
+    my $loopback = undef;
+    my $autortas = undef;
+    my $bgprouter = find_bgp_controller($local_node, $controller_cfg);
+    if($bgprouter) {
+	$ebgp = 1 if $plugin_config->{'asn'} ne $bgprouter->{asn};
+	$loopback = $bgprouter->{loopback} if $bgprouter->{loopback};
+	$asn = $bgprouter->{asn} if $bgprouter->{asn};
+	$autortas = $plugin_config->{'asn'} if $ebgp;
+    }
 
     return if !$asn;
 
     my $bgp = $config->{frr}->{router}->{"bgp $asn"} //= {};
 
-    my ($ifaceip, $interface) = PVE::Network::SDN::Zones::Plugin::find_local_ip_interface_peers(\@peers);
+    my ($ifaceip, $interface) = PVE::Network::SDN::Zones::Plugin::find_local_ip_interface_peers(\@peers, $loopback);
 
-    my $is_gateway = undef;
-    my $local_node = PVE::INotify::nodename();
-
-    foreach my $gatewaynode (PVE::Tools::split_list($gatewaynodes)) {
-	$is_gateway = 1 if $gatewaynode eq $local_node;
-    }
+    my $remoteas = $ebgp ? "external" : $asn;
 
+    #global options
     my @controller_config = (
 	"bgp router-id $ifaceip",
 	"no bgp default ipv4-unicast",
 	"coalesce-time 1000",
     );
 
+    push(@{$bgp->{""}}, @controller_config) if keys %{$bgp} == 0;
+
+    @controller_config = ();
+    
+    #VTEP neighbors
+    push @controller_config, "neighbor VTEP peer-group";
+    push @controller_config, "neighbor VTEP remote-as $remoteas";
+    push @controller_config, "neighbor VTEP bfd";
+
+    if($ebgp && $loopback) {
+	push @controller_config, "neighbor VTEP ebgp-multihop 10";
+	push @controller_config, "neighbor VTEP update-source $loopback";
+    }
+
+    # VTEP peers
     foreach my $address (@peers) {
 	next if $address eq $ifaceip;
-	push @controller_config, "neighbor $address remote-as $asn";
+	push @controller_config, "neighbor $address peer-group VTEP";
     }
 
-    if ($is_gateway) {
-	foreach my $address (@gatewaypeers) {
-	    push @controller_config, "neighbor $address remote-as external";
-	}
-    }
     push(@{$bgp->{""}}, @controller_config);
 
+    # address-family l2vpn
     @controller_config = ();
-    foreach my $address (@peers) {
-	next if $address eq $ifaceip;
-	push @controller_config, "neighbor $address activate";
-    }
+    push @controller_config, "neighbor VTEP activate";
     push @controller_config, "advertise-all-vni";
+    push @controller_config, "autort as $autortas" if $autortas;
     push(@{$bgp->{"address-family"}->{"l2vpn evpn"}}, @controller_config);
 
-    if ($is_gateway) {
-	# import /32 routes of evpn network from vrf1 to default vrf (for packet return)
-	@controller_config = map { "neighbor $_ activate" } @gatewaypeers;
-
-	push(@{$bgp->{"address-family"}->{"ipv4 unicast"}}, @controller_config);
-	push(@{$bgp->{"address-family"}->{"ipv6 unicast"}}, @controller_config);
-    }
-
     return $config;
 }
 
 sub generate_controller_zone_config {
-    my ($class, $plugin_config, $controller, $id, $uplinks, $config) = @_;
+    my ($class, $plugin_config, $controller, $controller_cfg, $id, $uplinks, $config) = @_;
+
+    my $local_node = PVE::INotify::nodename();
 
     my $vrf = "vrf_$id";
     my $vrfvxlan = $plugin_config->{'vrf-vxlan'};
+    my $exitnodes = $plugin_config->{'exitnodes'};
+
     my $asn = $controller->{asn};
-    my $gatewaynodes = $controller->{'gateway-nodes'};
+    my $ebgp = undef;
+    my $loopback = undef;
+    my $autortas = undef;
+    my $bgprouter = find_bgp_controller($local_node, $controller_cfg);
+    if($bgprouter) {
+        $ebgp = 1 if $controller->{'asn'} ne $bgprouter->{asn};
+	$loopback = $bgprouter->{loopback} if $bgprouter->{loopback};
+	$asn = $bgprouter->{asn} if $bgprouter->{asn};
+	$autortas = $controller->{'asn'} if $ebgp;
+    }
 
     return if !$vrf || !$vrfvxlan || !$asn;
 
@@ -120,11 +133,18 @@ sub generate_controller_zone_config {
     push @controller_config, "vni $vrfvxlan";
     push(@{$config->{frr}->{vrf}->{"$vrf"}}, @controller_config);
 
-    push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{""}}, "!");
+    #main vrf router
+    @controller_config = ();
+    push @controller_config, "no bgp ebgp-requires-policy" if $ebgp;
+#    push @controller_config, "!";
+    push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{""}}, @controller_config);
 
-    my $local_node = PVE::INotify::nodename();
+    if ($autortas) {
+	push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"l2vpn evpn"}}, "route-target import $autortas:$vrfvxlan");
+	push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"l2vpn evpn"}}, "route-target export $autortas:$vrfvxlan");
+    }
 
-    my $is_gateway = grep { $_ eq $local_node } PVE::Tools::split_list($gatewaynodes);
+    my $is_gateway = grep { $_ eq $local_node } PVE::Tools::split_list($exitnodes);
     if ($is_gateway) {
 
 	@controller_config = ();
@@ -165,13 +185,31 @@ sub on_update_hook {
 
     # we can only have 1 evpn controller / 1 asn by server
 
+    my $controllernb = 0;
     foreach my $id (keys %{$controller_cfg->{ids}}) {
 	next if $id eq $controllerid;
 	my $controller = $controller_cfg->{ids}->{$id};
-	die "only 1 evpn controller can be defined" if $controller->{type} eq "evpn";
+	next if $controller->{type} ne "evpn";
+	$controllernb++;
+	die "only 1 global evpn controller can be defined" if $controllernb > 1;
+    }
+}
+
+sub find_bgp_controller {
+    my ($nodename, $controller_cfg) = @_;
+
+    my $controller = undef;
+    foreach my $id  (keys %{$controller_cfg->{ids}}) {
+        $controller = $controller_cfg->{ids}->{$id};
+        next if $controller->{type} ne 'bgp';
+        next if $controller->{node} ne $nodename;
+	last;
     }
+
+    return $controller;
 }
 
+
 sub sort_frr_config {
     my $order = {};
     $order->{''} = 0;
diff --git a/PVE/Network/SDN/Controllers/FaucetPlugin.pm b/PVE/Network/SDN/Controllers/FaucetPlugin.pm
index dcac6eb..5742187 100644
--- a/PVE/Network/SDN/Controllers/FaucetPlugin.pm
+++ b/PVE/Network/SDN/Controllers/FaucetPlugin.pm
@@ -22,12 +22,12 @@ sub properties {
 
 # Plugin implementation
 sub generate_controller_config {
-    my ($class, $plugin_config, $controller, $id, $uplinks, $config) = @_;
+    my ($class, $plugin_config, $controller_cfg, $id, $uplinks, $config) = @_;
 
 }
 
 sub generate_controller_zone_config {
-    my ($class, $plugin_config, $controller, $id, $uplinks, $config) = @_;
+    my ($class, $plugin_config, $controller, $controller_cfg, $id, $uplinks, $config) = @_;
 
     my $dpid = $plugin_config->{'dp-id'};
     my $dphex = printf("%x",$dpid);
diff --git a/PVE/Network/SDN/Controllers/Makefile b/PVE/Network/SDN/Controllers/Makefile
index 3324125..11686a3 100644
--- a/PVE/Network/SDN/Controllers/Makefile
+++ b/PVE/Network/SDN/Controllers/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Plugin.pm FaucetPlugin.pm EvpnPlugin.pm
+SOURCES=Plugin.pm FaucetPlugin.pm EvpnPlugin.pm BgpPlugin.pm
 
 
 PERL5DIR=${DESTDIR}/usr/share/perl5
diff --git a/PVE/Network/SDN/Controllers/Plugin.pm b/PVE/Network/SDN/Controllers/Plugin.pm
index 06cd576..0c92b17 100644
--- a/PVE/Network/SDN/Controllers/Plugin.pm
+++ b/PVE/Network/SDN/Controllers/Plugin.pm
@@ -70,7 +70,14 @@ sub generate_sdn_config {
 }
 
 sub generate_controller_config {
-    my ($class, $plugin_config, $controller, $id, $uplinks, $config) = @_;
+    my ($class, $plugin_config, $controller_cfg, $id, $uplinks, $config) = @_;
+
+    die "please implement inside plugin";
+}
+
+
+sub generate_controller_zone_config {
+    my ($class, $plugin_config, $controller, $controller_cfg, $id, $uplinks, $config) = @_;
 
     die "please implement inside plugin";
 }
diff --git a/PVE/Network/SDN/Zones/EvpnPlugin.pm b/PVE/Network/SDN/Zones/EvpnPlugin.pm
index 5338a1b..d50ddb9 100644
--- a/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -4,6 +4,7 @@ use strict;
 use warnings;
 use PVE::Network::SDN::Zones::VxlanPlugin;
 use PVE::Exception qw(raise raise_param_exc);
+use PVE::JSONSchema qw(get_standard_option);
 use PVE::Tools qw($IPV4RE);
 use PVE::INotify;
 use PVE::Cluster;
@@ -27,6 +28,7 @@ sub properties {
 	    type => 'string',
 	    description => "Frr router name",
 	},
+	'exitnodes' => get_standard_option('pve-node-list'),
     };
 }
 
@@ -35,7 +37,8 @@ sub options {
     return {
         nodes => { optional => 1},
         'vrf-vxlan' => { optional => 0 },
-        'controller' => { optional => 0 },
+        controller => { optional => 0 },
+	exitnodes => { optional => 1 },
 	mtu => { optional => 1 },
 	dns => { optional => 1 },
 	reversedns => { optional => 1 },
@@ -59,10 +62,14 @@ sub generate_sdn_config {
     my $local_node = PVE::INotify::nodename();
 
     die "missing vxlan tag" if !$tag;
+    die "missing controller" if !$controller;
     warn "vlan-aware vnet can't be enabled with evpn plugin" if $vnet->{vlanaware};
 
     my @peers = PVE::Tools::split_list($controller->{'peers'});
-    my ($ifaceip, $iface) = PVE::Network::SDN::Zones::Plugin::find_local_ip_interface_peers(\@peers);
+#    my $bgprouter = PVE::Network::SDN::Controllers::EvpnController::find_bgp_controller($local_node, $controller_cfg);
+#    my $loopback = $bgprouter->{loopback} if $bgprouter->{loopback};
+    my $loopback = undef;
+    my ($ifaceip, $iface) = PVE::Network::SDN::Zones::Plugin::find_local_ip_interface_peers(\@peers, $loopback);
 
     my $mtu = 1450;
     $mtu = $interfaces_config->{$iface}->{mtu} - 50 if $interfaces_config->{$iface}->{mtu};
diff --git a/PVE/Network/SDN/Zones/Plugin.pm b/PVE/Network/SDN/Zones/Plugin.pm
index 6fc13eb..aa795a3 100644
--- a/PVE/Network/SDN/Zones/Plugin.pm
+++ b/PVE/Network/SDN/Zones/Plugin.pm
@@ -274,10 +274,17 @@ sub get_local_route_ip {
 
 
 sub find_local_ip_interface_peers {
-    my ($peers) = @_;
+    my ($peers, $iface) = @_;
 
     my $network_config = PVE::INotify::read_file('interfaces');
     my $ifaces = $network_config->{ifaces};
+    
+    #if iface is defined, return ip if exist (if not,try to find it on other ifaces)
+    if ($iface) {
+	my $ip = $ifaces->{$iface}->{address};
+	return ($ip,$iface) if $ip;
+    }
+
     #is a local ip member of peers list ?
     foreach my $address (@{$peers}) {
 	while (my $interface = each %$ifaces) {
-- 
2.20.1




^ permalink raw reply	[flat|nested] 6+ messages in thread

* [pve-devel] [PATCH V2 pve-network 2/4] zones: evpn : add support for loopback
  2020-11-25  9:01 [pve-devel] [PATCH V2 pve-network 0/4] add ebgp-evpn support Alexandre Derumier
  2020-11-25  9:01 ` [pve-devel] [PATCH V2 pve-network 1/4] controllers: improve bgp-evpn Alexandre Derumier
@ 2020-11-25  9:01 ` Alexandre Derumier
  2020-11-25  9:01 ` [pve-devel] [PATCH V2 pve-network 3/4] update test documentation Alexandre Derumier
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Alexandre Derumier @ 2020-11-25  9:01 UTC (permalink / raw)
  To: pve-devel

---
 PVE/Network/SDN/Zones.pm              | 2 +-
 PVE/Network/SDN/Zones/EvpnPlugin.pm   | 7 +++----
 PVE/Network/SDN/Zones/Plugin.pm       | 2 +-
 PVE/Network/SDN/Zones/QinQPlugin.pm   | 2 +-
 PVE/Network/SDN/Zones/SimplePlugin.pm | 2 +-
 PVE/Network/SDN/Zones/VlanPlugin.pm   | 2 +-
 PVE/Network/SDN/Zones/VxlanPlugin.pm  | 2 +-
 7 files changed, 9 insertions(+), 10 deletions(-)

diff --git a/PVE/Network/SDN/Zones.pm b/PVE/Network/SDN/Zones.pm
index 1f225dc..67d8f18 100644
--- a/PVE/Network/SDN/Zones.pm
+++ b/PVE/Network/SDN/Zones.pm
@@ -131,7 +131,7 @@ sub generate_etc_network_config {
 
 	my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
 	eval {
-	    $plugin->generate_sdn_config($plugin_config, $zone, $id, $vnet, $controller, $subnet_cfg, $interfaces_config, $config);
+	    $plugin->generate_sdn_config($plugin_config, $zone, $id, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config);
 	};
 	if (my $err = $@) {
 	    warn "zone $zone : vnet $id : $err\n";
diff --git a/PVE/Network/SDN/Zones/EvpnPlugin.pm b/PVE/Network/SDN/Zones/EvpnPlugin.pm
index d50ddb9..14bbf56 100644
--- a/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -49,7 +49,7 @@ sub options {
 
 # Plugin implementation
 sub generate_sdn_config {
-    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $subnet_cfg, $interfaces_config, $config) = @_;
+    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config) = @_;
 
     my $tag = $vnet->{tag};
     my $alias = $vnet->{alias};
@@ -66,9 +66,8 @@ sub generate_sdn_config {
     warn "vlan-aware vnet can't be enabled with evpn plugin" if $vnet->{vlanaware};
 
     my @peers = PVE::Tools::split_list($controller->{'peers'});
-#    my $bgprouter = PVE::Network::SDN::Controllers::EvpnController::find_bgp_controller($local_node, $controller_cfg);
-#    my $loopback = $bgprouter->{loopback} if $bgprouter->{loopback};
-    my $loopback = undef;
+    my $bgprouter = PVE::Network::SDN::Controllers::EvpnPlugin::find_bgp_controller($local_node, $controller_cfg);
+    my $loopback = $bgprouter->{loopback} if $bgprouter->{loopback};
     my ($ifaceip, $iface) = PVE::Network::SDN::Zones::Plugin::find_local_ip_interface_peers(\@peers, $loopback);
 
     my $mtu = 1450;
diff --git a/PVE/Network/SDN/Zones/Plugin.pm b/PVE/Network/SDN/Zones/Plugin.pm
index aa795a3..8592e3c 100644
--- a/PVE/Network/SDN/Zones/Plugin.pm
+++ b/PVE/Network/SDN/Zones/Plugin.pm
@@ -98,7 +98,7 @@ sub parse_section_header {
 }
 
 sub generate_sdn_config {
-    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $subnet_cfg, $interfaces_config, $config) = @_;
+    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config) = @_;
 
     die "please implement inside plugin";
 }
diff --git a/PVE/Network/SDN/Zones/QinQPlugin.pm b/PVE/Network/SDN/Zones/QinQPlugin.pm
index 5d40db8..2bd60db 100644
--- a/PVE/Network/SDN/Zones/QinQPlugin.pm
+++ b/PVE/Network/SDN/Zones/QinQPlugin.pm
@@ -49,7 +49,7 @@ sub options {
 
 # Plugin implementation
 sub generate_sdn_config {
-    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $subnet_cfg, $interfaces_config, $config) = @_;
+    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config) = @_;
 
     my $stag = $plugin_config->{tag};
     my $mtu = $plugin_config->{mtu};
diff --git a/PVE/Network/SDN/Zones/SimplePlugin.pm b/PVE/Network/SDN/Zones/SimplePlugin.pm
index c4f4475..ed41e62 100644
--- a/PVE/Network/SDN/Zones/SimplePlugin.pm
+++ b/PVE/Network/SDN/Zones/SimplePlugin.pm
@@ -43,7 +43,7 @@ sub options {
 
 # Plugin implementation
 sub generate_sdn_config {
-    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $subnet_cfg, $interfaces_config, $config) = @_;
+    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config) = @_;
 
     return $config if$config->{$vnetid}; # nothing to do
 
diff --git a/PVE/Network/SDN/Zones/VlanPlugin.pm b/PVE/Network/SDN/Zones/VlanPlugin.pm
index 7af9b2c..ca6bd8f 100644
--- a/PVE/Network/SDN/Zones/VlanPlugin.pm
+++ b/PVE/Network/SDN/Zones/VlanPlugin.pm
@@ -43,7 +43,7 @@ sub options {
 
 # Plugin implementation
 sub generate_sdn_config {
-    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $subnet_cfg, $interfaces_config, $config) = @_;
+    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config) = @_;
 
     my $bridge = $plugin_config->{bridge};
     die "can't find bridge $bridge" if !-d "/sys/class/net/$bridge";
diff --git a/PVE/Network/SDN/Zones/VxlanPlugin.pm b/PVE/Network/SDN/Zones/VxlanPlugin.pm
index 1fe16b8..c018d34 100644
--- a/PVE/Network/SDN/Zones/VxlanPlugin.pm
+++ b/PVE/Network/SDN/Zones/VxlanPlugin.pm
@@ -47,7 +47,7 @@ sub options {
 
 # Plugin implementation
 sub generate_sdn_config {
-    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $subnet_cfg, $interfaces_config, $config) = @_;
+    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config) = @_;
 
     my $tag = $vnet->{tag};
     my $alias = $vnet->{alias};
-- 
2.20.1




^ permalink raw reply	[flat|nested] 6+ messages in thread

* [pve-devel] [PATCH V2 pve-network 3/4] update test documentation
  2020-11-25  9:01 [pve-devel] [PATCH V2 pve-network 0/4] add ebgp-evpn support Alexandre Derumier
  2020-11-25  9:01 ` [pve-devel] [PATCH V2 pve-network 1/4] controllers: improve bgp-evpn Alexandre Derumier
  2020-11-25  9:01 ` [pve-devel] [PATCH V2 pve-network 2/4] zones: evpn : add support for loopback Alexandre Derumier
@ 2020-11-25  9:01 ` Alexandre Derumier
  2020-11-25  9:01 ` [pve-devel] [PATCH V2 pve-network 4/4] sdn: fix : pending parser Alexandre Derumier
  2020-11-25 14:00 ` [pve-devel] applied-series: [PATCH V2 pve-network 0/4] add ebgp-evpn support Thomas Lamprecht
  4 siblings, 0 replies; 6+ messages in thread
From: Alexandre Derumier @ 2020-11-25  9:01 UTC (permalink / raw)
  To: pve-devel

---
 test/documentation.txt | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/test/documentation.txt b/test/documentation.txt
index 7886966..6ee8ee6 100644
--- a/test/documentation.txt
+++ b/test/documentation.txt
@@ -12,14 +12,18 @@ pvesh create /cluster/sdn/vnets/vnet100/subnets/ --type subnet --subnet 192.168.
 #create a layer2 vxlan unicast transportzone
 pvesh create /cluster/sdn/zones/ --zone vxlanunicastzone --type vxlan --ipam pve --peers 192.168.0.1,192.168.0.2,192.168.0.3
 
-#create an controller
-pvesh create /cluster/sdn/controllers/ --controller frrrouter1 --type evpn --peers 192.168.0.1,192.168.0.2,192.168.0.3 --asn 1234 --gateway-nodes pxnode1,pxnode2 --gateway-external-peers 192.168.0.253,192.168.0.254
+#create an evpn controller
+pvesh create /cluster/sdn/controllers/ --controller evpn1 --type evpn --peers 192.168.0.1,192.168.0.2,192.168.0.3 --asn 1234
+
+#add a ebgp peer
+pvesh create /cluster/sdn/controllers/ --controller bgp1 --type bgp --peers 192.168.0.253,192.168.0.254 --asn 1234 --ebgp --node pxnode1
 
 #create a layer2 vxlan bgpevpn transportzone
-pvesh create /cluster/sdn/zones/ --zone layer2evpnzone --type evpn --ipam pve --controller frrrouter1
+pvesh create /cluster/sdn/zones/ --zone layer2evpnzone --type evpn --ipam pve --controller evpn1
+
+#create a layer3 routable vxlan bgpevpn transportzone + exit-nodes
+pvesh create /cluster/sdn/zones/ --zone layer3evpnzone --type evpn --ipam pve --controller evpn1 --vrf-vxlan 4000 --exit-nodes pxnode1,pxnode2
 
-#create a layer3 routable vxlan bgpevpn transportzone
-pvesh create /cluster/sdn/zones/ --zone layer3evpnzone --type evpn --ipam pve --controller frrrouter1 --vrf-vxlan 4000
 
 
 #create a vnet in the transportzone
-- 
2.20.1




^ permalink raw reply	[flat|nested] 6+ messages in thread

* [pve-devel] [PATCH V2 pve-network 4/4] sdn: fix : pending parser
  2020-11-25  9:01 [pve-devel] [PATCH V2 pve-network 0/4] add ebgp-evpn support Alexandre Derumier
                   ` (2 preceding siblings ...)
  2020-11-25  9:01 ` [pve-devel] [PATCH V2 pve-network 3/4] update test documentation Alexandre Derumier
@ 2020-11-25  9:01 ` Alexandre Derumier
  2020-11-25 14:00 ` [pve-devel] applied-series: [PATCH V2 pve-network 0/4] add ebgp-evpn support Thomas Lamprecht
  4 siblings, 0 replies; 6+ messages in thread
From: Alexandre Derumier @ 2020-11-25  9:01 UTC (permalink / raw)
  To: pve-devel

---
 PVE/API2/Network/SDN/Zones.pm   | 12 +++++++--
 PVE/Network/SDN.pm              | 45 ++++++++++++++++++++++++++++++---
 PVE/Network/SDN/Zones/Plugin.pm | 28 --------------------
 3 files changed, 52 insertions(+), 33 deletions(-)

diff --git a/PVE/API2/Network/SDN/Zones.pm b/PVE/API2/Network/SDN/Zones.pm
index 5ae577b..5bbdd36 100644
--- a/PVE/API2/Network/SDN/Zones.pm
+++ b/PVE/API2/Network/SDN/Zones.pm
@@ -38,12 +38,20 @@ my $api_sdn_zones_config = sub {
     $scfg->{digest} = $cfg->{digest};
 
     if ($scfg->{nodes}) {
-        $scfg->{nodes} = PVE::Network::SDN::Zones::Plugin->encode_value($scfg->{type}, 'nodes', $scfg->{nodes});
+        $scfg->{nodes} = PVE::Network::SDN::encode_value($scfg->{type}, 'nodes', $scfg->{nodes});
+    }
+
+    if ($scfg->{exitnodes}) {
+        $scfg->{exitnodes} = PVE::Network::SDN::encode_value($scfg->{type}, 'exitnodes', $scfg->{exitnodes});
     }
 
     my $pending = $scfg->{pending};
     if ($pending->{nodes}) {
-        $pending->{nodes} = PVE::Network::SDN::Zones::Plugin->encode_value($scfg->{type}, 'nodes', $pending->{nodes});
+        $pending->{nodes} = PVE::Network::SDN::encode_value($scfg->{type}, 'nodes', $pending->{nodes});
+    }
+
+    if ($pending->{exitnodes}) {
+        $pending->{exitnodes} = PVE::Network::SDN::encode_value($scfg->{type}, 'exitnodes', $pending->{exitnodes});
     }
 
     return $scfg;
diff --git a/PVE/Network/SDN.pm b/PVE/Network/SDN.pm
index 3cd73ff..c0c5672 100644
--- a/PVE/Network/SDN.pm
+++ b/PVE/Network/SDN.pm
@@ -6,6 +6,8 @@ use warnings;
 use Data::Dumper;
 use JSON;
 
+use PVE::JSONSchema;
+
 use PVE::Network::SDN::Vnets;
 use PVE::Network::SDN::Zones;
 use PVE::Network::SDN::Controllers;
@@ -96,7 +98,11 @@ sub pending_config {
 	    $pending->{$id}->{$key} = $running_object->{$key};
 	    if(!keys %{$config_object}) {
 		$pending->{$id}->{state} = "deleted";
-	    } elsif ($running_object->{$key} ne $config_object->{$key}) {
+	    } elsif (!defined($config_object->{$key})) {
+		$pending->{$id}->{"pending"}->{$key} = 'deleted';
+		$pending->{$id}->{state} = "changed";
+	    } elsif (PVE::Network::SDN::encode_value(undef, $key, $running_object->{$key})
+			 ne PVE::Network::SDN::encode_value(undef, $key, $config_object->{$key})) {
 		$pending->{$id}->{state} = "changed";
 	    }
 	}
@@ -107,8 +113,8 @@ sub pending_config {
 	my $config_object = $config_objects->{$id};
 
 	foreach my $key (sort keys %{$config_object}) {
-	    my $config_value = $config_object->{$key} if $config_object->{$key};
-	    my $running_value = $running_object->{$key} if $running_object->{$key};
+	    my $config_value = PVE::Network::SDN::encode_value(undef, $key, $config_object->{$key}) if $config_object->{$key};
+	    my $running_value = PVE::Network::SDN::encode_value(undef, $key, $running_object->{$key}) if $running_object->{$key};
 	    if($key eq 'type' || $key eq 'vnet') {
 		$pending->{$id}->{$key} = $config_value;
 	    } else {
@@ -210,5 +216,38 @@ sub generate_controller_config {
     PVE::Network::SDN::Controllers::reload_controller() if $reload;
 }
 
+
+sub decode_value {
+    my ($type, $key, $value) = @_;
+
+    if ($key eq 'nodes') {
+        my $res = {};
+
+        foreach my $node (PVE::Tools::split_list($value)) {
+            if (PVE::JSONSchema::pve_verify_node_name($node)) {
+                $res->{$node} = 1;
+            }
+        }
+
+        return $res;
+    }
+
+   return $value;
+}
+
+sub encode_value {
+    my ($type, $key, $value) = @_;
+
+    if ($key eq 'nodes' || $key eq 'exitnodes') {
+        if(ref($value) eq 'HASH') {
+            return join(',', sort keys(%$value));
+        } else {
+            return $value;
+        }
+    }
+
+    return $value;
+}
+
 1;
 
diff --git a/PVE/Network/SDN/Zones/Plugin.pm b/PVE/Network/SDN/Zones/Plugin.pm
index 8592e3c..ebb5c7e 100644
--- a/PVE/Network/SDN/Zones/Plugin.pm
+++ b/PVE/Network/SDN/Zones/Plugin.pm
@@ -55,34 +55,6 @@ sub private {
     return $defaultData;
 }
 
-sub decode_value {
-    my ($class, $type, $key, $value) = @_;
-
-    if ($key eq 'nodes') {
-        my $res = {};
-
-        foreach my $node (PVE::Tools::split_list($value)) {
-            if (PVE::JSONSchema::pve_verify_node_name($node)) {
-                $res->{$node} = 1;
-            }
-        }
-
-        return $res;
-    }
-
-   return $value;
-}
-
-sub encode_value {
-    my ($class, $type, $key, $value) = @_;
-
-    if ($key eq 'nodes') {
-        return join(',', keys(%$value));
-    }
-
-    return $value;
-}
-
 sub parse_section_header {
     my ($class, $line) = @_;
 
-- 
2.20.1




^ permalink raw reply	[flat|nested] 6+ messages in thread

* [pve-devel] applied-series: [PATCH V2 pve-network 0/4] add ebgp-evpn support
  2020-11-25  9:01 [pve-devel] [PATCH V2 pve-network 0/4] add ebgp-evpn support Alexandre Derumier
                   ` (3 preceding siblings ...)
  2020-11-25  9:01 ` [pve-devel] [PATCH V2 pve-network 4/4] sdn: fix : pending parser Alexandre Derumier
@ 2020-11-25 14:00 ` Thomas Lamprecht
  4 siblings, 0 replies; 6+ messages in thread
From: Thomas Lamprecht @ 2020-11-25 14:00 UTC (permalink / raw)
  To: Proxmox VE development discussion, Alexandre Derumier

On 25.11.20 10:01, Alexandre Derumier wrote:
> This add support for a new controller plugin "bgp",
> to manage specific bgp section by host.
> This is allowing ebgp
> 
> (I'll send doc soon)
> 
> Also some fixes on pending parser
> 
> Changelog V2:
> 
> - fix some bug in bgp plugin
> 
> Alexandre Derumier (4):
>   controllers: improve bgp-evpn
>   zones: evpn : add support for loopback
>   update test documentation
>   sdn: fix : pending parser
> 
>  PVE/API2/Network/SDN/Controllers.pm         |   1 +
>  PVE/API2/Network/SDN/Zones.pm               |  12 +-
>  PVE/Network/SDN.pm                          |  45 +++++-
>  PVE/Network/SDN/Controllers.pm              |  12 +-
>  PVE/Network/SDN/Controllers/BgpPlugin.pm    | 153 ++++++++++++++++++++
>  PVE/Network/SDN/Controllers/EvpnPlugin.pm   | 122 ++++++++++------
>  PVE/Network/SDN/Controllers/FaucetPlugin.pm |   4 +-
>  PVE/Network/SDN/Controllers/Makefile        |   2 +-
>  PVE/Network/SDN/Controllers/Plugin.pm       |   9 +-
>  PVE/Network/SDN/Zones.pm                    |   2 +-
>  PVE/Network/SDN/Zones/EvpnPlugin.pm         |  12 +-
>  PVE/Network/SDN/Zones/Plugin.pm             |  39 ++---
>  PVE/Network/SDN/Zones/QinQPlugin.pm         |   2 +-
>  PVE/Network/SDN/Zones/SimplePlugin.pm       |   2 +-
>  PVE/Network/SDN/Zones/VlanPlugin.pm         |   2 +-
>  PVE/Network/SDN/Zones/VxlanPlugin.pm        |   2 +-
>  test/documentation.txt                      |  14 +-
>  17 files changed, 336 insertions(+), 99 deletions(-)
>  create mode 100644 PVE/Network/SDN/Controllers/BgpPlugin.pm
> 



applied, thanks!

FYI, I tried to create a somewhat sensible perlcritic config which could be useful.

It shows a few conditionally declared variables (`my $foo = 1 if $bar`) in pve-network,
among other things:

https://pve.proxmox.com/wiki/Perl_Style_Guide#Basic_Linting_with_perlcritic

Maybe it help you (and naturally all others) to avoid a few such possible bugs.
Note, it is not a complete check, e.g., it does not cares a bout a few things
(missing variables or so). 





^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2020-11-25 14:00 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-25  9:01 [pve-devel] [PATCH V2 pve-network 0/4] add ebgp-evpn support Alexandre Derumier
2020-11-25  9:01 ` [pve-devel] [PATCH V2 pve-network 1/4] controllers: improve bgp-evpn Alexandre Derumier
2020-11-25  9:01 ` [pve-devel] [PATCH V2 pve-network 2/4] zones: evpn : add support for loopback Alexandre Derumier
2020-11-25  9:01 ` [pve-devel] [PATCH V2 pve-network 3/4] update test documentation Alexandre Derumier
2020-11-25  9:01 ` [pve-devel] [PATCH V2 pve-network 4/4] sdn: fix : pending parser Alexandre Derumier
2020-11-25 14:00 ` [pve-devel] applied-series: [PATCH V2 pve-network 0/4] add ebgp-evpn support Thomas Lamprecht

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal