| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- package dynamicrouting
- import (
- "os"
- "os/exec"
- "strings"
- "testing"
- )
- func TestAllProtocolsBirdConfigGeneration(t *testing.T) {
- mgr := &Manager{
- config: DynamicConfig{
- BGPInstance: BGPInstance{
- Enabled: true,
- AS: 65001,
- RouterID: "10.42.62.254",
- },
- BGPPeers: []BGPPeer{
- {
- ID: "peer-1",
- Name: "Uplink",
- RemoteAddress: "10.42.36.253",
- RemoteAS: 65002,
- UseBFD: true,
- },
- },
- OSPFInstances: []OSPFInstance{
- {
- ID: "ospf-1",
- Name: "Default",
- Version: 2,
- RouterID: "10.42.62.254",
- Enabled: true,
- },
- },
- OSPFAreas: []OSPFArea{
- {
- ID: "area-0",
- Name: "Default",
- AreaID: "0.0.0.0",
- AreaType: "default",
- },
- },
- OSPFInterfaces: []OSPFInterface{
- {
- ID: "ospf-if-1",
- Interfaces: "eth0",
- Area: "Default",
- Cost: 10,
- UseBFD: true,
- },
- },
- BFDInstance: BFDInstance{
- Enabled: true,
- },
- BFDInterfaces: []BFDInterface{
- {
- ID: "bfd-if-1",
- Interface: "eth0",
- MinRxInterval: 100,
- MinTxInterval: 100,
- Multiplier: 3,
- },
- },
- BFDNeighbors: []BFDNeighbor{
- {
- ID: "bfd-nbr-1",
- Address: "10.42.36.253",
- Interface: "eth0",
- },
- },
- RIPInstances: []RIPInstance{
- {
- ID: "rip-1",
- Name: "default",
- Version: 2,
- Enabled: true,
- RedistributeConnected: true,
- RedistributeStatic: true,
- },
- },
- RIPInterfaces: []RIPInterface{
- {
- ID: "rip-if-1",
- Instance: "default",
- Interface: "eth0",
- Metric: 1,
- Mode: "multicast",
- UseBFD: true,
- },
- },
- BabelInstances: []BabelInstance{
- {
- ID: "babel-1",
- Name: "default",
- RouterID: "10.42.62.254",
- Enabled: true,
- RedistributeConnected: true,
- },
- },
- BabelInterfaces: []BabelInterface{
- {
- ID: "babel-if-1",
- Instance: "default",
- Interface: "eth0",
- Type: "wired",
- RxCost: 10,
- HelloInterval: 4,
- UpdateInterval: 16,
- },
- },
- RAdvInstances: []RAdvInstance{
- {
- ID: "radv-1",
- Name: "default",
- Enabled: true,
- },
- },
- RAdvInterfaces: []RAdvInterface{
- {
- ID: "radv-if-1",
- Instance: "default",
- Interface: "eth0",
- MinRAInterval: 200,
- MaxRAInterval: 600,
- DefaultLifetime: 1800,
- Prefixes: []string{"2001:db8:1::/64"},
- RDNSS: []string{"2001:4860:4860::8888"},
- },
- },
- },
- }
- cfg := mgr.generateBirdConfig()
- // Verify all protocol blocks are generated
- if !strings.Contains(cfg, "protocol bfd bfd_main") {
- t.Errorf("Expected protocol bfd bfd_main in config, got:\n%s", cfg)
- }
- if !strings.Contains(cfg, "protocol rip rip_default") {
- t.Errorf("Expected protocol rip in config, got:\n%s", cfg)
- }
- if !strings.Contains(cfg, "protocol babel babel_default") {
- t.Errorf("Expected protocol babel in config, got:\n%s", cfg)
- }
- if !strings.Contains(cfg, "protocol radv radv_default") {
- t.Errorf("Expected protocol radv in config, got:\n%s", cfg)
- }
- if !strings.Contains(cfg, "bfd yes;") {
- t.Errorf("Expected bfd yes; in peer and interface, got:\n%s", cfg)
- }
- // Validate generated config directly with BIRD 2 compiler if bird binary is available
- if _, err := exec.LookPath("bird"); err == nil {
- tmpFile, err := os.CreateTemp("", "bird_val_*.conf")
- if err != nil {
- t.Fatalf("Failed to create temp file: %v", err)
- }
- defer os.Remove(tmpFile.Name())
- if _, err := tmpFile.WriteString(cfg); err != nil {
- t.Fatalf("Failed to write temp file: %v", err)
- }
- tmpFile.Close()
- cmd := exec.Command("bird", "-p", "-c", tmpFile.Name())
- out, err := cmd.CombinedOutput()
- if err != nil {
- t.Fatalf("bird -p validation failed: %s\nConfig:\n%s", string(out), cfg)
- }
- }
- }
- func TestBabelFallbackInterfaceAndInstanceManagement(t *testing.T) {
- tmpDir, err := os.MkdirTemp("", "debtik_babel_test_*")
- if err != nil {
- t.Fatalf("Failed to create temp dir: %v", err)
- }
- defer os.RemoveAll(tmpDir)
- mgr := &Manager{
- configPath: tmpDir + "/dynamic.json",
- config: DynamicConfig{
- BabelInstances: []BabelInstance{},
- BabelInterfaces: []BabelInterface{},
- },
- }
- // 1. Save Babel instance without explicit enabled flag
- inst, err := mgr.SaveBabelInstance(BabelInstance{
- Name: "internal-Maiden2",
- RouterID: "192.168.132.1",
- })
- if err != nil {
- t.Fatalf("SaveBabelInstance failed: %v", err)
- }
- if !inst.Enabled {
- t.Errorf("Expected inst.Enabled to be true when Disabled is false, got false")
- }
- // 2. Generate BIRD config when no interfaces exist - should generate fallback interface "*"
- cfg := mgr.generateBirdConfig()
- if !strings.Contains(cfg, `protocol babel babel_internal_Maiden2_0`) {
- t.Errorf("Expected protocol babel in config, got:\n%s", cfg)
- }
- if !strings.Contains(cfg, `interface "*"`) {
- t.Errorf("Expected fallback interface \"*\" in config, got:\n%s", cfg)
- }
- // 3. Save Babel interface without explicit instance - should default to "internal-Maiden2"
- iface, err := mgr.SaveBabelInterface(BabelInterface{
- Interface: "eth0",
- RxCost: 96,
- })
- if err != nil {
- t.Fatalf("SaveBabelInterface failed: %v", err)
- }
- if iface.Instance != "internal-Maiden2" {
- t.Errorf("Expected iface.Instance to default to internal-Maiden2, got %q", iface.Instance)
- }
- // 4. Regenerate config - now should contain eth0 template
- cfg2 := mgr.generateBirdConfig()
- if !strings.Contains(cfg2, `interface "eth0"`) {
- t.Errorf("Expected interface \"eth0\" in config, got:\n%s", cfg2)
- }
- // 5. Validate with bird -p if bird is installed
- if _, err := exec.LookPath("bird"); err == nil {
- tmpFile, err := os.CreateTemp("", "bird_babel_val_*.conf")
- if err != nil {
- t.Fatalf("Failed to create temp file: %v", err)
- }
- defer os.Remove(tmpFile.Name())
- if _, err := tmpFile.WriteString(cfg2); err != nil {
- t.Fatalf("Failed to write temp file: %v", err)
- }
- tmpFile.Close()
- cmd := exec.Command("bird", "-p", "-c", tmpFile.Name())
- out, err := cmd.CombinedOutput()
- if err != nil {
- t.Fatalf("bird -p validation failed on babel config: %s\nConfig:\n%s", string(out), cfg2)
- }
- }
- }
- func TestBFDAndRIPAndRAdvManagement(t *testing.T) {
- tmpDir, err := os.MkdirTemp("", "debtik_protocols_test_*")
- if err != nil {
- t.Fatalf("Failed to create temp dir: %v", err)
- }
- defer os.RemoveAll(tmpDir)
- mgr := &Manager{
- configPath: tmpDir + "/dynamic.json",
- }
- // 1. Save BFD instance
- err = mgr.SaveBFDInstance(BFDInstance{Enabled: true})
- if err != nil {
- t.Fatalf("SaveBFDInstance failed: %v", err)
- }
- bfdInst := mgr.GetBFDInstance()
- if !bfdInst.Enabled {
- t.Errorf("Expected bfdInst.Enabled to be true")
- }
- // 2. Check BFD config generation with 0 interfaces - should contain fallback interface "*"
- cfg := mgr.generateBirdConfig()
- if !strings.Contains(cfg, `protocol bfd bfd_main`) {
- t.Errorf("Expected protocol bfd bfd_main in config, got:\n%s", cfg)
- }
- if !strings.Contains(cfg, `interface "*"`) {
- t.Errorf("Expected fallback interface \"*\" in BFD config, got:\n%s", cfg)
- }
- // 3. Save BFD multihop neighbor with local address
- _, err = mgr.SaveBFDNeighbor(BFDNeighbor{
- Address: "10.0.0.1",
- LocalAddress: "10.0.0.2",
- Multihop: true,
- })
- if err != nil {
- t.Fatalf("SaveBFDNeighbor failed: %v", err)
- }
- // 4. Save RIP instance without explicit enabled flag
- ripInst, err := mgr.SaveRIPInstance(RIPInstance{
- Name: "rip1",
- Version: 2,
- })
- if err != nil {
- t.Fatalf("SaveRIPInstance failed: %v", err)
- }
- if !ripInst.Enabled {
- t.Errorf("Expected ripInst.Enabled to be true, got false")
- }
- // 5. Save RAdv instance without explicit enabled flag
- radvInst, err := mgr.SaveRAdvInstance(RAdvInstance{
- Name: "radv1",
- })
- if err != nil {
- t.Fatalf("SaveRAdvInstance failed: %v", err)
- }
- if !radvInst.Enabled {
- t.Errorf("Expected radvInst.Enabled to be true, got false")
- }
- // 6. Verify config with BIRD compiler
- cfgAll := mgr.generateBirdConfig()
- if !strings.Contains(cfgAll, "neighbor 10.0.0.1 local 10.0.0.2 multihop;") {
- t.Errorf("Expected multihop neighbor with local address, got:\n%s", cfgAll)
- }
- if !strings.Contains(cfgAll, `protocol rip rip_rip1_0`) {
- t.Errorf("Expected protocol rip in config, got:\n%s", cfgAll)
- }
- if !strings.Contains(cfgAll, `protocol radv radv_radv1_0`) {
- t.Errorf("Expected protocol radv in config, got:\n%s", cfgAll)
- }
- if _, err := exec.LookPath("bird"); err == nil {
- tmpFile, err := os.CreateTemp("", "bird_all_val_*.conf")
- if err != nil {
- t.Fatalf("Failed to create temp file: %v", err)
- }
- defer os.Remove(tmpFile.Name())
- if _, err := tmpFile.WriteString(cfgAll); err != nil {
- t.Fatalf("Failed to write temp file: %v", err)
- }
- tmpFile.Close()
- cmd := exec.Command("bird", "-p", "-c", tmpFile.Name())
- out, err := cmd.CombinedOutput()
- if err != nil {
- t.Fatalf("bird -p validation failed on all protocols config: %s\nConfig:\n%s", string(out), cfgAll)
- }
- }
- }
|