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
