feat: enhance BGP template and peer configuration handling. Introduce policies_json parameter for local IP and ASN overrides in BGP speakers. Implement BGP template rendering and update peer configuration to utilize templates, improving flexibility in BGP setup. Add tests for new rendering logic and ensure proper integration with existing configurations.
CI / changes (push) Successful in 6s
CI / openapi (push) Successful in 27s
CI / go (push) Successful in 23s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, , evobgp-web) (push) Has been skipped
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Has been skipped
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Successful in 15s
CI / docker-go (deploy/docker/evobgp-agent/Dockerfile, , evobgp-agent) (push) Successful in 1m1s
CI / docker-go (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, , evobgp-all) (push) Successful in 1m26s
CI / docker-go (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, , evobgp-api) (push) Successful in 1m33s
CI / docker-go (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, , evobgp-deploy) (push) Successful in 1m21s
CI / docker-go (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, , evobgp-ingest) (push) Successful in 1m24s
CI / docker-go (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, , evobgp-node) (push) Successful in 1m25s
CI / docker-go (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, , evobgp-render) (push) Successful in 1m28s
CI / docker-go (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, , evobgp-scheduler) (push) Successful in 1m21s
CI / changes (push) Successful in 6s
CI / openapi (push) Successful in 27s
CI / go (push) Successful in 23s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, , evobgp-web) (push) Has been skipped
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Has been skipped
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Successful in 15s
CI / docker-go (deploy/docker/evobgp-agent/Dockerfile, , evobgp-agent) (push) Successful in 1m1s
CI / docker-go (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, , evobgp-all) (push) Successful in 1m26s
CI / docker-go (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, , evobgp-api) (push) Successful in 1m33s
CI / docker-go (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, , evobgp-deploy) (push) Successful in 1m21s
CI / docker-go (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, , evobgp-ingest) (push) Successful in 1m24s
CI / docker-go (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, , evobgp-node) (push) Successful in 1m25s
CI / docker-go (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, , evobgp-render) (push) Successful in 1m28s
CI / docker-go (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, , evobgp-scheduler) (push) Successful in 1m21s
This commit is contained in:
+10
-1
@@ -577,6 +577,13 @@ components:
|
|||||||
bgp_speaker_id:
|
bgp_speaker_id:
|
||||||
type: ["string", "null"]
|
type: ["string", "null"]
|
||||||
description: "`null` - политика для всех спикеров."
|
description: "`null` - политика для всех спикеров."
|
||||||
|
policies_json:
|
||||||
|
type: string
|
||||||
|
description: >
|
||||||
|
JSON-объект (строка). Поля `local_ipv4`, `local_ipv6`, `local_asn` переопределяют
|
||||||
|
глобальные `bird_local_ipv4` / `bird_local_ipv6` / `bird_local_asn` тенанта.
|
||||||
|
Если эффективный локальный адрес или ASN пира отличается от значений в шаблоне BIRD,
|
||||||
|
в сгенерированный блок `protocol bgp … from bgp_template` добавляется строка `local … as …`.
|
||||||
additionalProperties: true
|
additionalProperties: true
|
||||||
|
|
||||||
BgpSpeaker:
|
BgpSpeaker:
|
||||||
@@ -1744,7 +1751,9 @@ paths:
|
|||||||
patch:
|
patch:
|
||||||
tags: [Peers]
|
tags: [Peers]
|
||||||
summary: Обновить пира
|
summary: Обновить пира
|
||||||
description: Политики, neighbor, ASN, привязка к `bgp_speaker_id` или `null` для всех спикеров.
|
description: >
|
||||||
|
Политики (`policies_json`: `local_ipv4`, `local_ipv6`, `local_asn`), neighbor, ASN,
|
||||||
|
привязка к `bgp_speaker_id` или `null` для всех спикеров.
|
||||||
operationId: patchPeer
|
operationId: patchPeer
|
||||||
parameters:
|
parameters:
|
||||||
- $ref: "#/components/parameters/IdempotencyKey"
|
- $ref: "#/components/parameters/IdempotencyKey"
|
||||||
|
|||||||
@@ -5,6 +5,116 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// BGP template names referenced by generated peers (BIRD 2).
|
||||||
|
const (
|
||||||
|
BGPTemplateNameV4 = "bgp_template"
|
||||||
|
BGPTemplateNameV6 = "bgp_template_v6"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BGPTemplatesOptions holds tenant defaults for template bgp bgp_template (+ v6 mirror).
|
||||||
|
type BGPTemplatesOptions struct {
|
||||||
|
LocalIPv4 string
|
||||||
|
LocalIPv6 string
|
||||||
|
LocalASN uint32
|
||||||
|
ExportFilterV4 string
|
||||||
|
ExportFilterV6 string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderBGPTemplates renders two template bgp blocks (IPv4 and IPv6 AFI).
|
||||||
|
func RenderBGPTemplates(opts BGPTemplatesOptions) (string, error) {
|
||||||
|
if strings.TrimSpace(opts.LocalIPv4) == "" || strings.TrimSpace(opts.LocalIPv6) == "" {
|
||||||
|
return "", fmt.Errorf("birdfmt: template local IPv4 and IPv6 are required")
|
||||||
|
}
|
||||||
|
if opts.LocalASN == 0 {
|
||||||
|
return "", fmt.Errorf("birdfmt: template local ASN must be non-zero")
|
||||||
|
}
|
||||||
|
exp4 := "all"
|
||||||
|
if strings.TrimSpace(opts.ExportFilterV4) != "" {
|
||||||
|
exp4 = "filter " + strings.TrimSpace(opts.ExportFilterV4)
|
||||||
|
}
|
||||||
|
exp6 := "all"
|
||||||
|
if strings.TrimSpace(opts.ExportFilterV6) != "" {
|
||||||
|
exp6 = "filter " + strings.TrimSpace(opts.ExportFilterV6)
|
||||||
|
}
|
||||||
|
var b strings.Builder
|
||||||
|
fmt.Fprintf(&b, "template bgp %s {\n", BGPTemplateNameV4)
|
||||||
|
b.WriteString(" local ")
|
||||||
|
b.WriteString(strings.TrimSpace(opts.LocalIPv4))
|
||||||
|
fmt.Fprintf(&b, " as %d;\n", opts.LocalASN)
|
||||||
|
b.WriteString(" ipv4 {\n")
|
||||||
|
b.WriteString(" import all;\n")
|
||||||
|
b.WriteString(" export ")
|
||||||
|
b.WriteString(exp4)
|
||||||
|
b.WriteString(";\n")
|
||||||
|
b.WriteString(" };\n")
|
||||||
|
b.WriteString("}\n\n")
|
||||||
|
fmt.Fprintf(&b, "template bgp %s {\n", BGPTemplateNameV6)
|
||||||
|
b.WriteString(" local ")
|
||||||
|
b.WriteString(strings.TrimSpace(opts.LocalIPv6))
|
||||||
|
fmt.Fprintf(&b, " as %d;\n", opts.LocalASN)
|
||||||
|
b.WriteString(" ipv6 {\n")
|
||||||
|
b.WriteString(" import all;\n")
|
||||||
|
b.WriteString(" export ")
|
||||||
|
b.WriteString(exp6)
|
||||||
|
b.WriteString(";\n")
|
||||||
|
b.WriteString(" };\n")
|
||||||
|
b.WriteString("}\n")
|
||||||
|
return b.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BGPPeerFromTemplateOptions describes protocol bgp NAME from TEMPLATE { … }.
|
||||||
|
type BGPPeerFromTemplateOptions struct {
|
||||||
|
ProtocolName string
|
||||||
|
TemplateName string
|
||||||
|
NeighborIP string
|
||||||
|
NeighborASN uint32
|
||||||
|
SourceAddress string
|
||||||
|
// If set, emits "local … as …" before neighbor (overrides template local/ASN for this peer).
|
||||||
|
OverrideLocalIP string
|
||||||
|
OverrideLocalASN uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenderProtocolBGPFromTemplate renders protocol bgp … from TEMPLATE { neighbor; multihop; source address; passive; }.
|
||||||
|
func RenderProtocolBGPFromTemplate(opts BGPPeerFromTemplateOptions) (string, error) {
|
||||||
|
if strings.TrimSpace(opts.ProtocolName) == "" {
|
||||||
|
return "", fmt.Errorf("birdfmt: protocol name is required")
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(opts.TemplateName) == "" {
|
||||||
|
return "", fmt.Errorf("birdfmt: template name is required")
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(opts.NeighborIP) == "" || strings.TrimSpace(opts.SourceAddress) == "" {
|
||||||
|
return "", fmt.Errorf("birdfmt: neighbor and source address are required")
|
||||||
|
}
|
||||||
|
if opts.NeighborASN == 0 {
|
||||||
|
return "", fmt.Errorf("birdfmt: neighbor ASN must be non-zero")
|
||||||
|
}
|
||||||
|
if opts.OverrideLocalIP != "" && opts.OverrideLocalASN == 0 {
|
||||||
|
return "", fmt.Errorf("birdfmt: override local ASN is required when override local IP is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
var b strings.Builder
|
||||||
|
b.WriteString("protocol bgp ")
|
||||||
|
b.WriteString(strings.TrimSpace(opts.ProtocolName))
|
||||||
|
b.WriteString(" from ")
|
||||||
|
b.WriteString(strings.TrimSpace(opts.TemplateName))
|
||||||
|
b.WriteString(" {\n")
|
||||||
|
if ip := strings.TrimSpace(opts.OverrideLocalIP); ip != "" {
|
||||||
|
b.WriteString(" local ")
|
||||||
|
b.WriteString(ip)
|
||||||
|
fmt.Fprintf(&b, " as %d;\n", opts.OverrideLocalASN)
|
||||||
|
}
|
||||||
|
b.WriteString(" neighbor ")
|
||||||
|
b.WriteString(strings.TrimSpace(opts.NeighborIP))
|
||||||
|
fmt.Fprintf(&b, " as %d;\n", opts.NeighborASN)
|
||||||
|
b.WriteString(" multihop;\n")
|
||||||
|
b.WriteString(" source address ")
|
||||||
|
b.WriteString(strings.TrimSpace(opts.SourceAddress))
|
||||||
|
b.WriteString(";\n")
|
||||||
|
b.WriteString(" passive;\n")
|
||||||
|
b.WriteString("}\n")
|
||||||
|
return b.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// BGPPeerIPv4Options describes a single BGP session (BIRD 2, IPv4 AF).
|
// BGPPeerIPv4Options describes a single BGP session (BIRD 2, IPv4 AF).
|
||||||
type BGPPeerIPv4Options struct {
|
type BGPPeerIPv4Options struct {
|
||||||
ProtocolName string // e.g. evobgp_peer_uplink
|
ProtocolName string // e.g. evobgp_peer_uplink
|
||||||
|
|||||||
@@ -27,3 +27,50 @@ func TestRenderProtocolBGPIPv4_ExportAll(t *testing.T) {
|
|||||||
t.Fatal(got)
|
t.Fatal(got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRenderBGPTemplates_Validation(t *testing.T) {
|
||||||
|
_, err := RenderBGPTemplates(BGPTemplatesOptions{})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderProtocolBGPFromTemplate_MultihopPassive(t *testing.T) {
|
||||||
|
got, err := RenderProtocolBGPFromTemplate(BGPPeerFromTemplateOptions{
|
||||||
|
ProtocolName: "evobgp_p_x",
|
||||||
|
TemplateName: BGPTemplateNameV4,
|
||||||
|
NeighborIP: "94.142.140.141",
|
||||||
|
NeighborASN: 65002,
|
||||||
|
SourceAddress: "77.232.38.173",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(got, "from bgp_template") {
|
||||||
|
t.Fatal(got)
|
||||||
|
}
|
||||||
|
if !strings.Contains(got, "multihop;") || !strings.Contains(got, "passive;") {
|
||||||
|
t.Fatal(got)
|
||||||
|
}
|
||||||
|
if !strings.Contains(got, "source address 77.232.38.173;") {
|
||||||
|
t.Fatal(got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderProtocolBGPFromTemplate_OverrideLocal(t *testing.T) {
|
||||||
|
got, err := RenderProtocolBGPFromTemplate(BGPPeerFromTemplateOptions{
|
||||||
|
ProtocolName: "p",
|
||||||
|
TemplateName: BGPTemplateNameV4,
|
||||||
|
NeighborIP: "192.0.2.2",
|
||||||
|
NeighborASN: 2,
|
||||||
|
SourceAddress: "10.0.0.1",
|
||||||
|
OverrideLocalIP: "10.0.0.1",
|
||||||
|
OverrideLocalASN: 65099,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(got, "local 10.0.0.1 as 65099;") {
|
||||||
|
t.Fatal(got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
// Package birdfmt builds BIRD 2 configuration text: main bird.conf skeleton, include
|
// Package birdfmt builds BIRD 2 configuration text: main bird.conf skeleton, include
|
||||||
// fragments under bird.d/, export filters, static route protocols, and minimal BGP peer
|
// fragments under bird.d/, export filters, static route protocols, BGP templates, and
|
||||||
// blocks. Runtime helpers parse-check configs (bird -p) and reload (birdc configure).
|
// BGP peer protocols (from template or standalone). Runtime helpers parse-check configs
|
||||||
|
// (bird -p) and reload (birdc configure).
|
||||||
//
|
//
|
||||||
// # Layout
|
// # Layout
|
||||||
//
|
//
|
||||||
// Operator keeps a stable bird.conf (or generated skeleton) next to EvoBGP fragments:
|
// Operator keeps a stable bird.conf (or generated skeleton) next to EvoBGP fragments:
|
||||||
//
|
//
|
||||||
// bird.conf — router id, protocol device, protocol direct, include lines
|
// bird.conf — router id, protocol device, protocol direct, include lines
|
||||||
// bird.d/evobgp_*.conf — generated prefixes, filters, peers (names from constants)
|
// bird.d/evobgp_*.conf — generated prefixes, filters, BGP templates, peers (names from constants)
|
||||||
//
|
//
|
||||||
// Include order should list filter definitions before protocols that reference them
|
// Include order should list filter definitions before protocols that reference them
|
||||||
// (see StandardIncludeFragments).
|
// (see StandardIncludeFragments).
|
||||||
|
|||||||
@@ -9,11 +9,12 @@ import (
|
|||||||
const (
|
const (
|
||||||
DirBirdD = "bird.d"
|
DirBirdD = "bird.d"
|
||||||
|
|
||||||
FragmentPrefixesV4 = "evobgp_prefixes_v4.conf"
|
FragmentPrefixesV4 = "evobgp_prefixes_v4.conf"
|
||||||
FragmentPrefixesV6 = "evobgp_prefixes_v6.conf"
|
FragmentPrefixesV6 = "evobgp_prefixes_v6.conf"
|
||||||
FragmentFiltersV4 = "evobgp_filters_v4.conf"
|
FragmentFiltersV4 = "evobgp_filters_v4.conf"
|
||||||
FragmentFiltersV6 = "evobgp_filters_v6.conf"
|
FragmentFiltersV6 = "evobgp_filters_v6.conf"
|
||||||
FragmentPeers = "evobgp_peers.conf"
|
FragmentBGPTemplate = "evobgp_bgp_template.conf"
|
||||||
|
FragmentPeers = "evobgp_peers.conf"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FragmentIncludePath returns a POSIX include path such as bird.d/evobgp_prefixes_v4.conf.
|
// FragmentIncludePath returns a POSIX include path such as bird.d/evobgp_prefixes_v4.conf.
|
||||||
@@ -24,11 +25,12 @@ func FragmentIncludePath(fragmentBaseName string) string {
|
|||||||
return DirBirdD + "/" + fragmentBaseName
|
return DirBirdD + "/" + fragmentBaseName
|
||||||
}
|
}
|
||||||
|
|
||||||
// StandardIncludeFragments is the recommended order: filters before peers that reference them.
|
// StandardIncludeFragments is the recommended order: filters before BGP templates and peers.
|
||||||
func StandardIncludeFragments() []string {
|
func StandardIncludeFragments() []string {
|
||||||
return []string{
|
return []string{
|
||||||
FragmentIncludePath(FragmentFiltersV4),
|
FragmentIncludePath(FragmentFiltersV4),
|
||||||
FragmentIncludePath(FragmentFiltersV6),
|
FragmentIncludePath(FragmentFiltersV6),
|
||||||
|
FragmentIncludePath(FragmentBGPTemplate),
|
||||||
FragmentIncludePath(FragmentPrefixesV4),
|
FragmentIncludePath(FragmentPrefixesV4),
|
||||||
FragmentIncludePath(FragmentPrefixesV6),
|
FragmentIncludePath(FragmentPrefixesV6),
|
||||||
FragmentIncludePath(FragmentPeers),
|
FragmentIncludePath(FragmentPeers),
|
||||||
|
|||||||
@@ -29,13 +29,24 @@ func TestStandardLayout_GeneratorMatchesFixtures(t *testing.T) {
|
|||||||
staticV6 := RenderStaticIPv6Protocol("evobgp_prefixes_v6", nil)
|
staticV6 := RenderStaticIPv6Protocol("evobgp_prefixes_v6", nil)
|
||||||
assertFileEquals(t, "testdata/scenarios/standard_layout/bird.d/evobgp_prefixes_v6.conf", staticV6)
|
assertFileEquals(t, "testdata/scenarios/standard_layout/bird.d/evobgp_prefixes_v6.conf", staticV6)
|
||||||
|
|
||||||
peer, err := RenderProtocolBGPIPv4(BGPPeerIPv4Options{
|
tpl, err := RenderBGPTemplates(BGPTemplatesOptions{
|
||||||
ProtocolName: "evobgp_peer_ci",
|
LocalIPv4: "192.0.2.1",
|
||||||
LocalIP: "192.0.2.1",
|
LocalIPv6: "2001:db8::1",
|
||||||
LocalASN: 65001,
|
LocalASN: 65001,
|
||||||
NeighborIP: "192.0.2.2",
|
ExportFilterV4: "evobgp_export_v4",
|
||||||
NeighborASN: 65002,
|
ExportFilterV6: "evobgp_export_v6",
|
||||||
ExportFilter: "evobgp_export_v4",
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
assertFileEquals(t, "testdata/scenarios/standard_layout/bird.d/evobgp_bgp_template.conf", tpl)
|
||||||
|
|
||||||
|
peer, err := RenderProtocolBGPFromTemplate(BGPPeerFromTemplateOptions{
|
||||||
|
ProtocolName: "evobgp_peer_ci",
|
||||||
|
TemplateName: BGPTemplateNameV4,
|
||||||
|
NeighborIP: "192.0.2.2",
|
||||||
|
NeighborASN: 65002,
|
||||||
|
SourceAddress: "192.0.2.1",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ router id 192.0.2.1;
|
|||||||
|
|
||||||
include "bird.d/evobgp_filters_v4.conf";
|
include "bird.d/evobgp_filters_v4.conf";
|
||||||
include "bird.d/evobgp_filters_v6.conf";
|
include "bird.d/evobgp_filters_v6.conf";
|
||||||
|
include "bird.d/evobgp_bgp_template.conf";
|
||||||
include "bird.d/evobgp_prefixes_v4.conf";
|
include "bird.d/evobgp_prefixes_v4.conf";
|
||||||
include "bird.d/evobgp_prefixes_v6.conf";
|
include "bird.d/evobgp_prefixes_v6.conf";
|
||||||
include "bird.d/evobgp_peers.conf";
|
include "bird.d/evobgp_peers.conf";
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ router id 192.0.2.1;
|
|||||||
|
|
||||||
include "bird.d/evobgp_filters_v4.conf";
|
include "bird.d/evobgp_filters_v4.conf";
|
||||||
include "bird.d/evobgp_filters_v6.conf";
|
include "bird.d/evobgp_filters_v6.conf";
|
||||||
|
include "bird.d/evobgp_bgp_template.conf";
|
||||||
include "bird.d/evobgp_prefixes_v4.conf";
|
include "bird.d/evobgp_prefixes_v4.conf";
|
||||||
include "bird.d/evobgp_prefixes_v6.conf";
|
include "bird.d/evobgp_prefixes_v6.conf";
|
||||||
include "bird.d/evobgp_peers.conf";
|
include "bird.d/evobgp_peers.conf";
|
||||||
|
|||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
template bgp bgp_template {
|
||||||
|
local 192.0.2.1 as 65001;
|
||||||
|
ipv4 {
|
||||||
|
import all;
|
||||||
|
export filter evobgp_export_v4;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template bgp bgp_template_v6 {
|
||||||
|
local 2001:db8::1 as 65001;
|
||||||
|
ipv6 {
|
||||||
|
import all;
|
||||||
|
export filter evobgp_export_v6;
|
||||||
|
};
|
||||||
|
}
|
||||||
+4
-6
@@ -1,8 +1,6 @@
|
|||||||
protocol bgp evobgp_peer_ci {
|
protocol bgp evobgp_peer_ci from bgp_template {
|
||||||
local 192.0.2.1 as 65001;
|
|
||||||
neighbor 192.0.2.2 as 65002;
|
neighbor 192.0.2.2 as 65002;
|
||||||
ipv4 {
|
multihop;
|
||||||
import all;
|
source address 192.0.2.1;
|
||||||
export filter evobgp_export_v4;
|
passive;
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -236,6 +236,16 @@ func buildPreviewFragments(st store.Backend, tenantID, moduleID, revisionID stri
|
|||||||
staticV6 := birdfmt.RenderStaticIPv6Routes("evobgp_prefixes_v6", sr6)
|
staticV6 := birdfmt.RenderStaticIPv6Routes("evobgp_prefixes_v6", sr6)
|
||||||
|
|
||||||
locals := birdLocalsFromStore(st, tenantID)
|
locals := birdLocalsFromStore(st, tenantID)
|
||||||
|
tplBody, err := birdfmt.RenderBGPTemplates(birdfmt.BGPTemplatesOptions{
|
||||||
|
LocalIPv4: locals.localV4,
|
||||||
|
LocalIPv6: locals.localV6,
|
||||||
|
LocalASN: locals.localASN,
|
||||||
|
ExportFilterV4: birdFilterNameV4,
|
||||||
|
ExportFilterV6: birdFilterNameV6,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
peersBody, err := renderPeersBirdFragment(st, tenantID, locals)
|
peersBody, err := renderPeersBirdFragment(st, tenantID, locals)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -252,6 +262,7 @@ func buildPreviewFragments(st store.Backend, tenantID, moduleID, revisionID stri
|
|||||||
|
|
||||||
p4 := birdfmt.FragmentIncludePath(birdfmt.FragmentFiltersV4)
|
p4 := birdfmt.FragmentIncludePath(birdfmt.FragmentFiltersV4)
|
||||||
p6 := birdfmt.FragmentIncludePath(birdfmt.FragmentFiltersV6)
|
p6 := birdfmt.FragmentIncludePath(birdfmt.FragmentFiltersV6)
|
||||||
|
pTpl := birdfmt.FragmentIncludePath(birdfmt.FragmentBGPTemplate)
|
||||||
px4 := birdfmt.FragmentIncludePath(birdfmt.FragmentPrefixesV4)
|
px4 := birdfmt.FragmentIncludePath(birdfmt.FragmentPrefixesV4)
|
||||||
px6 := birdfmt.FragmentIncludePath(birdfmt.FragmentPrefixesV6)
|
px6 := birdfmt.FragmentIncludePath(birdfmt.FragmentPrefixesV6)
|
||||||
pPeers := birdfmt.FragmentIncludePath(birdfmt.FragmentPeers)
|
pPeers := birdfmt.FragmentIncludePath(birdfmt.FragmentPeers)
|
||||||
@@ -260,6 +271,7 @@ func buildPreviewFragments(st store.Backend, tenantID, moduleID, revisionID stri
|
|||||||
"bird.conf": main,
|
"bird.conf": main,
|
||||||
p4: birdfmt.JoinFragments(birdfmt.ManagedBanner(revisionID), f4),
|
p4: birdfmt.JoinFragments(birdfmt.ManagedBanner(revisionID), f4),
|
||||||
p6: birdfmt.JoinFragments(birdfmt.ManagedBanner(revisionID), f6),
|
p6: birdfmt.JoinFragments(birdfmt.ManagedBanner(revisionID), f6),
|
||||||
|
pTpl: birdfmt.JoinFragments(birdfmt.ManagedBanner(revisionID), tplBody),
|
||||||
px4: birdfmt.JoinFragments(birdfmt.ManagedBanner(revisionID), staticV4),
|
px4: birdfmt.JoinFragments(birdfmt.ManagedBanner(revisionID), staticV4),
|
||||||
px6: birdfmt.JoinFragments(birdfmt.ManagedBanner(revisionID), staticV6),
|
px6: birdfmt.JoinFragments(birdfmt.ManagedBanner(revisionID), staticV6),
|
||||||
pPeers: peersBody,
|
pPeers: peersBody,
|
||||||
@@ -346,6 +358,30 @@ type peerPolicyJSON struct {
|
|||||||
LocalASN float64 `json:"local_asn"`
|
LocalASN float64 `json:"local_asn"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func effectivePeerLocals(loc birdLocals, pol peerPolicyJSON) (v4, v6 string, asn uint32) {
|
||||||
|
v4 = strings.TrimSpace(loc.localV4)
|
||||||
|
v6 = strings.TrimSpace(loc.localV6)
|
||||||
|
if s := strings.TrimSpace(pol.LocalIPv4); s != "" {
|
||||||
|
v4 = s
|
||||||
|
}
|
||||||
|
if s := strings.TrimSpace(pol.LocalIPv6); s != "" {
|
||||||
|
v6 = s
|
||||||
|
}
|
||||||
|
asn = loc.localASN
|
||||||
|
if pol.LocalASN >= 1 && pol.LocalASN <= 4294967295 {
|
||||||
|
asn = uint32(pol.LocalASN)
|
||||||
|
}
|
||||||
|
return v4, v6, asn
|
||||||
|
}
|
||||||
|
|
||||||
|
// peerNeedsLocalOverride is true when the peer's effective local IP or ASN differs from tenant defaults in the BGP template.
|
||||||
|
func peerNeedsLocalOverride(loc birdLocals, effLocal string, effASN uint32, ipv4 bool) bool {
|
||||||
|
if ipv4 {
|
||||||
|
return strings.TrimSpace(effLocal) != strings.TrimSpace(loc.localV4) || effASN != loc.localASN
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(effLocal) != strings.TrimSpace(loc.localV6) || effASN != loc.localASN
|
||||||
|
}
|
||||||
|
|
||||||
func renderPeersBirdFragment(st store.Backend, tenantID string, loc birdLocals) (string, error) {
|
func renderPeersBirdFragment(st store.Backend, tenantID string, loc birdLocals) (string, error) {
|
||||||
peers := st.ListPeers(tenantID)
|
peers := st.ListPeers(tenantID)
|
||||||
var parts []string
|
var parts []string
|
||||||
@@ -362,29 +398,22 @@ func renderPeersBirdFragment(st store.Backend, tenantID string, loc birdLocals)
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
pol := parsePeerPolicies(p.PoliciesJSON)
|
pol := parsePeerPolicies(p.PoliciesJSON)
|
||||||
lv4 := loc.localV4
|
lv4, lv6, asn := effectivePeerLocals(loc, pol)
|
||||||
if strings.TrimSpace(pol.LocalIPv4) != "" {
|
|
||||||
lv4 = strings.TrimSpace(pol.LocalIPv4)
|
|
||||||
}
|
|
||||||
lv6 := loc.localV6
|
|
||||||
if strings.TrimSpace(pol.LocalIPv6) != "" {
|
|
||||||
lv6 = strings.TrimSpace(pol.LocalIPv6)
|
|
||||||
}
|
|
||||||
asn := loc.localASN
|
|
||||||
if pol.LocalASN >= 1 && pol.LocalASN <= 4294967295 {
|
|
||||||
asn = uint32(pol.LocalASN)
|
|
||||||
}
|
|
||||||
proto := peerProtocolName(p.ID)
|
proto := peerProtocolName(p.ID)
|
||||||
ra := uint32(p.RemoteASN)
|
ra := uint32(p.RemoteASN)
|
||||||
if addr.Is4() {
|
if addr.Is4() {
|
||||||
s, err := birdfmt.RenderProtocolBGPIPv4(birdfmt.BGPPeerIPv4Options{
|
opts := birdfmt.BGPPeerFromTemplateOptions{
|
||||||
ProtocolName: proto,
|
ProtocolName: proto,
|
||||||
LocalIP: lv4,
|
TemplateName: birdfmt.BGPTemplateNameV4,
|
||||||
LocalASN: asn,
|
NeighborIP: addr.String(),
|
||||||
NeighborIP: addr.String(),
|
NeighborASN: ra,
|
||||||
NeighborASN: ra,
|
SourceAddress: lv4,
|
||||||
ExportFilter: birdFilterNameV4,
|
}
|
||||||
})
|
if peerNeedsLocalOverride(loc, lv4, asn, true) {
|
||||||
|
opts.OverrideLocalIP = lv4
|
||||||
|
opts.OverrideLocalASN = asn
|
||||||
|
}
|
||||||
|
s, err := birdfmt.RenderProtocolBGPFromTemplate(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -392,14 +421,18 @@ func renderPeersBirdFragment(st store.Backend, tenantID string, loc birdLocals)
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if addr.Is6() {
|
if addr.Is6() {
|
||||||
s, err := birdfmt.RenderProtocolBGPIPv6(birdfmt.BGPPeerIPv6Options{
|
opts := birdfmt.BGPPeerFromTemplateOptions{
|
||||||
ProtocolName: proto,
|
ProtocolName: proto,
|
||||||
LocalIP: lv6,
|
TemplateName: birdfmt.BGPTemplateNameV6,
|
||||||
LocalASN: asn,
|
NeighborIP: addr.String(),
|
||||||
NeighborIP: addr.String(),
|
NeighborASN: ra,
|
||||||
NeighborASN: ra,
|
SourceAddress: lv6,
|
||||||
ExportFilter: birdFilterNameV6,
|
}
|
||||||
})
|
if peerNeedsLocalOverride(loc, lv6, asn, false) {
|
||||||
|
opts.OverrideLocalIP = lv6
|
||||||
|
opts.OverrideLocalASN = asn
|
||||||
|
}
|
||||||
|
s, err := birdfmt.RenderProtocolBGPFromTemplate(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user