The frr object in perl which stores the whole frr config is now also
modeled in rust, so it changed a bit. Adjust the frr.conf.local merging
code so that the frr.conf.local is still merged correctly. This makes
use of the `custom_frr_config` properties scattered in many rust types.
So if we encounter a line in frr.conf.local that we need to merge, we
just throw it into this string vec and render it as-is.

Co-authored-by: Stefan Hanreich <[email protected]>
Signed-off-by: Gabriel Goller <[email protected]>
---
 src/PVE/Network/SDN/Controllers/EvpnPlugin.pm |   4 +-
 src/PVE/Network/SDN/Frr.pm                    | 204 +++++++++++++++---
 2 files changed, 171 insertions(+), 37 deletions(-)

diff --git a/src/PVE/Network/SDN/Controllers/EvpnPlugin.pm 
b/src/PVE/Network/SDN/Controllers/EvpnPlugin.pm
index 3ea3ce2f033a..1f221807cb7b 100644
--- a/src/PVE/Network/SDN/Controllers/EvpnPlugin.pm
+++ b/src/PVE/Network/SDN/Controllers/EvpnPlugin.pm
@@ -377,10 +377,10 @@ sub generate_zone_frr_config {
             my $main_bgp_router = 
$config->{frr}->{bgp}->{vrf_router}->{'default'};
             if ($main_bgp_router) {
                 $main_bgp_router->{address_families}->{ipv4_unicast} //= {};
-                push(@ 
{$main_bgp_router->{address_families}->{ipv4_unicast}->{import_vrf} }, $vrf);
+                push(@{ 
$main_bgp_router->{address_families}->{ipv4_unicast}->{import_vrf} }, $vrf);
 
                 $main_bgp_router->{address_families}->{ipv6_unicast} //= {};
-                push(@ 
{$main_bgp_router->{address_families}->{ipv6_unicast}->{import_vrf} }, $vrf);
+                push(@{ 
$main_bgp_router->{address_families}->{ipv6_unicast}->{import_vrf} }, $vrf);
             }
 
             # Redistribute connected in VRF router
diff --git a/src/PVE/Network/SDN/Frr.pm b/src/PVE/Network/SDN/Frr.pm
index f084ad5a578f..99002c6b65bf 100644
--- a/src/PVE/Network/SDN/Frr.pm
+++ b/src/PVE/Network/SDN/Frr.pm
@@ -197,7 +197,7 @@ numbers to be consecutive, starting from 1 and incrementing 
by 1 for each entry.
 sub fix_routemap_seqs {
     my ($frr_config) = @_;
 
-    my $routemaps = $frr_config->{'frr'}->{'bgp'}->{'routemaps'};
+    my $routemaps = $frr_config->{'frr'}->{'routemaps'};
 
     foreach my $id (sort keys %$routemaps) {
         my $routemap = $routemaps->{$id};
@@ -270,70 +270,204 @@ sub append_local_config {
     return if !$local_config;
 
     my $section = \$frr_config->{""};
-    my $router = undef;
+    my $isis_router_name = undef;
+    my $bgp_router_asn = undef;
+    my $bgp_router_vrf = undef;
     my $routemap = undef;
-    my $routemap_config = ();
-    my $routemap_action = undef;
+    my $interface = undef;
+    my $vrf = undef;
+    my $new_block = 0;
+    my $new_af_block = 0;
 
-    while ($local_config =~ /^\s*(.+?)\s*$/gm) {
+    while ($local_config =~ /^(.+?)\s*$/gm) {
         my $line = $1;
-        $line =~ s/^\s+|\s+$//g;
-
-        if ($line =~ m/^router (.+)$/) {
-            $router = $1;
-            $section = \$frr_config->{'frr'}->{'router'}->{$router}->{""};
+        $line =~ s/\s+$//g;
+
+        if ($line =~ m/^router isis (.+)$/) {
+            $isis_router_name = $1;
+            if (defined 
$frr_config->{'frr'}->{'isis'}->{'router'}->{$isis_router_name}) {
+                $section =
+                    
\($frr_config->{'frr'}->{'isis'}->{'router'}->{$isis_router_name}
+                        ->{'custom_frr_config'} //= []);
+            } else {
+                $new_block = 1;
+                push(
+                    $frr_config->{'frr'}->{'custom_frr_config'}->@*,
+                    "router isis $isis_router_name",
+                );
+                $section = \$frr_config->{'frr'}->{'custom_frr_config'};
+            }
+            next;
+        } elsif ($line =~ m/^router bgp (\S+)(?: vrf (.+))?$/) {
+            $bgp_router_asn = $1;
+            $bgp_router_vrf = $2 // 'default';
+
+            my $config_line =
+                defined($2)
+                ? "router bgp $bgp_router_asn vrf $bgp_router_vrf"
+                : "router bgp $bgp_router_asn";
+
+            if (
+                defined 
$frr_config->{'frr'}->{'bgp'}->{'vrf_router'}->{$bgp_router_vrf}
+                and 
$frr_config->{'frr'}->{'bgp'}->{'vrf_router'}->{$bgp_router_vrf}->{'asn'}
+                eq $bgp_router_asn
+            ) {
+                $section =
+                    
\($frr_config->{'frr'}->{'bgp'}->{'vrf_router'}->{$bgp_router_vrf}
+                        ->{'custom_frr_config'} //= []);
+            } else {
+                $new_block = 1;
+                push(
+                    $frr_config->{'frr'}->{'custom_frr_config'}->@*, 
$config_line,
+                );
+                $section = \$frr_config->{'frr'}->{'custom_frr_config'};
+            }
             next;
         } elsif ($line =~ m/^vrf (.+)$/) {
-            $section = \$frr_config->{'frr'}->{'vrf'}->{$1};
+            $vrf = $1;
+            if (defined $frr_config->{'frr'}->{'bgp'}->{'vrfs'}->{$vrf}) {
+                $section = 
\$frr_config->{'frr'}->{'bgp'}->{'vrfs'}->{$vrf}->{'custom_frr_config'};
+            } else {
+                $new_block = 1;
+                push($frr_config->{'frr'}->{'custom_frr_config'}->@*, "vrf 
$vrf");
+                $section = \$frr_config->{'frr'}->{'custom_frr_config'};
+            }
             next;
         } elsif ($line =~ m/^interface (.+)$/) {
-            $section = \$frr_config->{'frr_interfaces'}->{$1};
+            $interface = $1;
+            if (defined 
$frr_config->{'frr'}->{'isis'}->{'interfaces'}->{$interface}) {
+                $section = 
\($frr_config->{'frr'}->{'isis'}->{'interfaces'}->{$interface}
+                    ->{'custom_frr_config'} //= []);
+            } else {
+                $new_block = 1;
+                push(
+                    $frr_config->{'frr'}->{'custom_frr_config'}->@*, 
"interface $interface",
+                );
+                $section = \$frr_config->{'frr'}->{'custom_frr_config'};
+            }
             next;
         } elsif ($line =~ m/^bgp community-list (.+)$/) {
-            push(@{ $frr_config->{'frr_bgp_community_list'} }, $line);
+            push(@{ $frr_config->{'frr'}->{'custom_frr_config'} }, $line);
             next;
         } elsif ($line =~ m/address-family (.+)$/) {
-            $section = 
\$frr_config->{'frr'}->{'router'}->{$router}->{'address-family'}->{$1};
+            # convert the address family from frr (e.g. l2vpn evpn) into the 
rust property (e.g. l2vpn_evpn)
+            my $address_family_unchanged = $1;
+            my $address_family = $1 =~ s/ /_/gr;
+
+            if (
+                defined 
$frr_config->{'frr'}->{'bgp'}->{'vrf_router'}->{$bgp_router_vrf}
+                and 
$frr_config->{'frr'}->{'bgp'}->{'vrf_router'}->{$bgp_router_vrf}->{'asn'}
+                eq $bgp_router_asn
+            ) {
+                if (
+                    defined 
$frr_config->{'frr'}->{'bgp'}->{'vrf_router'}->{$bgp_router_vrf}
+                    ->{'address_families'}->{$address_family}
+                ) {
+                    $section =
+                        
\($frr_config->{'frr'}->{'bgp'}->{'vrf_router'}->{$bgp_router_vrf}
+                            
->{'address_families'}->{$address_family}->{custom_frr_config} //= []);
+                } else {
+                    $new_af_block = 1;
+                    push(
+                        
$frr_config->{'frr'}->{'bgp'}->{'vrf_router'}->{$bgp_router_vrf}
+                            ->{'custom_frr_config'}->@*,
+                        " address-family $address_family_unchanged",
+                    );
+                    $section = 
\$frr_config->{'frr'}->{'bgp'}->{'vrf_router'}->{$bgp_router_vrf}
+                        ->{'custom_frr_config'};
+                }
+            } else {
+                $new_af_block = 1;
+                push(
+                    $frr_config->{'frr'}->{'custom_frr_config'}->@*,
+                    " address-family $address_family_unchanged",
+                );
+                $section = \$frr_config->{'frr'}->{'custom_frr_config'};
+            }
             next;
         } elsif ($line =~ m/^route-map (.+) (permit|deny) (\d+)/) {
             $routemap = $1;
-            $routemap_config = ();
-            $routemap_action = $2;
-            $section = \$frr_config->{'frr_routemap'}->{$routemap};
+            my $routemap_action = $2;
+            my $seq_number = $3;
+            if (defined $frr_config->{'frr'}->{'routemaps'}->{$routemap}) {
+                my $index = 0;
+                foreach my $single_routemap 
($frr_config->{'frr'}->{'routemaps'}->{$routemap}->@*) {
+                    if (
+                        $single_routemap->{'seq'} == $seq_number
+                        && $single_routemap->{'action'} eq $routemap_action
+                    ) {
+                        last;
+                    }
+                    $index++;
+                }
+                if ($index < scalar @{ 
$frr_config->{'frr'}->{'routemaps'}->{$routemap} }) {
+                    $section = 
\($frr_config->{'frr'}->{'routemaps'}->{$routemap}->[$index]
+                        ->{'custom_frr_config'} //= []);
+                } else {
+                    $new_block = 1;
+                    push(
+                        $frr_config->{'frr'}->{'custom_frr_config'}->@*,
+                        "route-map $routemap $routemap_action $seq_number",
+                    );
+                    $section = \$frr_config->{'frr'}->{'custom_frr_config'};
+                }
+            } else {
+                $new_block = 1;
+                push(
+                    $frr_config->{'frr'}->{'custom_frr_config'}->@*,
+                    "route-map $routemap $routemap_action $seq_number",
+                );
+                $section = \$frr_config->{'frr'}->{'custom_frr_config'};
+            }
             next;
         } elsif ($line =~ m/^access-list (.+) seq (\d+) (.+)$/) {
-            $frr_config->{'frr_access_list'}->{$1}->{$2} = $3;
+            push($frr_config->{'frr'}->{'custom_frr_config'}->@*, $line);
             next;
         } elsif ($line =~ m/^ip prefix-list (.+) seq (\d+) (.*)$/) {
-            $frr_config->{'frr_prefix_list'}->{$1}->{$2} = $3;
+            push($frr_config->{'frr'}->{'custom_frr_config'}->@*, $line);
             next;
         } elsif ($line =~ m/^ipv6 prefix-list (.+) seq (\d+) (.*)$/) {
-            $frr_config->{'frr_prefix_list_v6'}->{$1}->{$2} = $3;
+            push($frr_config->{'frr'}->{'custom_frr_config'}->@*, $line);
             next;
-        } elsif ($line =~ m/^exit-address-family$/) {
+        } elsif ($line =~ m/exit-address-family$/) {
+            if ($new_af_block) {
+                push(@{$$section}, $line);
+                $section = 
\$frr_config->{'frr'}->{'bgp'}->{'custom_frr_config'};
+            } else {
+                $section =
+                    
\($frr_config->{'frr'}->{'bgp'}->{'vrf_router'}->{$bgp_router_vrf}
+                        ->{'custom_frr_config'} //= []);
+            }
+            $new_af_block = 0;
             next;
-        } elsif ($line =~ m/^exit$/) {
-            if ($router) {
-                $section = \$frr_config->{''};
-                $router = undef;
-            } elsif ($routemap) {
-                push(@{$$section}, { rule => $routemap_config, action => 
$routemap_action });
-                $section = \$frr_config->{''};
+        } elsif ($line =~ m/^exit/) {
+            if ($bgp_router_vrf || $vrf || $interface || $routemap || 
$isis_router_name) {
+                # this means we just added a new router/vrf/interface/routemap
+                if ($new_block) {
+                    push(@{$$section}, $line);
+                    push(@{$$section}, "!");
+                }
+                $section = \$frr_config->{'frr'}->{'custom_frr_config'};
+                # we can't stack these, so exit out of all of them 
(technically we can have a vrf inside of a router bgp block, but we don't 
support that)
+                $isis_router_name = undef;
+                $bgp_router_vrf = undef;
+                $bgp_router_asn = undef;
+                $vrf = undef;
+                $interface = undef;
                 $routemap = undef;
-                $routemap_action = undef;
-                $routemap_config = ();
+            } else {
+                $section = \$frr_config->{'frr'}->{'custom_frr_config'};
+                push(@{$$section}, $line);
+                push(@{$$section}, "!");
             }
+            $new_block = 0;
             next;
         } elsif ($line =~ m/!/) {
             next;
         }
 
         next if !$section;
-        if ($routemap) {
-            push(@{$routemap_config}, $line);
-        } else {
-            push(@{$$section}, $line);
-        }
+        push(@{$$section}, $line);
     }
 }
 
-- 
2.47.3




Reply via email to