Previously the frr config generation and writing was only done in the
evpn plugin. This means that it was not possible to create a standalone
bgp and isis plugin without an evpn plugin in place. (The config would
just never be written.) To fix this, factor out the frr generation and
writing into a separate module and check if a frr-type-plugin is being
used. This also paves the way for the fabrics, which would get the
config from rust and then use this frr helper.

Signed-off-by: Gabriel Goller <g.gol...@proxmox.com>
---
 src/PVE/Network/SDN/Controllers.pm            |  47 ++-
 src/PVE/Network/SDN/Controllers/BgpPlugin.pm  |  18 +-
 src/PVE/Network/SDN/Controllers/EvpnPlugin.pm | 289 +----------------
 src/PVE/Network/SDN/Controllers/Frr.pm        | 296 ++++++++++++++++++
 src/PVE/Network/SDN/Controllers/IsisPlugin.pm |  18 +-
 src/PVE/Network/SDN/Controllers/Makefile      |   2 +-
 src/PVE/Network/SDN/Zones/EvpnPlugin.pm       |  15 +
 7 files changed, 383 insertions(+), 302 deletions(-)
 create mode 100644 src/PVE/Network/SDN/Controllers/Frr.pm

diff --git a/src/PVE/Network/SDN/Controllers.pm 
b/src/PVE/Network/SDN/Controllers.pm
index fd7ad54ac38c..43f154b7338e 100644
--- a/src/PVE/Network/SDN/Controllers.pm
+++ b/src/PVE/Network/SDN/Controllers.pm
@@ -12,6 +12,7 @@ use PVE::Cluster qw(cfs_read_file cfs_write_file 
cfs_lock_file);
 use PVE::Network::SDN::Vnets;
 use PVE::Network::SDN::Zones;
 
+use PVE::Network::SDN::Controllers::Frr;
 use PVE::Network::SDN::Controllers::EvpnPlugin;
 use PVE::Network::SDN::Controllers::BgpPlugin;
 use PVE::Network::SDN::Controllers::IsisPlugin;
@@ -148,10 +149,22 @@ sub reload_controller {
 
     return if !$controller_cfg;
 
+    my $frr_reload = 0;
+
     foreach my $id (keys %{$controller_cfg->{ids}}) {
        my $plugin_config = $controller_cfg->{ids}->{$id};
-       my $plugin = 
PVE::Network::SDN::Controllers::Plugin->lookup($plugin_config->{type});
-       $plugin->reload_controller();
+       my $type = $plugin_config->{type};
+       my @frr_types = ("bgp", "isis", "evpn");
+       if (grep {$type} @frr_types) {
+           $frr_reload = 1;
+       } else {
+           my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($type);
+           $plugin->reload_controller();
+       }
+    }
+
+    if ($frr_reload) {
+       PVE::Network::SDN::Controllers::Frr::reload_controller();
     }
 }
 
@@ -161,12 +174,22 @@ sub generate_controller_rawconfig {
     my $cfg = PVE::Network::SDN::running_config();
     my $controller_cfg = $cfg->{controllers};
     return if !$controller_cfg;
+    my $frr_generate = 0;
 
     my $rawconfig = "";
     foreach my $id (keys %{$controller_cfg->{ids}}) {
        my $plugin_config = $controller_cfg->{ids}->{$id};
-       my $plugin = 
PVE::Network::SDN::Controllers::Plugin->lookup($plugin_config->{type});
-       $rawconfig .= $plugin->generate_controller_rawconfig($plugin_config, 
$config);
+       my $type = $plugin_config->{type};
+       my @frr_types = ("bgp", "isis", "evpn");
+       if (grep {$type} @frr_types) {
+           $frr_generate = 1;
+       } else {
+           my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($type);
+           $rawconfig .= 
$plugin->generate_controller_rawconfig($plugin_config, $config);
+       }
+    }
+    if ($frr_generate) {
+        $rawconfig .= 
PVE::Network::SDN::Controllers::Frr::generate_controller_rawconfig($config);
     }
     return $rawconfig;
 }
@@ -178,10 +201,22 @@ sub write_controller_config {
     my $controller_cfg = $cfg->{controllers};
     return if !$controller_cfg;
 
+    my $frr_reload = 0;
+
     foreach my $id (keys %{$controller_cfg->{ids}}) {
        my $plugin_config = $controller_cfg->{ids}->{$id};
-       my $plugin = 
PVE::Network::SDN::Controllers::Plugin->lookup($plugin_config->{type});
-       $plugin->write_controller_config($plugin_config, $config);
+       my $type = $plugin_config->{type};
+       my @frr_types = ("bgp", "isis", "evpn");
+       if (grep {$type} @frr_types) {
+           $frr_reload = 1;
+       } else {
+           my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($type);
+           $plugin->write_controller_config($plugin_config, $config);
+       }
+    }
+    
+    if ($frr_reload) {
+       PVE::Network::SDN::Controllers::Frr::write_controller_config($config);
     }
 }
 
diff --git a/src/PVE/Network/SDN/Controllers/BgpPlugin.pm 
b/src/PVE/Network/SDN/Controllers/BgpPlugin.pm
index 53963e5ad7f4..a4d3e9990647 100644
--- a/src/PVE/Network/SDN/Controllers/BgpPlugin.pm
+++ b/src/PVE/Network/SDN/Controllers/BgpPlugin.pm
@@ -7,6 +7,7 @@ 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::Frr;
 use PVE::Network::SDN::Controllers::Plugin;
 use PVE::Network::SDN::Zones::Plugin;
 use Net::IP;
@@ -164,19 +165,22 @@ sub on_update_hook {
     }
 }
 
+sub reload_controller {
+    my ($class) = @_;
+    #return PVE::Network::SDN::Controllers::Frr::reload_controller($class);
+    die "implemented in the Frr helper";
+}
+
 sub generate_controller_rawconfig {
     my ($class, $plugin_config, $config) = @_;
-    return "";
+    #return 
PVE::Network::SDN::Controllers::Frr::generate_controller_rawconfig($class, 
$plugin_config, $config);
+    die "implemented in the Frr helper";
 }
 
 sub write_controller_config {
     my ($class, $plugin_config, $config) = @_;
-    return;
-}
-
-sub reload_controller {
-    my ($class) = @_;
-    return;
+    #return 
PVE::Network::SDN::Controllers::Frr::write_controller_config($class, 
$plugin_config, $config);
+    die "implemented in the Frr helper";
 }
 
 1;
diff --git a/src/PVE/Network/SDN/Controllers/EvpnPlugin.pm 
b/src/PVE/Network/SDN/Controllers/EvpnPlugin.pm
index c245ea29cf90..6f875cb5dbf9 100644
--- a/src/PVE/Network/SDN/Controllers/EvpnPlugin.pm
+++ b/src/PVE/Network/SDN/Controllers/EvpnPlugin.pm
@@ -9,6 +9,7 @@ use PVE::Tools qw(run_command file_set_contents 
file_get_contents);
 use PVE::RESTEnvironment qw(log_warn);
 
 use PVE::Network::SDN::Controllers::Plugin;
+use PVE::Network::SDN::Controllers::Frr;
 use PVE::Network::SDN::Zones::Plugin;
 use Net::IP;
 
@@ -109,6 +110,7 @@ sub generate_controller_config {
     push @controller_config, "neighbor VTEP route-map MAP_VTEP_IN in";
     push @controller_config, "neighbor VTEP route-map MAP_VTEP_OUT out";
     push @controller_config, "advertise-all-vni";
+    # https://datatracker.ietf.org/doc/html/rfc8365#section-5.1.2.1
     push @controller_config, "autort as $autortas" if $autortas;
     push(@{$bgp->{"address-family"}->{"l2vpn evpn"}}, @controller_config);
 
@@ -195,7 +197,7 @@ sub generate_controller_zone_config {
     push @controller_config, "no bgp hard-administrative-reset";
     push @controller_config, "no bgp graceful-restart notification";
 
-#    push @controller_config, "!";
+    #push @controller_config, "!";
     push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{""}}, 
@controller_config);
 
     if ($autortas) {
@@ -204,7 +206,6 @@ sub generate_controller_zone_config {
     }
 
     if ($is_gateway) {
-
        $config->{frr_prefix_list}->{'only_default'}->{1} = "permit 0.0.0.0/0";
        $config->{frr_prefix_list_v6}->{'only_default_v6'}->{1} = "permit ::/0";
 
@@ -356,291 +357,17 @@ sub find_isis_controller {
     return $res;
 }
 
-sub generate_frr_recurse{
-   my ($final_config, $content, $parentkey, $level) = @_;
-
-   my $keylist = {};
-   $keylist->{'address-family'} = 1;
-   $keylist->{router} = 1;
-
-   my $exitkeylist = {};
-   $exitkeylist->{'address-family'} = 1;
-
-   my $simple_exitkeylist = {};
-   $simple_exitkeylist->{router} = 1;
-
-   # FIXME: make this generic
-   my $paddinglevel = undef;
-   if ($level == 1 || $level == 2) {
-       $paddinglevel = $level - 1;
-   } elsif ($level == 3 || $level ==  4) {
-       $paddinglevel = $level - 2;
-   }
-
-   my $padding = "";
-   $padding = ' ' x ($paddinglevel) if $paddinglevel;
-
-   if (ref $content eq  'HASH') {
-       foreach my $key (sort keys %$content) {
-           next if $key eq 'vrf';
-           if ($parentkey && defined($keylist->{$parentkey})) {
-               push @{$final_config}, $padding."!";
-               push @{$final_config}, $padding."$parentkey $key";
-           } elsif ($key ne '' && !defined($keylist->{$key})) {
-               push @{$final_config}, $padding."$key";
-           }
-
-           my $option = $content->{$key};
-           generate_frr_recurse($final_config, $option, $key, $level+1);
-
-           push @{$final_config}, $padding."exit-$parentkey" if $parentkey && 
defined($exitkeylist->{$parentkey});
-           push @{$final_config}, $padding."exit" if $parentkey && 
defined($simple_exitkeylist->{$parentkey});
-       }
-    }
-
-    if (ref $content eq 'ARRAY') {
-       push @{$final_config}, map { $padding . "$_" } @$content;
-    }
-}
-
-sub generate_frr_vrf {
-   my ($final_config, $vrfs) = @_;
-
-   return if !$vrfs;
-
-   my @config = ();
-
-   foreach my $id (sort keys %$vrfs) {
-       my $vrf = $vrfs->{$id};
-       push @config, "!";
-       push @config, "vrf $id";
-       foreach my $rule (@$vrf) {
-           push @config, " $rule";
-
-       }
-       push @config, "exit-vrf";
-    }
-
-    push @{$final_config}, @config;
-}
-
-sub generate_frr_simple_list {
-   my ($final_config, $rules) = @_;
-
-   return if !$rules;
-
-   my @config = ();
-   push @{$final_config}, "!";
-   foreach my $rule (sort @$rules) {
-       push @{$final_config}, $rule;
-   }
-}
-
-sub generate_frr_interfaces {
-   my ($final_config, $interfaces) = @_;
-
-   foreach my $k (sort keys %$interfaces) {
-       my $iface = $interfaces->{$k};
-       push @{$final_config}, "!";
-       push @{$final_config}, "interface $k";
-       foreach my $rule (sort @$iface) {
-           push @{$final_config}, " $rule";
-       }
-   }
-}
-
-sub generate_frr_routemap {
-   my ($final_config, $routemaps) = @_;
-
-   foreach my $id (sort keys %$routemaps) {
-
-       my $routemap = $routemaps->{$id};
-       my $order = 0;
-       foreach my $seq (@$routemap) {
-               $order++;
-               next if !defined($seq->{action});
-               my @config = ();
-               push @config, "!";
-               push @config, "route-map $id $seq->{action} $order";
-               my $rule = $seq->{rule};
-               push @config, map { " $_" } @$rule;
-               push @{$final_config}, @config;
-               push @{$final_config}, "exit";
-       }
-   }
-}
-
-sub generate_frr_list {
-    my ($final_config, $lists, $type) = @_;
-
-    my $config = [];
-
-    for my $id (sort keys %$lists) {
-       my $list = $lists->{$id};
-
-       for my $seq (sort keys %$list) {
-           my $rule = $list->{$seq};
-           push @$config, "$type $id seq $seq $rule";
-       }
-    }
-
-    if (@$config > 0) {
-       push @{$final_config}, "!", @$config;
-    }
-}
-
-sub read_local_frr_config {
-    if (-e "/etc/frr/frr.conf.local") {
-       return file_get_contents("/etc/frr/frr.conf.local");
-    }
-};
-
 sub generate_controller_rawconfig {
     my ($class, $plugin_config, $config) = @_;
-
-    my $nodename = PVE::INotify::nodename();
-
-    my $final_config = [];
-    push @{$final_config}, "frr version 8.5.2";
-    push @{$final_config}, "frr defaults datacenter";
-    push @{$final_config}, "hostname $nodename";
-    push @{$final_config}, "log syslog informational";
-    push @{$final_config}, "service integrated-vtysh-config";
-    push @{$final_config}, "!";
-
-    my $local_conf = read_local_frr_config();
-    if ($local_conf) {
-       parse_merge_frr_local_config($config, $local_conf);
-    }
-
-    generate_frr_vrf($final_config, $config->{frr}->{vrf});
-    generate_frr_interfaces($final_config, $config->{frr_interfaces});
-    generate_frr_recurse($final_config, $config->{frr}, undef, 0);
-    generate_frr_list($final_config, $config->{frr_access_list}, 
"access-list");
-    generate_frr_list($final_config, $config->{frr_prefix_list}, "ip 
prefix-list");
-    generate_frr_list($final_config, $config->{frr_prefix_list_v6}, "ipv6 
prefix-list");
-    generate_frr_simple_list($final_config, $config->{frr_bgp_community_list});
-    generate_frr_routemap($final_config, $config->{frr_routemap});
-    generate_frr_simple_list($final_config, $config->{frr_ip_protocol});
-
-    push @{$final_config}, "!";
-    push @{$final_config}, "line vty";
-    push @{$final_config}, "!";
-
-    my $rawconfig = join("\n", @{$final_config});
-
-    return if !$rawconfig;
-    return $rawconfig;
-}
-
-sub parse_merge_frr_local_config {
-    my ($config, $local_conf) = @_;
-
-    my $section = \$config->{""};
-    my $router = undef;
-    my $routemap = undef;
-    my $routemap_config = ();
-    my $routemap_action = undef;
-
-    while ($local_conf =~ /^\s*(.+?)\s*$/gm) {
-        my $line = $1;
-       $line =~ s/^\s+|\s+$//g;
-
-       if ($line =~ m/^router (.+)$/) {
-           $router = $1;
-           $section = \$config->{'frr'}->{'router'}->{$router}->{""};
-           next;
-       } elsif ($line =~ m/^vrf (.+)$/) {
-           $section = \$config->{'frr'}->{'vrf'}->{$1};
-           next;
-       } elsif ($line =~ m/^interface (.+)$/) {
-           $section = \$config->{'frr_interfaces'}->{$1};
-           next;
-       } elsif ($line =~ m/^bgp community-list (.+)$/) {
-           push(@{$config->{'frr_bgp_community_list'}}, $line);
-           next;
-       } elsif ($line =~ m/address-family (.+)$/) {
-           $section = 
\$config->{'frr'}->{'router'}->{$router}->{'address-family'}->{$1};
-           next;
-       } elsif ($line =~ m/^route-map (.+) (permit|deny) (\d+)/) {
-           $routemap = $1;
-           $routemap_config = ();
-           $routemap_action = $2;
-           $section = \$config->{'frr_routemap'}->{$routemap};
-           next;
-       } elsif ($line =~ m/^access-list (.+) seq (\d+) (.+)$/) {
-           $config->{'frr_access_list'}->{$1}->{$2} = $3;
-           next;
-       } elsif ($line =~ m/^ip prefix-list (.+) seq (\d+) (.*)$/) {
-           $config->{'frr_prefix_list'}->{$1}->{$2} = $3;
-           next;
-       } elsif ($line =~ m/^ipv6 prefix-list (.+) seq (\d+) (.*)$/) {
-           $config->{'frr_prefix_list_v6'}->{$1}->{$2} = $3;
-           next;
-       } elsif($line =~ m/^exit-address-family$/) {
-           next;
-       } elsif($line =~ m/^exit$/) {
-           if($router) {
-               $section = \$config->{''};
-               $router = undef;
-           } elsif($routemap) {
-               push(@{$$section}, { rule => $routemap_config, action => 
$routemap_action });
-               $section = \$config->{''};
-               $routemap = undef;
-               $routemap_action = undef;
-               $routemap_config = ();
-           }
-           next;
-       } elsif($line =~ m/!/) {
-           next;
-       }
-
-       next if !$section;
-       if($routemap) {
-           push(@{$routemap_config}, $line);
-       } else {
-           push(@{$$section}, $line);
-       }
-    }
+    #return 
PVE::Network::SDN::Controllers::Frr::generate_controller_rawconfig($class, 
$plugin_config, $config);
+    die "implemented in the Frr helper";
 }
 
 sub write_controller_config {
     my ($class, $plugin_config, $config) = @_;
-
-    my $rawconfig = $class->generate_controller_rawconfig($plugin_config, 
$config);
-    return if !$rawconfig;
-    return if !-d "/etc/frr";
-
-    file_set_contents("/etc/frr/frr.conf", $rawconfig);
-}
-
-sub reload_controller {
-    my ($class) = @_;
-
-    my $conf_file = "/etc/frr/frr.conf";
-    my $bin_path = "/usr/lib/frr/frr-reload.py";
-
-    if (!-e $bin_path) {
-       log_warn("missing $bin_path. Please install frr-pythontools package");
-       return;
-    }
-
-    my $err = sub {
-       my $line = shift;
-       if ($line =~ /ERROR:/) {
-           warn "$line \n";
-       }
-    };
-
-    if (-e $conf_file && -e $bin_path) {
-       eval {
-           run_command([$bin_path, '--stdout', '--reload', $conf_file], 
outfunc => {}, errfunc => $err);
-       };
-       if ($@) {
-           warn "frr reload command fail. Restarting frr.";
-           eval { run_command(['systemctl', 'restart', 'frr']); };
-       }
-    }
+    
+    #return 
PVE::Network::SDN::Controllers::Frr::write_controller_config($class, 
$plugin_config, $config);
+    die "implemented in the Frr helper";
 }
 
 1;
diff --git a/src/PVE/Network/SDN/Controllers/Frr.pm 
b/src/PVE/Network/SDN/Controllers/Frr.pm
new file mode 100644
index 000000000000..386dcae543e8
--- /dev/null
+++ b/src/PVE/Network/SDN/Controllers/Frr.pm
@@ -0,0 +1,296 @@
+package PVE::Network::SDN::Controllers::Frr;
+
+use strict;
+use warnings;
+
+use PVE::RESTEnvironment qw(log_warn);
+use PVE::Tools qw(file_get_contents file_set_contents);
+
+sub read_local_frr_config {
+    if (-e "/etc/frr/frr.conf.local") {
+       return file_get_contents("/etc/frr/frr.conf.local");
+    }
+};
+
+sub reload_controller {
+    my $conf_file = "/etc/frr/frr.conf";
+    my $bin_path = "/usr/lib/frr/frr-reload.py";
+
+    if (!-e $bin_path) {
+       log_warn("missing $bin_path. Please install frr-pythontools package");
+       return;
+    }
+
+    my $err = sub {
+       my $line = shift;
+       if ($line =~ /ERROR:/) {
+           warn "$line \n";
+       }
+    };
+
+    if (-e $conf_file && -e $bin_path) {
+       eval {
+           run_command([$bin_path, '--stdout', '--reload', $conf_file], 
outfunc => {}, errfunc => $err);
+       };
+       if ($@) {
+           warn "frr reload command fail. Restarting frr.";
+           eval { run_command(['systemctl', 'restart', 'frr']); };
+       }
+    }
+}
+
+sub generate_controller_rawconfig {
+    my ($config) = @_;
+
+    my $nodename = PVE::INotify::nodename();
+
+    my $final_config = [];
+    push @{$final_config}, "frr version 8.5.2";
+    push @{$final_config}, "frr defaults datacenter";
+    push @{$final_config}, "hostname $nodename";
+    push @{$final_config}, "log syslog informational";
+    push @{$final_config}, "service integrated-vtysh-config";
+    push @{$final_config}, "!";
+
+    my $local_conf = read_local_frr_config();
+    if ($local_conf) {
+       parse_merge_frr_local_config($config, $local_conf);
+    }
+
+    generate_frr_vrf($final_config, $config->{frr}->{vrf});
+    generate_frr_interfaces($final_config, $config->{frr_interfaces});
+    generate_frr_recurse($final_config, $config->{frr}, undef, 0);
+    generate_frr_list($final_config, $config->{frr_access_list}, 
"access-list");
+    generate_frr_list($final_config, $config->{frr_prefix_list}, "ip 
prefix-list");
+    generate_frr_list($final_config, $config->{frr_prefix_list_v6}, "ipv6 
prefix-list");
+    generate_frr_simple_list($final_config, $config->{frr_bgp_community_list});
+    generate_frr_routemap($final_config, $config->{frr_routemap});
+    generate_frr_simple_list($final_config, $config->{frr_ip_protocol});
+
+    push @{$final_config}, "!";
+    push @{$final_config}, "line vty";
+    push @{$final_config}, "!";
+
+    my $rawconfig = join("\n", @{$final_config});
+
+    return if !$rawconfig;
+    return $rawconfig;
+}
+
+sub parse_merge_frr_local_config {
+    my ($config, $local_conf) = @_;
+
+    my $section = \$config->{""};
+    my $router = undef;
+    my $routemap = undef;
+    my $routemap_config = ();
+    my $routemap_action = undef;
+
+    while ($local_conf =~ /^\s*(.+?)\s*$/gm) {
+        my $line = $1;
+       $line =~ s/^\s+|\s+$//g;
+
+       if ($line =~ m/^router (.+)$/) {
+           $router = $1;
+           $section = \$config->{'frr'}->{'router'}->{$router}->{""};
+           next;
+       } elsif ($line =~ m/^vrf (.+)$/) {
+           $section = \$config->{'frr'}->{'vrf'}->{$1};
+           next;
+       } elsif ($line =~ m/^interface (.+)$/) {
+           $section = \$config->{'frr_interfaces'}->{$1};
+           next;
+       } elsif ($line =~ m/^bgp community-list (.+)$/) {
+           push(@{$config->{'frr_bgp_community_list'}}, $line);
+           next;
+       } elsif ($line =~ m/address-family (.+)$/) {
+           $section = 
\$config->{'frr'}->{'router'}->{$router}->{'address-family'}->{$1};
+           next;
+       } elsif ($line =~ m/^route-map (.+) (permit|deny) (\d+)/) {
+           $routemap = $1;
+           $routemap_config = ();
+           $routemap_action = $2;
+           $section = \$config->{'frr_routemap'}->{$routemap};
+           next;
+       } elsif ($line =~ m/^access-list (.+) seq (\d+) (.+)$/) {
+           $config->{'frr_access_list'}->{$1}->{$2} = $3;
+           next;
+       } elsif ($line =~ m/^ip prefix-list (.+) seq (\d+) (.*)$/) {
+           $config->{'frr_prefix_list'}->{$1}->{$2} = $3;
+           next;
+       } elsif ($line =~ m/^ipv6 prefix-list (.+) seq (\d+) (.*)$/) {
+           $config->{'frr_prefix_list_v6'}->{$1}->{$2} = $3;
+           next;
+       } elsif($line =~ m/^exit-address-family$/) {
+           next;
+       } elsif($line =~ m/^exit$/) {
+           if($router) {
+               $section = \$config->{''};
+               $router = undef;
+           } elsif($routemap) {
+               push(@{$$section}, { rule => $routemap_config, action => 
$routemap_action });
+               $section = \$config->{''};
+               $routemap = undef;
+               $routemap_action = undef;
+               $routemap_config = ();
+           }
+           next;
+       } elsif($line =~ m/!/) {
+           next;
+       }
+
+       next if !$section;
+       if($routemap) {
+           push(@{$routemap_config}, $line);
+       } else {
+           push(@{$$section}, $line);
+       }
+    }
+}
+
+sub write_controller_config {
+    my ($config) = @_;
+
+    my $rawconfig = generate_controller_rawconfig($config);
+    return if !$rawconfig;
+    return if !-d "/etc/frr";
+
+    file_set_contents("/etc/frr/frr.conf", $rawconfig);
+}
+
+
+sub generate_frr_recurse{
+   my ($final_config, $content, $parentkey, $level) = @_;
+
+   my $keylist = {};
+   $keylist->{'address-family'} = 1;
+   $keylist->{router} = 1;
+
+   my $exitkeylist = {};
+   $exitkeylist->{'address-family'} = 1;
+
+   my $simple_exitkeylist = {};
+   $simple_exitkeylist->{router} = 1;
+
+   # FIXME: make this generic
+   my $paddinglevel = undef;
+   if ($level == 1 || $level == 2) {
+       $paddinglevel = $level - 1;
+   } elsif ($level == 3 || $level ==  4) {
+       $paddinglevel = $level - 2;
+   }
+
+   my $padding = "";
+   $padding = ' ' x ($paddinglevel) if $paddinglevel;
+
+   if (ref $content eq  'HASH') {
+       foreach my $key (sort keys %$content) {
+           next if $key eq 'vrf';
+           if ($parentkey && defined($keylist->{$parentkey})) {
+               push @{$final_config}, $padding."!";
+               push @{$final_config}, $padding."$parentkey $key";
+           } elsif ($key ne '' && !defined($keylist->{$key})) {
+               push @{$final_config}, $padding."$key";
+           }
+
+           my $option = $content->{$key};
+           generate_frr_recurse($final_config, $option, $key, $level+1);
+
+           push @{$final_config}, $padding."exit-$parentkey" if $parentkey && 
defined($exitkeylist->{$parentkey});
+           push @{$final_config}, $padding."exit" if $parentkey && 
defined($simple_exitkeylist->{$parentkey});
+       }
+    }
+
+    if (ref $content eq 'ARRAY') {
+       push @{$final_config}, map { $padding . "$_" } @$content;
+    }
+}
+
+sub generate_frr_vrf {
+   my ($final_config, $vrfs) = @_;
+
+   return if !$vrfs;
+
+   my @config = ();
+
+   foreach my $id (sort keys %$vrfs) {
+       my $vrf = $vrfs->{$id};
+       push @config, "!";
+       push @config, "vrf $id";
+       foreach my $rule (@$vrf) {
+           push @config, " $rule";
+
+       }
+       push @config, "exit-vrf";
+    }
+
+    push @{$final_config}, @config;
+}
+
+sub generate_frr_simple_list {
+   my ($final_config, $rules) = @_;
+
+   return if !$rules;
+
+   my @config = ();
+   push @{$final_config}, "!";
+   foreach my $rule (sort @$rules) {
+       push @{$final_config}, $rule;
+   }
+}
+
+sub generate_frr_list {
+    my ($final_config, $lists, $type) = @_;
+
+    my $config = [];
+
+    for my $id (sort keys %$lists) {
+       my $list = $lists->{$id};
+
+       for my $seq (sort keys %$list) {
+           my $rule = $list->{$seq};
+           push @$config, "$type $id seq $seq $rule";
+       }
+    }
+
+    if (@$config > 0) {
+       push @{$final_config}, "!", @$config;
+    }
+}
+
+
+sub generate_frr_interfaces {
+   my ($final_config, $interfaces) = @_;
+
+   foreach my $k (sort keys %$interfaces) {
+       my $iface = $interfaces->{$k};
+       push @{$final_config}, "!";
+       push @{$final_config}, "interface $k";
+       foreach my $rule (sort @$iface) {
+           push @{$final_config}, " $rule";
+       }
+   }
+}
+
+sub generate_frr_routemap {
+   my ($final_config, $routemaps) = @_;
+
+   foreach my $id (sort keys %$routemaps) {
+
+       my $routemap = $routemaps->{$id};
+       my $order = 0;
+       foreach my $seq (@$routemap) {
+               $order++;
+               next if !defined($seq->{action});
+               my @config = ();
+               push @config, "!";
+               push @config, "route-map $id $seq->{action} $order";
+               my $rule = $seq->{rule};
+               push @config, map { " $_" } @$rule;
+               push @{$final_config}, @config;
+               push @{$final_config}, "exit";
+       }
+   }
+}
+
+1;
diff --git a/src/PVE/Network/SDN/Controllers/IsisPlugin.pm 
b/src/PVE/Network/SDN/Controllers/IsisPlugin.pm
index 97c6876db303..50a11742fff6 100644
--- a/src/PVE/Network/SDN/Controllers/IsisPlugin.pm
+++ b/src/PVE/Network/SDN/Controllers/IsisPlugin.pm
@@ -7,6 +7,7 @@ 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::Frr;
 use PVE::Network::SDN::Controllers::Plugin;
 use PVE::Network::SDN::Zones::Plugin;
 use Net::IP;
@@ -113,19 +114,22 @@ sub on_update_hook {
     }
 }
 
+sub reload_controller {
+    my ($class) = @_;
+    #return PVE::Network::SDN::Controllers::Frr::reload_controller($class);
+    die "implemented in the Frr helper";
+}
+
 sub generate_controller_rawconfig {
     my ($class, $plugin_config, $config) = @_;
-    return "";
+    #return 
PVE::Network::SDN::Controllers::Frr::generate_controller_rawconfig($class, 
$plugin_config, $config);
+    die "implemented in the Frr helper";
 }
 
 sub write_controller_config {
     my ($class, $plugin_config, $config) = @_;
-    return;
-}
-
-sub reload_controller {
-    my ($class) = @_;
-    return;
+    #return 
PVE::Network::SDN::Controllers::Frr::write_controller_config($class, 
$plugin_config, $config);
+    die "implemented in the Frr helper";
 }
 
 1;
diff --git a/src/PVE/Network/SDN/Controllers/Makefile 
b/src/PVE/Network/SDN/Controllers/Makefile
index fd9f881a0ad2..3b0387913cdc 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 
Frr.pm
 
 
 PERL5DIR=${DESTDIR}/usr/share/perl5
diff --git a/src/PVE/Network/SDN/Zones/EvpnPlugin.pm 
b/src/PVE/Network/SDN/Zones/EvpnPlugin.pm
index 4843756a75bd..6212ba0a02d9 100644
--- a/src/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/src/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -323,6 +323,21 @@ sub vnet_update_hook {
     }
 }
 
+sub reload_controller {
+    my ($class) = @_;
+    die "implemented in the Frr helper";
+}
+
+sub generate_controller_rawconfig {
+    my ($class, $config) = @_;
+    die "implemented in the Frr helper";
+}
+
+sub write_controller_config {
+    my ($class, $config) = @_;
+    die "implemented in the Frr helper";
+}
+
 1;
 
 
-- 
2.39.5



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

Reply via email to