From 4424d8b3ed44e661f4bfd252a7f3426741b37fbe Mon Sep 17 00:00:00 2001 From: Gregory Kopels Date: Sun, 22 Sep 2024 07:49:33 +0300 Subject: [PATCH] cnf network: add frrk8 metallb test cases --- .../core/network/metallb/internal/frr/frr.go | 194 +++++ .../metallb/internal/tsparams/consts.go | 5 + .../metallb/internal/tsparams/mlbvars.go | 2 + .../core/network/metallb/tests/bfd-test.go | 15 +- .../core/network/metallb/tests/bgp-tests.go | 5 +- .../cnf/core/network/metallb/tests/common.go | 51 +- .../core/network/metallb/tests/frrk8-tests.go | 733 ++++++++++++++++++ .../core/network/metallb/tests/layer2-test.go | 2 +- 8 files changed, 995 insertions(+), 12 deletions(-) create mode 100644 tests/cnf/core/network/metallb/tests/frrk8-tests.go diff --git a/tests/cnf/core/network/metallb/internal/frr/frr.go b/tests/cnf/core/network/metallb/internal/frr/frr.go index 563e5f2e8..74c028845 100644 --- a/tests/cnf/core/network/metallb/internal/frr/frr.go +++ b/tests/cnf/core/network/metallb/internal/frr/frr.go @@ -45,6 +45,50 @@ type ( Bestpath bool `json:"bestpath,omitempty"` } `json:"routes"` } + + advertisedRoute struct { + AddrPrefix string `json:"addrPrefix"` + PrefixLen int `json:"prefixLen"` + Network string `json:"network"` + NextHop string `json:"nextHop"` + Metric int `json:"metric"` + LocPrf int `json:"locPrf"` + Weight int `json:"weight"` + Path string `json:"path"` + BGPOriginCode string `json:"bgpOriginCode"` + } + // BgpAdvertisedRoutes creates struct from json output. + BgpAdvertisedRoutes struct { + BGPTableVersion int `json:"bgpTableVersion"` + BGPLocalRouterID string `json:"bgpLocalRouterId"` + DefaultLocPrf int `json:"defaultLocPrf"` + LocalAS int `json:"localAS"` + AdvertisedRoutes map[string]advertisedRoute `json:"advertisedRoutes"` + } + // BgpReceivedRoutes struct includes Routes map. + BgpReceivedRoutes struct { + Routes map[string][]RouteInfo `json:"routes"` + } + // RouteInfo struct includes route info. + RouteInfo struct { + Prefix string `json:"prefix"` + PrefixLen int `json:"prefixLen"` + Protocol string `json:"protocol"` + Metric int `json:"metric"` + Uptime string `json:"uptime"` + Nexthops []Nexthop `json:"nexthops"` + } + // Nexthop struct includes nexthop route info. + Nexthop struct { + Flags int `json:"flags"` + Fib bool `json:"fib"` + IP string `json:"ip"` + Afi string `json:"afi"` + InterfaceIndex int `json:"interfaceIndex"` + InterfaceName string `json:"interfaceName"` + Active bool `json:"active"` + Weight int `json:"weight"` + } ) // DefineBaseConfig defines minimal required FRR configuration. @@ -91,6 +135,51 @@ func DefineBGPConfig(localBGPASN, remoteBGPASN int, neighborsIPAddresses []strin return bgpConfig } +// DefineBGPConfigWitgStaticRoutes returns string which represents BGP config file peering to all given IP addresses. +func DefineBGPConfigWitgStaticRoutes(localBGPASN, remoteBGPASN int, neighborsIPAddresses []string, + multiHop, bfd bool) string { + bgpConfig := tsparams.FRRBaseConfig + fmt.Sprintf("ip route %s/32 172.16.0.10\n", neighborsIPAddresses[1]) + + fmt.Sprintf("ip route %s/32 172.16.0.11\n!\n", neighborsIPAddresses[0]) + + fmt.Sprintf("router bgp %d\n", localBGPASN) + + tsparams.FRRDefaultBGPPreConfig + + for _, ipAddress := range neighborsIPAddresses { + bgpConfig += fmt.Sprintf(" neighbor %s remote-as %d\n neighbor %s password %s\n", + ipAddress, remoteBGPASN, ipAddress, tsparams.BGPPassword) + + if bfd { + bgpConfig += fmt.Sprintf(" neighbor %s bfd\n", ipAddress) + } + + if multiHop { + bgpConfig += fmt.Sprintf(" neighbor %s ebgp-multihop 2\n", ipAddress) + } + } + + bgpConfig += "!\naddress-family ipv4 unicast\n" + for _, ipAddress := range neighborsIPAddresses { + bgpConfig += fmt.Sprintf(" neighbor %s activate\n", ipAddress) + } + + bgpConfig += " network 192.168.100.0/24\n" + bgpConfig += " network 192.168.200.0/24\n" + bgpConfig += "exit-address-family\n" + + // Add network commands only once for IPv6 + bgpConfig += "!\naddress-family ipv6 unicast\n" + for _, ipAddress := range neighborsIPAddresses { + bgpConfig += fmt.Sprintf(" neighbor %s activate\n", ipAddress) + } + + bgpConfig += " network 2001:100::0/64\n" + bgpConfig += " network 2001:200::0/64\n" + bgpConfig += "exit-address-family\n" + + bgpConfig += "!\nline vty\n!\nend\n" + + return bgpConfig +} + // BGPNeighborshipHasState verifies that BGP session on a pod has given state. func BGPNeighborshipHasState(frrPod *pod.Builder, neighborIPAddress string, state string) (bool, error) { var result map[string]bgpDescription @@ -230,3 +319,108 @@ func runningConfig(frrPod *pod.Builder) (string, error) { return bgpStateOut.String(), nil } + +// GetBGPAdvertisedRoutes retrieves the routes advertised from the external frr pod to the frr nodes. +func GetBGPAdvertisedRoutes(frrPod *pod.Builder, nodeIPs []string) (string, error) { + var allRoutes strings.Builder + + // Loop through each nodeIP and execute the command + for _, nodeIP := range nodeIPs { + // Execute the BGP command for each nodeIP + routes, err := frrPod.ExecCommand(append(netparam.VtySh, + fmt.Sprintf("sh ip bgp neighbors %s advertised-routes json", nodeIP))) + if err != nil { + return "", fmt.Errorf("error collecting BGP advertised routes from pod %s for nodeIP %s: %w", + frrPod.Definition.Name, nodeIP, err) + } + + // Parse the BGP advertised routes for each nodeIP + bgpAdvertised, err := parseBGPAdvertisedRoutes(routes.String()) + if err != nil { + return "", fmt.Errorf("error parsing BGP advertised routes for nodeIP %s: %w", nodeIP, err) + } + + // Store the result for the current nodeIP + allRoutes.WriteString(fmt.Sprintf("Node IP: %s\n", nodeIP)) + allRoutes.WriteString(bgpAdvertised) + allRoutes.WriteString("\n") + } + + // Return all results combined + return allRoutes.String(), nil +} + +func parseBGPAdvertisedRoutes(jsonData string) (string, error) { + var bgpRoutes BgpAdvertisedRoutes + + // Parse the JSON data into the struct + err := json.Unmarshal([]byte(jsonData), &bgpRoutes) + if err != nil { + return "", fmt.Errorf("error parsing BGP advertised routes: %w", err) + } + + // Format only the network values as a string + var result strings.Builder + for _, route := range bgpRoutes.AdvertisedRoutes { + result.WriteString(fmt.Sprintf("%s\n", route.Network)) + } + + return result.String(), nil +} + +// VerifyBGPReceivedRoutes verifies routes were received via BGP. +func VerifyBGPReceivedRoutes(frrk8sPods []*pod.Builder) (string, error) { + var result strings.Builder + + for _, frrk8sPod := range frrk8sPods { + // Run the "sh ip route bgp json" command on each pod + output, err := frrk8sPod.ExecCommand(append(netparam.VtySh, "sh ip route bgp json"), "frr") + if err != nil { + return "", fmt.Errorf("error collecting BGP received routes from pod %s: %w", + frrk8sPod.Definition.Name, err) + } + + // Parse the JSON output to get the BGP routes + bgpRoutes, err := parseBGPReceivedRoutes(output.String()) + + if err != nil { + return "", fmt.Errorf("error parsing BGP JSON from pod %s: %w", frrk8sPod.Definition.Name, err) + } + + // Write the pod name to the result + result.WriteString(fmt.Sprintf("Pod: %s\n", frrk8sPod.Definition.Name)) + + // Extract and write the prefixes (keys of the Routes map) and corresponding route info + for prefix, routeInfos := range bgpRoutes.Routes { + result.WriteString(fmt.Sprintf(" Prefix: %s\n", prefix)) + + for _, routeInfo := range routeInfos { + result.WriteString(fmt.Sprintf(" Route Info: Prefix: %s", routeInfo.Prefix)) + } + } + + result.WriteString("\n") // Add an empty line between pods + } + + return result.String(), nil +} + +func parseBGPReceivedRoutes(jsonData string) (*BgpReceivedRoutes, error) { + var bgpRoutes map[string][]RouteInfo // This matches the structure of your JSON + + // Parse the JSON data into the struct + err := json.Unmarshal([]byte(jsonData), &bgpRoutes) + if err != nil { + return nil, fmt.Errorf("error parsing BGP received routes: %w", err) + } + + // Create a new BgpReceivedRoutes struct and populate it with the parsed data + parsedRoutes := &BgpReceivedRoutes{ + Routes: bgpRoutes, // The map directly holds prefixes as keys + } + + // Print the parsed routes for debugging + fmt.Printf("Parsed Routes: %+v\n", bgpRoutes) + + return parsedRoutes, nil +} diff --git a/tests/cnf/core/network/metallb/internal/tsparams/consts.go b/tests/cnf/core/network/metallb/internal/tsparams/consts.go index 7292c9ad8..6cb3a3f8f 100644 --- a/tests/cnf/core/network/metallb/internal/tsparams/consts.go +++ b/tests/cnf/core/network/metallb/internal/tsparams/consts.go @@ -9,8 +9,13 @@ const ( LabelBGPTestCases = "bgp" // LabelLayer2TestCases represents layer2 label that can be used for test cases selection. LabelLayer2TestCases = "layer2" + // LabelFRRTestCases represents frrk8 label that can be used for test cases selection. + LabelFRRTestCases = "frrk8" // BGPPassword var is used to set password for BGP session between FRR speakers. BGPPassword = "bgp-test" + // MlbAddressListError an error message when the ECO_CNF_CORE_NET_MLB_ADDR_LIST is incorrect. + MlbAddressListError = "An unexpected error occurred while " + + "determining the IP addresses from the ECO_CNF_CORE_NET_MLB_ADDR_LIST environment variable." // DaemonsFile represents FRR default daemon configuration template. DaemonsFile = ` # This file tells the frr package which daemons to start. diff --git a/tests/cnf/core/network/metallb/internal/tsparams/mlbvars.go b/tests/cnf/core/network/metallb/internal/tsparams/mlbvars.go index 1c48375c6..b6902a942 100644 --- a/tests/cnf/core/network/metallb/internal/tsparams/mlbvars.go +++ b/tests/cnf/core/network/metallb/internal/tsparams/mlbvars.go @@ -44,6 +44,8 @@ var ( FRRK8sDefaultLabel = "component=frr-k8s" // ExternalMacVlanNADName represents default external NetworkAttachmentDefinition name. ExternalMacVlanNADName = "external" + // HubMacVlanNADName represents default external NetworkAttachmentDefinition name. + HubMacVlanNADName = "nad-hub" // SleepCMD represents shel sleep command. SleepCMD = []string{"/bin/bash", "-c", "sleep INF"} // FRRContainerName represents default FRR's container name. diff --git a/tests/cnf/core/network/metallb/tests/bfd-test.go b/tests/cnf/core/network/metallb/tests/bfd-test.go index e68b09a63..b2fc154df 100644 --- a/tests/cnf/core/network/metallb/tests/bfd-test.go +++ b/tests/cnf/core/network/metallb/tests/bfd-test.go @@ -35,8 +35,7 @@ var _ = Describe("BFD", Ordered, Label(tsparams.LabelBFDTestCases), ContinueOnFa var err error By("Getting MetalLb load balancer ip addresses") ipv4metalLbIPList, ipv6metalLbIPList, err = metallbenv.GetMetalLbIPByIPStack() - Expect(err).ToNot(HaveOccurred(), "An unexpected error occurred while "+ - "determining the IP addresses from the ECO_CNF_CORE_NET_MLB_ADDR_LIST environment variable.") + Expect(err).ToNot(HaveOccurred(), tsparams.MlbAddressListError) if len(ipv4metalLbIPList) < 2 { Skip("MetalLb BFD tests require 2 ip addresses. Please check ECO_CNF_CORE_NET_MLB_ADDR_LIST env var") @@ -60,7 +59,7 @@ var _ = Describe("BFD", Ordered, Label(tsparams.LabelBFDTestCases), ContinueOnFa err = metallbenv.IsEnvVarMetalLbIPinNodeExtNetRange(ipv4NodeAddrList, ipv4metalLbIPList, nil) Expect(err).ToNot(HaveOccurred(), "Failed to validate metalLb exported ip address") - createExternalNad() + createExternalNad(tsparams.ExternalMacVlanNADName) }) Context("single hop", Label("singlehop"), func() { @@ -211,7 +210,7 @@ var _ = Describe("BFD", Ordered, Label(tsparams.LabelBFDTestCases), ContinueOnFa DescribeTable("should provide fast link failure detection", reportxml.ID("47186"), func(bgpProtocol, ipStack string, externalTrafficPolicy corev1.ServiceExternalTrafficPolicyType) { - createExternalNad() + createExternalNad(tsparams.ExternalMacVlanNADName) By("Verifying that speaker route map is not empty") Expect(speakerRoutesMap).ToNot(BeNil(), "Speaker route map is empty") @@ -526,8 +525,12 @@ func buildRoutesMap(podList []*pod.Builder, nextHopList []string) (map[string]st routesMap := make(map[string]string) - for num, pod := range podList { - routesMap[pod.Definition.Spec.NodeName] = nextHopList[num] + for _, pod := range podList { + if pod.Definition.Spec.NodeName == "worker-0" { + routesMap[pod.Definition.Spec.NodeName] = nextHopList[1] + } else { + routesMap[pod.Definition.Spec.NodeName] = nextHopList[0] + } } return routesMap, nil diff --git a/tests/cnf/core/network/metallb/tests/bgp-tests.go b/tests/cnf/core/network/metallb/tests/bgp-tests.go index b687a1ab8..ca218f9b2 100644 --- a/tests/cnf/core/network/metallb/tests/bgp-tests.go +++ b/tests/cnf/core/network/metallb/tests/bgp-tests.go @@ -31,8 +31,7 @@ var _ = Describe("BGP", Ordered, Label(tsparams.LabelBGPTestCases), ContinueOnFa var err error By("Getting MetalLb load balancer ip addresses") ipv4metalLbIPList, ipv6metalLbIPList, err = metallbenv.GetMetalLbIPByIPStack() - Expect(err).ToNot(HaveOccurred(), "An unexpected error occurred while "+ - "determining the IP addresses from the ECO_CNF_CORE_NET_MLB_ADDR_LIST environment variable.") + Expect(err).ToNot(HaveOccurred(), tsparams.MlbAddressListError) By("Getting external nodes ip addresses") cnfWorkerNodeList, err = nodes.List(APIClient, @@ -64,7 +63,7 @@ var _ = Describe("BGP", Ordered, Label(tsparams.LabelBGPTestCases), ContinueOnFa BeforeEach(func() { By("Creating External NAD") - createExternalNad() + createExternalNad(tsparams.ExternalMacVlanNADName) By("Listing metalLb speakers pod") var err error diff --git a/tests/cnf/core/network/metallb/tests/common.go b/tests/cnf/core/network/metallb/tests/common.go index b8958f744..d8b6e1419 100644 --- a/tests/cnf/core/network/metallb/tests/common.go +++ b/tests/cnf/core/network/metallb/tests/common.go @@ -90,12 +90,22 @@ func createConfigMap( return masterConfigMap } -func createExternalNad() { +func createHubConfigMap(name string) *configmap.Builder { + frrBFDConfig := frr.DefineBGPConfig( + 64500, tsparams.LocalBGPASN, []string{"10.10.0.10"}, false, false) + configMapData := frr.DefineBaseConfig(tsparams.DaemonsFile, frrBFDConfig, "") + hubConfigMap, err := configmap.NewBuilder(APIClient, name, tsparams.TestNamespaceName).WithData(configMapData).Create() + Expect(err).ToNot(HaveOccurred(), "Failed to create hub config map") + + return hubConfigMap +} + +func createExternalNad(name string) { By("Creating external BR-EX NetworkAttachmentDefinition") macVlanPlugin, err := define.MasterNadPlugin(coreparams.OvnExternalBridge, "bridge", nad.IPAMStatic()) Expect(err).ToNot(HaveOccurred(), "Failed to define master nad plugin") - externalNad, err = nad.NewBuilder(APIClient, tsparams.ExternalMacVlanNADName, tsparams.TestNamespaceName). + externalNad, err = nad.NewBuilder(APIClient, name, tsparams.TestNamespaceName). WithMasterPlugin(macVlanPlugin).Create() Expect(err).ToNot(HaveOccurred(), "Failed to create external NetworkAttachmentDefinition") Expect(externalNad.Exists()).To(BeTrue(), "Failed to detect external NetworkAttachmentDefinition") @@ -206,6 +216,43 @@ func createFrrPod( return frrPod } +func createFrrHubPod( + name string, + nodeName string, + configmapName string, + defaultCMD []string, + secondaryNetConfig []*types.NetworkSelectionElement, + podName ...string) *pod.Builder { + if len(podName) > 0 { + name = podName[0] + } + + frrPod := pod.NewBuilder(APIClient, name, tsparams.TestNamespaceName, NetConfig.FrrImage). + DefineOnNode(nodeName). + WithTolerationToMaster(). + WithSecondaryNetwork(secondaryNetConfig). + RedefineDefaultCMD(defaultCMD) + + By("Creating FRR container") + + if configmapName != "" { + frrContainer := pod.NewContainerBuilder( + tsparams.FRRSecondContainerName, NetConfig.CnfNetTestContainer, tsparams.SleepCMD). + WithSecurityCapabilities([]string{"NET_ADMIN", "NET_RAW", "SYS_ADMIN"}, true) + + frrCtr, err := frrContainer.GetContainerCfg() + Expect(err).ToNot(HaveOccurred(), "Failed to get container configuration") + frrPod.WithAdditionalContainer(frrCtr).WithLocalVolume(configmapName, "/etc/frr") + } + + By("Creating FRR pod in the test namespace") + + frrPod, err := frrPod.WithPrivilegedFlag().CreateAndWaitUntilRunning(5 * time.Minute) + Expect(err).ToNot(HaveOccurred(), "Failed to create FRR test pod") + + return frrPod +} + func setupMetalLbService( ipStack string, ipAddressPool *metallb.IPAddressPoolBuilder, diff --git a/tests/cnf/core/network/metallb/tests/frrk8-tests.go b/tests/cnf/core/network/metallb/tests/frrk8-tests.go new file mode 100644 index 000000000..e07c42f5b --- /dev/null +++ b/tests/cnf/core/network/metallb/tests/frrk8-tests.go @@ -0,0 +1,733 @@ +package tests + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/golang/glog" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/openshift-kni/eco-goinfra/pkg/configmap" + "github.com/openshift-kni/eco-goinfra/pkg/daemonset" + "github.com/openshift-kni/eco-goinfra/pkg/metallb" + "github.com/openshift-kni/eco-goinfra/pkg/nad" + "github.com/openshift-kni/eco-goinfra/pkg/namespace" + "github.com/openshift-kni/eco-goinfra/pkg/nodes" + "github.com/openshift-kni/eco-goinfra/pkg/pod" + "github.com/openshift-kni/eco-goinfra/pkg/reportxml" + "github.com/openshift-kni/eco-goinfra/pkg/service" + . "github.com/openshift-kni/eco-gotests/tests/cnf/core/network/internal/netinittools" + "github.com/openshift-kni/eco-gotests/tests/cnf/core/network/metallb/internal/frr" + "github.com/openshift-kni/eco-gotests/tests/cnf/core/network/metallb/internal/metallbenv" + "github.com/openshift-kni/eco-gotests/tests/cnf/core/network/metallb/internal/tsparams" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/wait" +) + +var ( + externalAdvertisedIPv4Routes = []string{"192.168.100.0/24", "192.168.200.0/24"} + externalAdvertisedIPv6Routes = []string{"2001:100::0/64", "2001:200::0/64"} +) +var _ = Describe("FRR", Ordered, Label(tsparams.LabelFRRTestCases), ContinueOnFailure, func() { + BeforeAll(func() { + var ( + err error + ) + + By("Getting MetalLb load balancer ip addresses") + ipv4metalLbIPList, ipv6metalLbIPList, err = metallbenv.GetMetalLbIPByIPStack() + Expect(err).ToNot(HaveOccurred(), tsparams.MlbAddressListError) + + By("Getting external nodes ip addresses") + cnfWorkerNodeList, err = nodes.List(APIClient, + metav1.ListOptions{LabelSelector: labels.Set(NetConfig.WorkerLabelMap).String()}) + Expect(err).ToNot(HaveOccurred(), "Failed to discover worker nodes") + + By("Selecting worker node for BGP tests") + workerLabelMap, workerNodeList = setWorkerNodeListAndLabelForBfdTests(cnfWorkerNodeList, metalLbTestsLabel) + ipv4NodeAddrList, err = nodes.ListExternalIPv4Networks( + APIClient, metav1.ListOptions{LabelSelector: labels.Set(workerLabelMap).String()}) + Expect(err).ToNot(HaveOccurred(), "Failed to collect external nodes ip addresses") + + err = metallbenv.IsEnvVarMetalLbIPinNodeExtNetRange(ipv4NodeAddrList, ipv4metalLbIPList, nil) + Expect(err).ToNot(HaveOccurred(), "Failed to validate metalLb exported ip address") + + By("Listing master nodes") + masterNodeList, err = nodes.List(APIClient, + metav1.ListOptions{LabelSelector: labels.Set(NetConfig.ControlPlaneLabelMap).String()}) + Expect(err).ToNot(HaveOccurred(), "Fail to list master nodes") + Expect(len(masterNodeList)).To(BeNumerically(">", 0), + "Failed to detect master nodes") + }) + + Context("IBGP Single hop", func() { + + var ( + nodeAddrList []string + addressPool []string + err error + frrk8sPods []*pod.Builder + ) + + BeforeAll(func() { + By("Setting test iteration parameters") + _, _, _, nodeAddrList, addressPool, _, err = + metallbenv.DefineIterationParams( + ipv4metalLbIPList, ipv6metalLbIPList, ipv4NodeAddrList, ipv6NodeAddrList, "IPv4") + Expect(err).ToNot(HaveOccurred(), "Fail to set iteration parameters") + + }) + + AfterEach(func() { + resetOperatorAndTestNS() + }) + + It("Verify that prefixes configured with alwaysBlock are not received by the FRR speakers", + reportxml.ID("74270"), func() { + By("Creating a new instance of MetalLB Speakers on workers blocking specific incoming prefixes") + err := createNewMetalLbDaemonSetAndWaitUntilItsRunningWithAlwaysBlock(tsparams.DefaultTimeout, + workerLabelMap, []string{externalAdvertisedIPv4Routes[0]}) + Expect(err).ToNot(HaveOccurred(), "Failed to recreate metalLb daemonset") + + By("Creating external FRR pod on master node") + frrPod := deployTestPods(addressPool) + + By("Creating BGP Peers") + createBGPPeerAndVerifyIfItsReady(ipv4metalLbIPList[0], "", 64500, + false, frrk8sPods) + + By("Checking that BGP session is established and up") + verifyMetalLbBGPSessionsAreUPOnFrrPod(frrPod, removePrefixFromIPList(ipv4NodeAddrList)) + time.Sleep(30 * time.Second) + By("Validating BGP route prefix") + validatePrefix(frrPod, "IPv4", removePrefixFromIPList(nodeAddrList), addressPool, 32) + + By("Create a frrconfiguration allow all") + err = createFrrConfiguration("frrconfig-allow-all", ipv4metalLbIPList[0], + 64500, nil, false) + Expect(err).ToNot(HaveOccurred(), "Fail to create a FRRconfiguration with allow all") + + frrk8sPods, err := pod.List(APIClient, NetConfig.MlbOperatorNamespace, metav1.ListOptions{ + LabelSelector: "app=frr-k8s", + }) + Expect(err).ToNot(HaveOccurred(), "Fail to find Frrk8 pod list") + + By("Verify that the node FRR pods advertises two routes") + err = verifyExternalAdvertisedRoutes(frrPod, ipv4NodeAddrList, externalAdvertisedIPv4Routes) + Expect(err).ToNot(HaveOccurred(), "Fail to find advertised routes") + + By("Validate BGP received routes") + Eventually(func() string { + // Get the routes + routes, err := frr.VerifyBGPReceivedRoutes(frrk8sPods) + Expect(err).ToNot(HaveOccurred(), "Failed to verify BGP routes") + + // Return the routes to be checked + return routes + }, 60*time.Second, 5*time.Second).Should(ContainSubstring(externalAdvertisedIPv4Routes[1]), + "Fail to find received routes") + }) + + It("Verify the FRR node only receives routes that are configured in the allowed prefixes", + reportxml.ID("74272"), func() { + + By("Creating a new instance of MetalLB Speakers on workers") + err = metallbenv.CreateNewMetalLbDaemonSetAndWaitUntilItsRunning(tsparams.DefaultTimeout, workerLabelMap) + Expect(err).ToNot(HaveOccurred(), "Failed to recreate metalLb daemonset") + + frrPod := deployTestPods(addressPool) + + By("Creating BGP Peers") + createBGPPeerAndVerifyIfItsReady(ipv4metalLbIPList[0], "", 64500, false, frrk8sPods) + + By("Checking that BGP session is established and up") + verifyMetalLbBGPSessionsAreUPOnFrrPod(frrPod, removePrefixFromIPList(ipv4NodeAddrList)) + + By("Validating BGP route prefix") + validatePrefix(frrPod, "IPv4", removePrefixFromIPList(nodeAddrList), addressPool, 32) + + By("Create a frrconfiguration with prefix filter") + err := createFrrConfiguration("frrconfig-filtered", ipv4metalLbIPList[0], + 64500, []string{externalAdvertisedIPv4Routes[0], externalAdvertisedIPv6Routes[0]}, false) + Expect(err).ToNot(HaveOccurred(), "Fail to create a FRRconfiguration") + + frrk8sPods, err := pod.List(APIClient, NetConfig.MlbOperatorNamespace, metav1.ListOptions{ + LabelSelector: "app=frr-k8s", + }) + Expect(err).ToNot(HaveOccurred(), "Fail to find Frrk8 pod list") + + By("Verify that the node FRR pods advertises two routes") + err = verifyExternalAdvertisedRoutes(frrPod, ipv4NodeAddrList, externalAdvertisedIPv4Routes) + Expect(err).ToNot(HaveOccurred(), "Fail to find advertised routes") + + By("Validate BGP received routes") + Eventually(func() string { + // Get the routes + routes, err := frr.VerifyBGPReceivedRoutes(frrk8sPods) + Expect(err).ToNot(HaveOccurred(), "Failed to verify BGP routes") + + // Return the routes to be checked + return routes + }, 60*time.Second, 5*time.Second).Should(SatisfyAll( + ContainSubstring(externalAdvertisedIPv4Routes[0]), // First IP address + Not(ContainSubstring(externalAdvertisedIPv4Routes[1])), // Second IP address + ), "Fail to find all expected received routes") + }) + + It("Verify that when the allow all mode is configured all routes are received on the FRR speaker", + reportxml.ID("74273"), func() { + + By("Creating a new instance of MetalLB Speakers on workers") + err = metallbenv.CreateNewMetalLbDaemonSetAndWaitUntilItsRunning(tsparams.DefaultTimeout, workerLabelMap) + Expect(err).ToNot(HaveOccurred(), "Failed to recreate metalLb daemonset") + + frrPod := deployTestPods(addressPool) + + By("Creating BGP Peers") + createBGPPeerAndVerifyIfItsReady(ipv4metalLbIPList[0], "", 64500, false, frrk8sPods) + + By("Checking that BGP session is established and up") + verifyMetalLbBGPSessionsAreUPOnFrrPod(frrPod, removePrefixFromIPList(ipv4NodeAddrList)) + + By("Validating BGP route prefix") + validatePrefix(frrPod, "IPv4", removePrefixFromIPList(nodeAddrList), addressPool, 32) + + By("Create a frrconfiguration allow all") + err = createFrrConfiguration("frrconfig-allow-all", ipv4metalLbIPList[0], 64500, nil, false) + Expect(err).ToNot(HaveOccurred(), "Fail to create a FRRconfiguration with allow all") + + frrk8sPods, err := pod.List(APIClient, NetConfig.MlbOperatorNamespace, metav1.ListOptions{ + LabelSelector: "app=frr-k8s", + }) + Expect(err).ToNot(HaveOccurred(), "Fail to find Frrk8 pod list") + + By("Verify that the node FRR pods advertises two routes") + err = verifyExternalAdvertisedRoutes(frrPod, ipv4NodeAddrList, externalAdvertisedIPv4Routes) + Expect(err).ToNot(HaveOccurred(), "Fail to find advertised routes") + + By("Validate BGP received routes") + Eventually(func() string { + // Get the routes + routes, err := frr.VerifyBGPReceivedRoutes(frrk8sPods) + Expect(err).ToNot(HaveOccurred(), "Failed to verify BGP routes") + + // Return the routes to be checked + return routes + }, 60*time.Second, 5*time.Second).Should(SatisfyAll( + ContainSubstring(externalAdvertisedIPv4Routes[0]), // First IP address + ContainSubstring(externalAdvertisedIPv4Routes[1]), // Second IP address + ), "Fail to find all expected received routes") + }) + + It("Verify that a FRR speaker can be updated by merging two different FRRConfigurations", + reportxml.ID("74274"), func() { + + By("Creating a new instance of MetalLB Speakers on workers") + err = metallbenv.CreateNewMetalLbDaemonSetAndWaitUntilItsRunning(tsparams.DefaultTimeout, workerLabelMap) + Expect(err).ToNot(HaveOccurred(), "Failed to recreate metalLb daemonset") + + frrPod := deployTestPods(addressPool) + + By("Creating BGP Peers") + createBGPPeerAndVerifyIfItsReady(ipv4metalLbIPList[0], "", 64500, false, frrk8sPods) + + By("Checking that BGP session is established and up") + verifyMetalLbBGPSessionsAreUPOnFrrPod(frrPod, removePrefixFromIPList(ipv4NodeAddrList)) + + By("Validating BGP route prefix") + validatePrefix(frrPod, "IPv4", removePrefixFromIPList(nodeAddrList), addressPool, 32) + + By("Create first frrconfiguration that receieves a single route") + err := createFrrConfiguration("frrconfig-filtered-1", ipv4metalLbIPList[0], 64500, + []string{externalAdvertisedIPv4Routes[0], externalAdvertisedIPv6Routes[0]}, false) + Expect(err).ToNot(HaveOccurred(), "Fail to create a first FRRconfiguration") + + frrk8sPods, err := pod.List(APIClient, NetConfig.MlbOperatorNamespace, metav1.ListOptions{ + LabelSelector: "app=frr-k8s", + }) + Expect(err).ToNot(HaveOccurred(), "Fail to find Frrk8 pod list") + + By("Verify that the node FRR pods advertises two routes") + err = verifyExternalAdvertisedRoutes(frrPod, ipv4NodeAddrList, externalAdvertisedIPv4Routes) + Expect(err).ToNot(HaveOccurred(), "Fail to find advertised routes") + + By("Validate BGP received only the first route") + Eventually(func() string { + // Get the routes + routes, err := frr.VerifyBGPReceivedRoutes(frrk8sPods) + Expect(err).ToNot(HaveOccurred(), "Failed to verify BGP routes") + + // Return the routes to be checked + return routes + }, 60*time.Second, 5*time.Second).Should(SatisfyAll( + ContainSubstring(externalAdvertisedIPv4Routes[0]), // First IP address + Not(ContainSubstring(externalAdvertisedIPv4Routes[1])), // Second IP address + ), "Fail to find all expected received routes") + + By("Create second frrconfiguration that receives a single route") + err = createFrrConfiguration("frrconfig-filtered-2", ipv4metalLbIPList[0], 64500, + []string{externalAdvertisedIPv4Routes[1], externalAdvertisedIPv6Routes[1]}, false) + Expect(err).ToNot(HaveOccurred(), "Fail to create a second FRRconfiguration") + + By("Validate BGP received only the first route") + Eventually(func() string { + // Get the routes + routes, err := frr.VerifyBGPReceivedRoutes(frrk8sPods) + Expect(err).ToNot(HaveOccurred(), "Failed to verify BGP routes") + + // Return the routes to be checked + return routes + }, 60*time.Second, 5*time.Second).Should(SatisfyAll( + ContainSubstring(externalAdvertisedIPv4Routes[0]), // First IP address + ContainSubstring(externalAdvertisedIPv4Routes[1]), // Second IP address + ), "Fail to find all expected received routes") + }) + + It("Verify that a FRR speaker rejects a contrasting FRRConfiguration merge", + reportxml.ID("74275"), func() { + + By("Creating a new instance of MetalLB Speakers on workers") + err = metallbenv.CreateNewMetalLbDaemonSetAndWaitUntilItsRunning(tsparams.DefaultTimeout, workerLabelMap) + Expect(err).ToNot(HaveOccurred(), "Failed to recreate metalLb daemonset") + + frrPod := deployTestPods(addressPool) + + By("Creating BGP Peers") + createBGPPeerAndVerifyIfItsReady(ipv4metalLbIPList[0], "", 64500, + false, frrk8sPods) + + By("Checking that BGP session is established and up") + verifyMetalLbBGPSessionsAreUPOnFrrPod(frrPod, removePrefixFromIPList(ipv4NodeAddrList)) + + By("Validating BGP route prefix") + validatePrefix(frrPod, "IPv4", removePrefixFromIPList(nodeAddrList), addressPool, 32) + + By("Create first frrconfiguration that receieves a single route") + err := createFrrConfiguration("frrconfig-filtered-1", ipv4metalLbIPList[0], 64500, + []string{externalAdvertisedIPv4Routes[0], externalAdvertisedIPv6Routes[0]}, false) + Expect(err).ToNot(HaveOccurred(), "Fail to create a first FRRconfiguration") + + frrk8sPods, err := pod.List(APIClient, NetConfig.MlbOperatorNamespace, metav1.ListOptions{ + LabelSelector: "app=frr-k8s", + }) + Expect(err).ToNot(HaveOccurred(), "Fail to find Frrk8 pod list") + + By("Verify that the node FRR pods advertises two routes") + err = verifyExternalAdvertisedRoutes(frrPod, ipv4NodeAddrList, externalAdvertisedIPv4Routes) + Expect(err).ToNot(HaveOccurred(), "Fail to find advertised routes") + + By("Validate BGP received routes") + Eventually(func() string { + // Get the routes + routes, err := frr.VerifyBGPReceivedRoutes(frrk8sPods) + Expect(err).ToNot(HaveOccurred(), "Failed to verify BGP routes") + + // Return the routes to be checked + return routes + }, 60*time.Second, 5*time.Second).Should(SatisfyAll( + ContainSubstring(externalAdvertisedIPv4Routes[0]), // First IP address + Not(ContainSubstring(externalAdvertisedIPv4Routes[1])), // Second IP address + ), "Fail to find all expected received routes") + + By("Create second frrconfiguration with an incorrect AS configuration") + err = createFrrConfiguration("frrconfig-filtered-2", ipv4metalLbIPList[0], 64501, + []string{externalAdvertisedIPv4Routes[1], externalAdvertisedIPv6Routes[1]}, false) + Expect(err).To(HaveOccurred(), "successful created the FRRconfiguration and expected to fail") + }) + }) + + Context("BGP Multihop", func() { + BeforeAll(func() { + By("Creating a new instance of MetalLB Speakers on workers") + err := metallbenv.CreateNewMetalLbDaemonSetAndWaitUntilItsRunning(tsparams.DefaultTimeout, workerLabelMap) + Expect(err).ToNot(HaveOccurred(), "Failed to recreate metalLb daemonset") + + frrk8sPods, err := pod.List(APIClient, NetConfig.MlbOperatorNamespace, metav1.ListOptions{ + LabelSelector: "app=frr-k8s", + }) + Expect(err).ToNot(HaveOccurred(), "Fail to list speaker pods") + + By("Collecting information before test") + + Expect(err).ToNot(HaveOccurred(), "Failed to list speaker pods") + By("Setting test iteration parameters") + masterClientPodIP, _, _, _, addressPool, _, err := + metallbenv.DefineIterationParams( + ipv4metalLbIPList, ipv6metalLbIPList, ipv4NodeAddrList, ipv6NodeAddrList, "IPv4") + Expect(err).ToNot(HaveOccurred(), "Fail to set iteration parameters") + + By("Adding static routes to the speakers") + speakerRoutesMap, err := buildRoutesMap(frrk8sPods, ipv4metalLbIPList) + Expect(err).ToNot(HaveOccurred(), "Failed to build speaker route map") + + for _, frrk8sPod := range frrk8sPods { + out, err := frr.SetStaticRoute(frrk8sPod, "add", masterClientPodIP, speakerRoutesMap) + Expect(err).ToNot(HaveOccurred(), out) + } + + By("Creating an IPAddressPool and BGPAdvertisement") + + ipAddressPool := setupBgpAdvertisement(addressPool, int32(32)) + + By("Creating a MetalLB service") + setupMetalLbService("IPv4", ipAddressPool, "Cluster") + + By("Creating nginx test pod on worker node") + setupNGNXPod(workerNodeList[0].Definition.Name) + + By("Creating External NAD for master FRR pod") + createExternalNad(tsparams.ExternalMacVlanNADName) + + By("Creating External NAD for hub FRR pods") + createExternalNad(tsparams.HubMacVlanNADName) + + By("Creating static ip annotation for hub0") + hub0BRstaticIPAnnotation := pod.StaticIPAnnotation("external", []string{"10.46.81.131/24", "2001:81:81::1/64"}) + hub0BRstaticIPAnnotation = append(hub0BRstaticIPAnnotation, + pod.StaticIPAnnotation(tsparams.HubMacVlanNADName, []string{"172.16.0.10/24", "2001:100:100::10/64"})...) + + By("Creating static ip annotation for hub1") + hub1BRstaticIPAnnotation := pod.StaticIPAnnotation("external", []string{"10.46.81.132/24", "2001:81:81::2/64"}) + hub1BRstaticIPAnnotation = append(hub1BRstaticIPAnnotation, + pod.StaticIPAnnotation(tsparams.HubMacVlanNADName, []string{"172.16.0.11/24", "2001:100:100::11/64"})...) + + By("Creating MetalLb Hub pod configMap") + hubConfigMap := createHubConfigMap("frr-hub-node-config") + + By("Creating FRR Hub Worker-0 Pod") + _ = createFrrHubPod("hub-pod-worker-0", + workerNodeList[0].Object.Name, hubConfigMap.Definition.Name, []string{}, hub0BRstaticIPAnnotation) + + By("Creating FRR Hub Worker-1 Pod") + _ = createFrrHubPod("hub-pod-worker-1", + workerNodeList[1].Object.Name, hubConfigMap.Definition.Name, []string{}, hub1BRstaticIPAnnotation) + }) + + AfterAll(func() { + By("Removing static routes from the speakers") + frrk8sPods, err := pod.List(APIClient, NetConfig.MlbOperatorNamespace, metav1.ListOptions{ + LabelSelector: tsparams.FRRK8sDefaultLabel, + }) + Expect(err).ToNot(HaveOccurred(), "Failed to list pods") + + speakerRoutesMap, err := buildRoutesMap(frrk8sPods, ipv4metalLbIPList) + Expect(err).ToNot(HaveOccurred(), "Failed to build speaker route map") + + for _, frrk8sPod := range frrk8sPods { + out, err := frr.SetStaticRoute(frrk8sPod, "del", "172.16.0.1", speakerRoutesMap) + fmt.Println("OUT", out) + Expect(err).ToNot(HaveOccurred(), out) + } + + resetOperatorAndTestNS() + }) + + It("Validate a FRR node receives and sends IPv4 and IPv6 routes from an IBGP multihop FRR instance", + reportxml.ID("74278"), func() { + + By("Collecting information before test") + frrk8sPods, err := pod.List(APIClient, NetConfig.MlbOperatorNamespace, metav1.ListOptions{ + LabelSelector: "app=frr-k8s", + }) + Expect(err).ToNot(HaveOccurred(), "Failed to list speaker pods") + By("Setting test iteration parameters") + _, _, _, nodeAddrList, addressPool, _, err := + metallbenv.DefineIterationParams( + ipv4metalLbIPList, ipv6metalLbIPList, ipv4NodeAddrList, ipv6NodeAddrList, "IPv4") + Expect(err).ToNot(HaveOccurred(), "Fail to set iteration parameters") + + By("Creating MetalLb Master pod configMap") + frrPod := createMasterFrrPod(64500, nodeAddrList, false) + + By("Creating BGP Peers") + createBGPPeerAndVerifyIfItsReady("172.16.0.1", "", tsparams.LocalBGPASN, + false, frrk8sPods) + + By("Checking that BGP session is established and up") + verifyMetalLbBGPSessionsAreUPOnFrrPod(frrPod, removePrefixFromIPList(ipv4NodeAddrList)) + + By("Validating BGP route prefix") + validatePrefix(frrPod, "IPv4", removePrefixFromIPList(nodeAddrList), addressPool, 32) + + By("Create a frrconfiguration allow all for EBGP multihop") + err = createFrrConfiguration("frrconfig-allow-all", "172.16.0.1", 64500, nil, false) + Expect(err).ToNot(HaveOccurred(), "Fail to create an allow-all FRRconfiguration") + + By("Verify that the node FRR pods advertises two routes") + err = verifyExternalAdvertisedRoutes(frrPod, ipv4NodeAddrList, externalAdvertisedIPv4Routes) + Expect(err).ToNot(HaveOccurred(), "Fail to find advertised routes") + + By("Validate BGP received routes") + Eventually(func() string { + // Get the routes + routes, err := frr.VerifyBGPReceivedRoutes(frrk8sPods) + Expect(err).ToNot(HaveOccurred(), "Failed to verify BGP routes") + + // Return the routes to be checked + return routes + }, 60*time.Second, 5*time.Second).Should(SatisfyAll( + ContainSubstring(externalAdvertisedIPv4Routes[0]), // First IP address + ContainSubstring(externalAdvertisedIPv4Routes[1]), // Second IP address + ), "Fail to find all expected received routes") + }) + + It("Validate a FRR node receives and sends IPv4 and IPv6 routes from an EBGP multihop FRR instance", + reportxml.ID("47279"), func() { + + By("Collecting information before test") + frrk8sPods, err := pod.List(APIClient, NetConfig.MlbOperatorNamespace, metav1.ListOptions{ + LabelSelector: "app=frr-k8s", + }) + Expect(err).ToNot(HaveOccurred(), "Failed to list speaker pods") + By("Setting test iteration parameters") + _, _, _, nodeAddrList, addressPool, _, err := + metallbenv.DefineIterationParams( + ipv4metalLbIPList, ipv6metalLbIPList, ipv4NodeAddrList, ipv6NodeAddrList, "IPv4") + Expect(err).ToNot(HaveOccurred(), "Fail to set iteration parameters") + + By("Creating MetalLb Master pod configMap") + frrPod := createMasterFrrPod(64501, nodeAddrList, true) + + By("Creating BGP Peers") + createBGPPeerAndVerifyIfItsReady("172.16.0.1", "", tsparams.RemoteBGPASN, + true, frrk8sPods) + + By("Checking that BGP session is established and up") + verifyMetalLbBGPSessionsAreUPOnFrrPod(frrPod, removePrefixFromIPList(ipv4NodeAddrList)) + + By("Validating BGP route prefix") + validatePrefix(frrPod, "IPv4", removePrefixFromIPList(nodeAddrList), addressPool, 32) + + By("Create a frrconfiguration allow all for EBGP multihop") + err = createFrrConfiguration("frrconfig-allow-all", "172.16.0.1", 64501, nil, true) + Expect(err).ToNot(HaveOccurred(), "Fail to create an allow-all FRRconfiguration") + + By("Verify that the node FRR pods advertises two routes") + err = verifyExternalAdvertisedRoutes(frrPod, ipv4NodeAddrList, externalAdvertisedIPv4Routes) + Expect(err).ToNot(HaveOccurred(), "Fail to find advertised routes") + + By("Validate BGP received routes") + Eventually(func() string { + // Get the routes + routes, err := frr.VerifyBGPReceivedRoutes(frrk8sPods) + Expect(err).ToNot(HaveOccurred(), "Failed to verify BGP routes") + + // Return the routes to be checked + return routes + }, 60*time.Second, 5*time.Second).Should(SatisfyAll( + ContainSubstring(externalAdvertisedIPv4Routes[0]), // First IP address + ContainSubstring(externalAdvertisedIPv4Routes[1]), // Second IP address + ), "Fail to find all expected received routes") + }) + }) + +}) + +func createNewMetalLbDaemonSetAndWaitUntilItsRunningWithAlwaysBlock(timeout time.Duration, + nodeLabel map[string]string, prefixes []string) error { + glog.V(90).Infof("Verifying if metalLb daemonset is running") + + metalLbIo, err := metallb.Pull(APIClient, tsparams.MetalLbIo, NetConfig.MlbOperatorNamespace) + + if err == nil { + glog.V(90).Infof("MetalLb daemonset is running. Removing daemonset.") + + _, err = metalLbIo.Delete() + + if err != nil { + return err + } + } + + glog.V(90).Infof("Create new metalLb speaker's daemonSet.") + + metalLbIo = metallb.NewBuilder( + APIClient, tsparams.MetalLbIo, NetConfig.MlbOperatorNamespace, nodeLabel) + + metalLbIo.WithFRRConfigAlwaysBlock(prefixes) + _, err = metalLbIo.Create() + + if err != nil { + return err + } + + var metalLbDs *daemonset.Builder + + err = wait.PollUntilContextTimeout( + context.TODO(), 3*time.Second, timeout, true, func(ctx context.Context) (bool, error) { + metalLbDs, err = daemonset.Pull(APIClient, tsparams.MetalLbDsName, NetConfig.MlbOperatorNamespace) + if err != nil { + glog.V(90).Infof("Error to pull daemonset %s namespace %s, retry", + tsparams.MetalLbDsName, NetConfig.MlbOperatorNamespace) + + return false, nil + } + + return true, nil + }) + + if err != nil { + return err + } + + glog.V(90).Infof("Waiting until the new metalLb daemonset is in Ready state.") + + if metalLbDs.IsReady(timeout) { + return nil + } + + return fmt.Errorf("metallb daemonSet is not ready") +} + +func deployTestPods(addressPool []string) *pod.Builder { + var err error + + By("Creating an IPAddressPool and BGPAdvertisement") + + ipAddressPool := setupBgpAdvertisement(addressPool, int32(32)) + + By("Creating a MetalLB service") + setupMetalLbService("IPv4", ipAddressPool, "Cluster") + + By("Creating nginx test pod on worker node") + setupNGNXPod(workerNodeList[0].Definition.Name) + + By("Creating External NAD") + createExternalNad(tsparams.ExternalMacVlanNADName) + + By("Creating static ip annotation") + + staticIPAnnotation := pod.StaticIPAnnotation( + externalNad.Definition.Name, []string{fmt.Sprintf("%s/%s", ipv4metalLbIPList[0], "24")}) + + By("Creating MetalLb configMap") + + masterConfigMap := createConfigMapWithStaticRoutes(tsparams.LocalBGPASN, ipv4NodeAddrList, false, false) + + By("Creating FRR Pod") + + frrPod := createFrrPod( + masterNodeList[0].Object.Name, masterConfigMap.Definition.Name, []string{}, staticIPAnnotation) + + Eventually(func() error { + return err + }, time.Minute, tsparams.DefaultRetryInterval).ShouldNot(HaveOccurred(), + "Failed to collect metrics from speaker pods") + + return frrPod +} + +func createFrrConfiguration(name, bgpPeerIP string, remoteAS uint32, + filteredIP []string, ebgp bool) error { + frrConfig := metallb.NewFrrConfigurationBuilder(APIClient, name, + NetConfig.MlbOperatorNamespace).WithBGPRouter(tsparams.LocalBGPASN). + WithBGPNeighbor(bgpPeerIP, remoteAS, 0) + if len(filteredIP) > 0 { + frrConfig.WithToReceiveModeFiltered(filteredIP, 0, 0) + } else { + frrConfig.WithToReceiveModeAll(0, 0) + } + + if ebgp { + frrConfig.WithEBGPMultiHop(0, 0) + } + + frrConfig. + WithHoldTime(metav1.Duration{Duration: 90 * time.Second}, 0, 0). + WithKeepalive(metav1.Duration{Duration: 30 * time.Second}, 0, 0). + WithBGPPassword("bgp-test", 0, 0). + WithPort(179, 0, 0) + + _, err := frrConfig.Create() + + if err != nil { + glog.V(90).Infof("failed to create a frrconfiugration") + + return err + } + + return nil +} + +func createMasterFrrPod(localAS int, ipv4NodeAddrList []string, ebgpMultiHop bool) *pod.Builder { + masterConfigMap := createConfigMapWithStaticRoutes(localAS, ipv4NodeAddrList, ebgpMultiHop, false) + + By("Creating static ip annotation for master FRR pod") + + masterStaticIPAnnotation := pod.StaticIPAnnotation( + tsparams.HubMacVlanNADName, []string{"172.16.0.1/24", "2001:100:100::254/64"}) + + By("Creating FRR Master Pod") + + frrPod := createFrrPod( + masterNodeList[0].Object.Name, masterConfigMap.Definition.Name, []string{}, masterStaticIPAnnotation) + + return frrPod +} + +func createConfigMapWithStaticRoutes( + bgpAsn int, nodeAddrList []string, enableMultiHop, enableBFD bool) *configmap.Builder { + frrBFDConfig := frr.DefineBGPConfigWitgStaticRoutes( + bgpAsn, tsparams.LocalBGPASN, removePrefixFromIPList(nodeAddrList), enableMultiHop, enableBFD) + configMapData := frr.DefineBaseConfig(tsparams.DaemonsFile, frrBFDConfig, "") + masterConfigMap, err := configmap.NewBuilder(APIClient, "frr-master-node-config", tsparams.TestNamespaceName). + WithData(configMapData).Create() + Expect(err).ToNot(HaveOccurred(), "Failed to create config map") + + return masterConfigMap +} + +func verifyExternalAdvertisedRoutes(frrPod *pod.Builder, ipv4NodeAddrList, externalExpectedRoutes []string) error { + advertisedRoutes, err := frr.GetBGPAdvertisedRoutes(frrPod, removePrefixFromIPList(ipv4NodeAddrList)) + if err != nil { + return fmt.Errorf("failed to find advertised routes: %w", err) + } + + actualRoutes := strings.Split(strings.TrimSpace(advertisedRoutes), "\n") + expectedRoutes := externalExpectedRoutes + + // Check that the actual routes contain all the expected routes + for _, expectedRoute := range expectedRoutes { + matched, err := ContainElement(expectedRoute).Match(actualRoutes) + if err != nil { + return fmt.Errorf("failed to match route %s: %w", expectedRoute, err) + } + + if !matched { + return fmt.Errorf("expected route %s not found", expectedRoute) + } + } + + // Return nil if all expected routes are found + return nil +} + +func resetOperatorAndTestNS() { + By("Cleaning MetalLb operator namespace") + + metalLbNs, err := namespace.Pull(APIClient, NetConfig.MlbOperatorNamespace) + Expect(err).ToNot(HaveOccurred(), "Failed to pull metalLb operator namespace") + err = metalLbNs.CleanObjects( + tsparams.DefaultTimeout, + metallb.GetBGPPeerGVR(), + metallb.GetBFDProfileGVR(), + metallb.GetBGPPeerGVR(), + metallb.GetBGPAdvertisementGVR(), + metallb.GetIPAddressPoolGVR(), + metallb.GetMetalLbIoGVR(), + metallb.GetFrrConfigurationGVR()) + Expect(err).ToNot(HaveOccurred(), "Failed to remove object's from operator namespace") + + By("Cleaning test namespace") + + err = namespace.NewBuilder(APIClient, tsparams.TestNamespaceName).CleanObjects( + tsparams.DefaultTimeout, + pod.GetGVR(), + service.GetServiceGVR(), + configmap.GetGVR(), + nad.GetGVR()) + Expect(err).ToNot(HaveOccurred(), "Failed to clean test namespace") +} diff --git a/tests/cnf/core/network/metallb/tests/layer2-test.go b/tests/cnf/core/network/metallb/tests/layer2-test.go index 539d56572..bade2d400 100644 --- a/tests/cnf/core/network/metallb/tests/layer2-test.go +++ b/tests/cnf/core/network/metallb/tests/layer2-test.go @@ -79,7 +79,7 @@ var _ = Describe("Layer2", Ordered, Label(tsparams.LabelLayer2TestCases), Contin setupMetalLbService(netparam.IPV4Family, ipAddressPool, "Cluster") By("Creating external Network Attachment Definition") - createExternalNad() + createExternalNad(tsparams.ExternalMacVlanNADName) By("Creating client test pod") clientTestPod, err = pod.NewBuilder(