From 7d7735dde23ebbffa2ba8534f9c29be6fff34201 Mon Sep 17 00:00:00 2001 From: Enrique Llorente Date: Wed, 24 Jul 2024 16:49:01 +0200 Subject: [PATCH] multicast, udn: Add unit test Signed-off-by: Enrique Llorente Co-authored-by: Dumitru Ceara Signed-off-by: Dumitru Ceara --- go-controller/pkg/ovn/master_test.go | 14 +- go-controller/pkg/ovn/multicast_test.go | 303 +++++++++------- go-controller/pkg/ovn/namespace_test.go | 6 +- .../network_segmentation_multicast_test.go | 331 ++++++++++++++++++ 4 files changed, 519 insertions(+), 135 deletions(-) create mode 100644 go-controller/pkg/ovn/network_segmentation_multicast_test.go diff --git a/go-controller/pkg/ovn/master_test.go b/go-controller/pkg/ovn/master_test.go index 1b0d9a18836..91d2825adad 100644 --- a/go-controller/pkg/ovn/master_test.go +++ b/go-controller/pkg/ovn/master_test.go @@ -1842,16 +1842,20 @@ func newNetworkClusterPortGroup(netControllerName string) *nbdb.PortGroup { return pg } +func newNetworkRouterPortGroup(netControllerName string) *nbdb.PortGroup { + fakeController := getFakeController(netControllerName) + pgIDs := fakeController.getClusterPortGroupDbIDs(types.ClusterRtrPortGroupNameBase) + pg := libovsdbutil.BuildPortGroup(pgIDs, nil, nil) + pg.UUID = pgIDs.String() + return pg +} + func newClusterPortGroup() *nbdb.PortGroup { return newNetworkClusterPortGroup(DefaultNetworkControllerName) } func newRouterPortGroup() *nbdb.PortGroup { - fakeController := getFakeController(DefaultNetworkControllerName) - pgIDs := fakeController.getClusterPortGroupDbIDs(types.ClusterRtrPortGroupNameBase) - pg := libovsdbutil.BuildPortGroup(pgIDs, nil, nil) - pg.UUID = pgIDs.String() - return pg + return newNetworkRouterPortGroup(DefaultNetworkControllerName) } func newOVNClusterRouter() *nbdb.LogicalRouter { diff --git a/go-controller/pkg/ovn/multicast_test.go b/go-controller/pkg/ovn/multicast_test.go index 435bed7c4ff..a5d5a828acb 100644 --- a/go-controller/pkg/ovn/multicast_test.go +++ b/go-controller/pkg/ovn/multicast_test.go @@ -16,6 +16,7 @@ import ( "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/util" + nadapi "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -53,9 +54,9 @@ func setIpMode(m ipMode) { config.IPv6Mode = m.IPv6Mode } -func getMulticastDefaultExpectedData(clusterPortGroup, clusterRtrPortGroup *nbdb.PortGroup) []libovsdb.TestData { +func getMulticastExpectedDataForNetwork(netControllerName string, clusterPortGroup, clusterRtrPortGroup *nbdb.PortGroup) []libovsdb.TestData { match := getMulticastACLMatch() - aclIDs := getDefaultMcastACLDbIDs(mcastDefaultDenyID, libovsdbutil.ACLEgress, DefaultNetworkControllerName) + aclIDs := getDefaultMcastACLDbIDs(mcastDefaultDenyID, libovsdbutil.ACLEgress, netControllerName) aclName := libovsdbutil.GetACLName(aclIDs) defaultDenyEgressACL := libovsdbops.BuildACL( aclName, @@ -74,7 +75,7 @@ func getMulticastDefaultExpectedData(clusterPortGroup, clusterRtrPortGroup *nbdb ) defaultDenyEgressACL.UUID = "defaultDenyEgressACL_UUID" - aclIDs = getDefaultMcastACLDbIDs(mcastDefaultDenyID, libovsdbutil.ACLIngress, DefaultNetworkControllerName) + aclIDs = getDefaultMcastACLDbIDs(mcastDefaultDenyID, libovsdbutil.ACLIngress, netControllerName) aclName = libovsdbutil.GetACLName(aclIDs) defaultDenyIngressACL := libovsdbops.BuildACL( aclName, @@ -92,7 +93,7 @@ func getMulticastDefaultExpectedData(clusterPortGroup, clusterRtrPortGroup *nbdb defaultDenyIngressACL.UUID = "defaultDenyIngressACL_UUID" clusterPortGroup.ACLs = []string{defaultDenyEgressACL.UUID, defaultDenyIngressACL.UUID} - aclIDs = getDefaultMcastACLDbIDs(mcastAllowInterNodeID, libovsdbutil.ACLEgress, DefaultNetworkControllerName) + aclIDs = getDefaultMcastACLDbIDs(mcastAllowInterNodeID, libovsdbutil.ACLEgress, netControllerName) aclName = libovsdbutil.GetACLName(aclIDs) egressMatch := libovsdbutil.GetACLMatch(clusterRtrPortGroup.Name, match, libovsdbutil.ACLEgress) defaultAllowEgressACL := libovsdbops.BuildACL( @@ -112,7 +113,7 @@ func getMulticastDefaultExpectedData(clusterPortGroup, clusterRtrPortGroup *nbdb ) defaultAllowEgressACL.UUID = "defaultAllowEgressACL_UUID" - aclIDs = getDefaultMcastACLDbIDs(mcastAllowInterNodeID, libovsdbutil.ACLIngress, DefaultNetworkControllerName) + aclIDs = getDefaultMcastACLDbIDs(mcastAllowInterNodeID, libovsdbutil.ACLIngress, netControllerName) aclName = libovsdbutil.GetACLName(aclIDs) ingressMatch := libovsdbutil.GetACLMatch(clusterRtrPortGroup.Name, match, libovsdbutil.ACLIngress) defaultAllowIngressACL := libovsdbops.BuildACL( @@ -140,8 +141,8 @@ func getMulticastDefaultExpectedData(clusterPortGroup, clusterRtrPortGroup *nbdb } } -func getMulticastDefaultStaleData(clusterPortGroup, clusterRtrPortGroup *nbdb.PortGroup) []libovsdb.TestData { - testData := getMulticastDefaultExpectedData(clusterPortGroup, clusterRtrPortGroup) +func getMulticastStaleDataForNetwork(netControllerName string, clusterPortGroup, clusterRtrPortGroup *nbdb.PortGroup) []libovsdb.TestData { + testData := getMulticastExpectedDataForNetwork(netControllerName, clusterPortGroup, clusterRtrPortGroup) defaultDenyIngressACL := testData[0].(*nbdb.ACL) newName := libovsdbutil.JoinACLName(types.ClusterPortGroupNameBase, "DefaultDenyMulticastIngress") defaultDenyIngressACL.Name = &newName @@ -172,22 +173,19 @@ func getMulticastDefaultStaleData(clusterPortGroup, clusterRtrPortGroup *nbdb.Po } } -func getDefaultPortGroups() (clusterPortGroup, clusterRtrPortGroup *nbdb.PortGroup) { - clusterPortGroup = newClusterPortGroup() - clusterRtrPortGroup = newRouterPortGroup() - return -} - func getMulticastPolicyExpectedData(ns string, ports []string) []libovsdb.TestData { - fakeController := getFakeController(DefaultNetworkControllerName) + return getMulticastPolicyExpectedDataForNetwork(DefaultNetworkControllerName, ns, ports) +} +func getMulticastPolicyExpectedDataForNetwork(netControllerName string, ns string, ports []string) []libovsdb.TestData { + fakeController := getFakeController(netControllerName) pg_hash := fakeController.getNamespacePortGroupName(ns) egressMatch := libovsdbutil.GetACLMatch(pg_hash, fakeController.getMulticastACLEgrMatch(), libovsdbutil.ACLEgress) - ip4AddressSet, ip6AddressSet := getNsAddrSetHashNames(ns) + ip4AddressSet, ip6AddressSet := getNsAddrSetHashNamesForNetwork(netControllerName, ns) mcastMatch := getACLMatchAF(getMulticastACLIgrMatchV4(ip4AddressSet), getMulticastACLIgrMatchV6(ip6AddressSet), config.IPv4Mode, config.IPv6Mode) ingressMatch := libovsdbutil.GetACLMatch(pg_hash, mcastMatch, libovsdbutil.ACLIngress) - aclIDs := getNamespaceMcastACLDbIDs(ns, libovsdbutil.ACLEgress, DefaultNetworkControllerName) + aclIDs := getNamespaceMcastACLDbIDs(ns, libovsdbutil.ACLEgress, netControllerName) aclName := libovsdbutil.GetACLName(aclIDs) egressACL := libovsdbops.BuildACL( aclName, @@ -206,7 +204,7 @@ func getMulticastPolicyExpectedData(ns string, ports []string) []libovsdb.TestDa ) egressACL.UUID = ns + "mc-egress-UUID" - aclIDs = getNamespaceMcastACLDbIDs(ns, libovsdbutil.ACLIngress, DefaultNetworkControllerName) + aclIDs = getNamespaceMcastACLDbIDs(ns, libovsdbutil.ACLIngress, netControllerName) aclName = libovsdbutil.GetACLName(aclIDs) ingressACL := libovsdbops.BuildACL( aclName, @@ -250,8 +248,8 @@ func getNamespacePG(ns, controllerName string) *nbdb.PortGroup { return pg } -func getMulticastPolicyStaleData(ns string, ports []string) []libovsdb.TestData { - testData := getMulticastPolicyExpectedData(ns, ports) +func getMulticastPolicyStaleDataForNetwork(netControllerName string, ns string, ports []string) []libovsdb.TestData { + testData := getMulticastPolicyExpectedDataForNetwork(netControllerName, ns, ports) egressACL := testData[0].(*nbdb.ACL) newName := libovsdbutil.JoinACLName(ns, "MulticastAllowEgress") @@ -324,6 +322,157 @@ func updateMulticast(fakeOvn *FakeOVN, ns *v1.Namespace, enable bool) { gomega.Expect(err).NotTo(gomega.HaveOccurred()) } +func startBaseNetworkController(fakeOvn *FakeOVN, netInfo util.NetInfo, nad *nadapi.NetworkAttachmentDefinition) *BaseNetworkController { + if nad != nil { + gomega.Expect(fakeOvn.NewSecondaryNetworkController(nad)).To(gomega.Succeed()) + controller, ok := fakeOvn.secondaryControllers[netInfo.GetNetworkName()] + gomega.Expect(ok).To(gomega.BeTrue()) + return &controller.bnc.BaseNetworkController + } else { + return &fakeOvn.controller.BaseNetworkController + } +} + +func testDefaultMulticastACLCreation(fakeOvn *FakeOVN, netControllerName string, netInfo util.NetInfo, nad *nadapi.NetworkAttachmentDefinition) { + clusterPortGroup := newNetworkClusterPortGroup(netControllerName) + clusterRtrPortGroup := newNetworkRouterPortGroup(netControllerName) + fakeOvn.startWithDBSetup(libovsdb.TestSetup{ + NBData: []libovsdb.TestData{ + clusterPortGroup, + clusterRtrPortGroup, + }, + }) + bnc := startBaseNetworkController(fakeOvn, netInfo, nad) + + gomega.Expect(bnc.createDefaultDenyMulticastPolicy()).To(gomega.Succeed()) + gomega.Expect(bnc.createDefaultAllowMulticastPolicy()).To(gomega.Succeed()) + + gomega.Eventually(fakeOvn.nbClient).Should(libovsdb.HaveData( + getMulticastExpectedDataForNetwork(netControllerName, clusterPortGroup, clusterRtrPortGroup))) +} + +func testUpdateStaleDefaultMulticastACLs(fakeOvn *FakeOVN, netControllerName string, netInfo util.NetInfo, nad *nadapi.NetworkAttachmentDefinition) { + // start with stale ACLs + clusterPortGroup := newNetworkClusterPortGroup(netControllerName) + clusterRtrPortGroup := newNetworkRouterPortGroup(netControllerName) + fakeOvn.startWithDBSetup(libovsdb.TestSetup{ + NBData: getMulticastStaleDataForNetwork(netControllerName, clusterPortGroup, clusterRtrPortGroup), + }) + bnc := startBaseNetworkController(fakeOvn, netInfo, nad) + + gomega.Expect(bnc.createDefaultDenyMulticastPolicy()).To(gomega.Succeed()) + gomega.Expect(bnc.createDefaultAllowMulticastPolicy()).To(gomega.Succeed()) + + // check acls are updated + gomega.Eventually(fakeOvn.nbClient).Should(libovsdb.HaveData( + getMulticastExpectedDataForNetwork(netControllerName, clusterPortGroup, clusterRtrPortGroup))) +} + +func testCleanupMulticastResourcesOnDisable(fakeOvn *FakeOVN, netControllerName string, netInfo util.NetInfo, nad *nadapi.NetworkAttachmentDefinition) { + nsName := "namespace1" + clusterPortGroup := newNetworkClusterPortGroup(netControllerName) + clusterRtrPortGroup := newNetworkRouterPortGroup(netControllerName) + initialData := getMulticastExpectedDataForNetwork(netControllerName, clusterPortGroup, clusterRtrPortGroup) + + nsData := getMulticastPolicyExpectedDataForNetwork(netControllerName, nsName, nil) + initialData = append(initialData, nsData...) + // namespace is still present, but multicast support is disabled + namespace1 := *newNamespace(nsName) + fakeOvn.startWithDBSetup(libovsdb.TestSetup{NBData: initialData}, + &v1.NamespaceList{ + Items: []v1.Namespace{ + namespace1, + }, + }, + ) + bnc := startBaseNetworkController(fakeOvn, netInfo, nad) + + // this "if !oc.multicastSupport" part of SetupMaster + gomega.Expect(bnc.disableMulticast()).To(gomega.Succeed()) + // check acls are deleted when multicast is disabled + clusterPortGroup = newNetworkClusterPortGroup(netControllerName) + clusterRtrPortGroup = newNetworkRouterPortGroup(netControllerName) + namespacePortGroup := getNamespacePG(nsName, netControllerName) + expectedData := []libovsdb.TestData{ + clusterPortGroup, + clusterRtrPortGroup, + namespacePortGroup, + } + gomega.Eventually(fakeOvn.nbClient).Should(libovsdb.HaveData(expectedData)) +} + +func testCreateNamespaceMulticastACLs(fakeOvn *FakeOVN, netControllerName string, netInfo util.NetInfo, nad *nadapi.NetworkAttachmentDefinition) { + nsName := "namespace1" + clusterPortGroup := newNetworkClusterPortGroup(netControllerName) + clusterRtrPortGroup := newNetworkRouterPortGroup(netControllerName) + expectedData := getMulticastExpectedDataForNetwork(netControllerName, clusterPortGroup, clusterRtrPortGroup) + // namespace exists, but multicast acls do not + namespace1 := *newNamespace(nsName) + namespace1.Annotations[util.NsMulticastAnnotation] = "true" + fakeOvn.startWithDBSetup(libovsdb.TestSetup{NBData: expectedData}, + &v1.NamespaceList{ + Items: []v1.Namespace{ + namespace1, + }, + }, + ) + bnc := startBaseNetworkController(fakeOvn, netInfo, nad) + + gomega.Expect(bnc.WatchNamespaces()).To(gomega.Succeed()) + expectedData = append(expectedData, getMulticastPolicyExpectedDataForNetwork(netControllerName, nsName, nil)...) + gomega.Eventually(fakeOvn.nbClient).Should(libovsdb.HaveData(expectedData)) +} + +func testUpdateStaleNamespaceMulticastACLs(fakeOvn *FakeOVN, netControllerName string, netInfo util.NetInfo, nad *nadapi.NetworkAttachmentDefinition) { + // start with stale ACLs for existing namespace + nsName := "namespace1" + clusterPortGroup := newNetworkClusterPortGroup(netControllerName) + clusterRtrPortGroup := newNetworkRouterPortGroup(netControllerName) + expectedData := getMulticastExpectedDataForNetwork(netControllerName, clusterPortGroup, clusterRtrPortGroup) + expectedData = append(expectedData, getMulticastPolicyStaleDataForNetwork(netControllerName, nsName, nil)...) + namespace1 := *newNamespace(nsName) + namespace1.Annotations[util.NsMulticastAnnotation] = "true" + fakeOvn.startWithDBSetup(libovsdb.TestSetup{NBData: expectedData}, + &v1.NamespaceList{ + Items: []v1.Namespace{ + namespace1, + }, + }, + ) + bnc := startBaseNetworkController(fakeOvn, netInfo, nad) + + gomega.Expect(bnc.WatchNamespaces()).To(gomega.Succeed()) + expectedData = getMulticastExpectedDataForNetwork(netControllerName, clusterPortGroup, clusterRtrPortGroup) + expectedData = append(expectedData, getMulticastPolicyExpectedDataForNetwork(netControllerName, nsName, nil)...) + gomega.Eventually(fakeOvn.nbClient).Should(libovsdb.HaveData(expectedData)) +} + +func testCleanupNamespaceMulticastACLs(fakeOvn *FakeOVN, netControllerName string, netInfo util.NetInfo, nad *nadapi.NetworkAttachmentDefinition) { + nsName := "namespace1" + + // start with stale ACLs + clusterPortGroup := newNetworkClusterPortGroup(netControllerName) + clusterRtrPortGroup := newNetworkRouterPortGroup(netControllerName) + defaultMulticastData := getMulticastExpectedDataForNetwork(netControllerName, clusterPortGroup, clusterRtrPortGroup) + namespaceMulticastData := getMulticastPolicyExpectedDataForNetwork(netControllerName, nsName, nil) + namespace1 := *newNamespace(nsName) + + fakeOvn.startWithDBSetup(libovsdb.TestSetup{NBData: append(defaultMulticastData, namespaceMulticastData...)}, + &v1.NamespaceList{ + Items: []v1.Namespace{ + namespace1, + }, + }, + ) + bnc := startBaseNetworkController(fakeOvn, netInfo, nad) + + gomega.Expect(bnc.WatchNamespaces()).To(gomega.Succeed()) + // only namespaced acls should be dereferenced, default acls will stay + namespacePortGroup := getNamespacePG(nsName, netControllerName) + expectedData := append(defaultMulticastData, namespacePortGroup) + gomega.Eventually(fakeOvn.nbClient).Should(libovsdb.HaveData(expectedData)) +} + var _ = ginkgo.Describe("OVN Multicast with IP Address Family", func() { const ( namespaceName1 = "namespace1" @@ -362,21 +511,7 @@ var _ = ginkgo.Describe("OVN Multicast with IP Address Family", func() { ginkgo.Context("on startup", func() { ginkgo.It("creates default Multicast ACLs", func() { app.Action = func(ctx *cli.Context) error { - clusterPortGroup, clusterRtrPortGroup := getDefaultPortGroups() - fakeOvn.startWithDBSetup(libovsdb.TestSetup{ - NBData: []libovsdb.TestData{ - clusterPortGroup, - clusterRtrPortGroup, - }, - }) - // this is "if oc.multicastSupport" part of SetupMaster - err := fakeOvn.controller.createDefaultDenyMulticastPolicy() - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - err = fakeOvn.controller.createDefaultAllowMulticastPolicy() - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - - gomega.Eventually(fakeOvn.nbClient).Should(libovsdb.HaveData( - getMulticastDefaultExpectedData(clusterPortGroup, clusterRtrPortGroup))) + testDefaultMulticastACLCreation(fakeOvn, DefaultNetworkControllerName, &util.DefaultNetInfo{}, nil) return nil } err := app.Run([]string{app.Name}) @@ -384,19 +519,7 @@ var _ = ginkgo.Describe("OVN Multicast with IP Address Family", func() { }) ginkgo.It("updates stale default Multicast ACLs", func() { app.Action = func(ctx *cli.Context) error { - // start with stale ACLs - clusterPortGroup, clusterRtrPortGroup := getDefaultPortGroups() - fakeOvn.startWithDBSetup(libovsdb.TestSetup{ - NBData: getMulticastDefaultStaleData(clusterPortGroup, clusterRtrPortGroup), - }) - // this is "if oc.multicastSupport" part of SetupMaster - err := fakeOvn.controller.createDefaultDenyMulticastPolicy() - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - err = fakeOvn.controller.createDefaultAllowMulticastPolicy() - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - // check acls are updated - gomega.Eventually(fakeOvn.nbClient).Should(libovsdb.HaveData( - getMulticastDefaultExpectedData(clusterPortGroup, clusterRtrPortGroup))) + testUpdateStaleDefaultMulticastACLs(fakeOvn, DefaultNetworkControllerName, &util.DefaultNetInfo{}, nil) return nil } err := app.Run([]string{app.Name}) @@ -404,32 +527,7 @@ var _ = ginkgo.Describe("OVN Multicast with IP Address Family", func() { }) ginkgo.It("cleans up Multicast resources when multicast is disabled", func() { app.Action = func(ctx *cli.Context) error { - clusterPortGroup, clusterRtrPortGroup := getDefaultPortGroups() - initialData := getMulticastDefaultExpectedData(clusterPortGroup, clusterRtrPortGroup) - - nsData := getMulticastPolicyExpectedData(namespaceName1, nil) - initialData = append(initialData, nsData...) - // namespace is still present, but multicast support is disabled - namespace1 := *newNamespace(namespaceName1) - fakeOvn.startWithDBSetup(libovsdb.TestSetup{NBData: initialData}, - &v1.NamespaceList{ - Items: []v1.Namespace{ - namespace1, - }, - }, - ) - // this "if !oc.multicastSupport" part of SetupMaster - err := fakeOvn.controller.disableMulticast() - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - // check acls are deleted when multicast is disabled - clusterPortGroup, clusterRtrPortGroup = getDefaultPortGroups() - namespacePortGroup := getNamespacePG(namespaceName1, fakeOvn.controller.controllerName) - expectedData := []libovsdb.TestData{ - clusterPortGroup, - clusterRtrPortGroup, - namespacePortGroup, - } - gomega.Eventually(fakeOvn.nbClient).Should(libovsdb.HaveData(expectedData)) + testCleanupMulticastResourcesOnDisable(fakeOvn, DefaultNetworkControllerName, &util.DefaultNetInfo{}, nil) return nil } err := app.Run([]string{app.Name}) @@ -437,22 +535,7 @@ var _ = ginkgo.Describe("OVN Multicast with IP Address Family", func() { }) ginkgo.It("creates namespace Multicast ACLs", func() { app.Action = func(ctx *cli.Context) error { - clusterPortGroup, clusterRtrPortGroup := getDefaultPortGroups() - expectedData := getMulticastDefaultExpectedData(clusterPortGroup, clusterRtrPortGroup) - // namespace exists, but multicast acls do not - namespace1 := *newNamespace(namespaceName1) - namespace1.Annotations[util.NsMulticastAnnotation] = "true" - fakeOvn.startWithDBSetup(libovsdb.TestSetup{NBData: expectedData}, - &v1.NamespaceList{ - Items: []v1.Namespace{ - namespace1, - }, - }, - ) - err := fakeOvn.controller.WatchNamespaces() - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - expectedData = append(expectedData, getMulticastPolicyExpectedData(namespaceName1, nil)...) - gomega.Eventually(fakeOvn.nbClient).Should(libovsdb.HaveData(expectedData)) + testCreateNamespaceMulticastACLs(fakeOvn, DefaultNetworkControllerName, &util.DefaultNetInfo{}, nil) return nil } err := app.Run([]string{app.Name}) @@ -460,26 +543,7 @@ var _ = ginkgo.Describe("OVN Multicast with IP Address Family", func() { }) ginkgo.It("updates stale namespace Multicast ACLs", func() { app.Action = func(ctx *cli.Context) error { - // start with stale ACLs for existing namespace - clusterPortGroup, clusterRtrPortGroup := getDefaultPortGroups() - expectedData := getMulticastDefaultExpectedData(clusterPortGroup, clusterRtrPortGroup) - expectedData = append(expectedData, getMulticastPolicyStaleData(namespaceName1, nil)...) - namespace1 := *newNamespace(namespaceName1) - namespace1.Annotations[util.NsMulticastAnnotation] = "true" - fakeOvn.startWithDBSetup(libovsdb.TestSetup{NBData: expectedData}, - &v1.NamespaceList{ - Items: []v1.Namespace{ - namespace1, - }, - }, - ) - - err := fakeOvn.controller.WatchNamespaces() - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - - expectedData = getMulticastDefaultExpectedData(clusterPortGroup, clusterRtrPortGroup) - expectedData = append(expectedData, getMulticastPolicyExpectedData(namespaceName1, nil)...) - gomega.Eventually(fakeOvn.nbClient).Should(libovsdb.HaveData(expectedData)) + testUpdateStaleNamespaceMulticastACLs(fakeOvn, DefaultNetworkControllerName, &util.DefaultNetInfo{}, nil) return nil } @@ -488,26 +552,7 @@ var _ = ginkgo.Describe("OVN Multicast with IP Address Family", func() { }) ginkgo.It("cleans up namespace Multicast ACLs when multicast is disabled for namespace", func() { app.Action = func(ctx *cli.Context) error { - // start with stale ACLs - clusterPortGroup, clusterRtrPortGroup := getDefaultPortGroups() - defaultMulticastData := getMulticastDefaultExpectedData(clusterPortGroup, clusterRtrPortGroup) - namespaceMulticastData := getMulticastPolicyExpectedData(namespaceName1, nil) - namespace1 := *newNamespace(namespaceName1) - - fakeOvn.startWithDBSetup(libovsdb.TestSetup{NBData: append(defaultMulticastData, namespaceMulticastData...)}, - &v1.NamespaceList{ - Items: []v1.Namespace{ - namespace1, - }, - }, - ) - - err := fakeOvn.controller.WatchNamespaces() - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - // only namespaced acls should be dereferenced, default acls will stay - namespacePortGroup := getNamespacePG(namespaceName1, fakeOvn.controller.controllerName) - expectedData := append(defaultMulticastData, namespacePortGroup) - gomega.Eventually(fakeOvn.nbClient).Should(libovsdb.HaveData(expectedData)) + testCleanupNamespaceMulticastACLs(fakeOvn, DefaultNetworkControllerName, &util.DefaultNetInfo{}, nil) return nil } err := app.Run([]string{app.Name}) diff --git a/go-controller/pkg/ovn/namespace_test.go b/go-controller/pkg/ovn/namespace_test.go index 3e2a9f3ae5a..015a621d368 100644 --- a/go-controller/pkg/ovn/namespace_test.go +++ b/go-controller/pkg/ovn/namespace_test.go @@ -68,7 +68,11 @@ func newNamespace(namespace string) *v1.Namespace { } func getNsAddrSetHashNames(ns string) (string, string) { - return addressset.GetHashNamesForAS(getNamespaceAddrSetDbIDs(ns, DefaultNetworkControllerName)) + return getNsAddrSetHashNamesForNetwork(DefaultNetworkControllerName, ns) +} + +func getNsAddrSetHashNamesForNetwork(netControllerName, ns string) (string, string) { + return addressset.GetHashNamesForAS(getNamespaceAddrSetDbIDs(ns, netControllerName)) } func buildNamespaceAddressSets(namespace string, ips []string) (*nbdb.AddressSet, *nbdb.AddressSet) { diff --git a/go-controller/pkg/ovn/network_segmentation_multicast_test.go b/go-controller/pkg/ovn/network_segmentation_multicast_test.go new file mode 100644 index 00000000000..5dd0cb544d6 --- /dev/null +++ b/go-controller/pkg/ovn/network_segmentation_multicast_test.go @@ -0,0 +1,331 @@ +package ovn + +import ( + "github.com/urfave/cli/v2" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/onsi/gomega/format" + + "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/config" + ovntest "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing" + "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types" + "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/util" +) + +var _ = Describe("[Network Segmentation] OVN Multicast with IP Address Family", func() { + const ( + namespaceName1 = "namespace1" + nodeName = "node1" + ) + var ( + app *cli.App + fakeOvn *FakeOVN + gomegaFormatMaxLength int + nad = ovntest.GenerateNAD("bluenet", "rednad", namespaceName1, + types.Layer3Topology, "100.128.0.0/16", types.NetworkRolePrimary) + netInfo util.NetInfo + ) + + BeforeEach(func() { + // Restore global default values before each testcase + config.PrepareTestConfig() + config.IPv4Mode = true + config.IPv6Mode = false + config.EnableMulticast = true + config.OVNKubernetesFeature.EnableNetworkSegmentation = true + config.OVNKubernetesFeature.EnableMultiNetwork = true + config.OVNKubernetesFeature.EnableMultiNetworkPolicy = true + + app = cli.NewApp() + app.Name = "test" + // flags are written to config.EnableMulticast + // if there is no --enable-multicast flag, it will set to false. + // alternative approach is to give this flag to app.Run, but that require more changes. + //app.Flags = config.Flags + + fakeOvn = NewFakeOVN(true) + gomegaFormatMaxLength = format.MaxLength + format.MaxLength = 0 + + var err error + netInfo, err = util.ParseNADInfo(nad) + Expect(err).ToNot(HaveOccurred()) + }) + + AfterEach(func() { + fakeOvn.shutdown() + format.MaxLength = gomegaFormatMaxLength + }) + + Context("on startup", func() { + It("creates default Multicast ACLs", func() { + app.Action = func(ctx *cli.Context) error { + testDefaultMulticastACLCreation(fakeOvn, getNetworkControllerName(netInfo.GetNetworkName()), netInfo, nad) + return nil + } + err := app.Run([]string{app.Name}) + Expect(err).NotTo(HaveOccurred()) + }) + It("updates stale default Multicast ACLs", func() { + app.Action = func(ctx *cli.Context) error { + testUpdateStaleDefaultMulticastACLs(fakeOvn, getNetworkControllerName(netInfo.GetNetworkName()), netInfo, nad) + return nil + } + err := app.Run([]string{app.Name}) + Expect(err).NotTo(HaveOccurred()) + }) + It("cleans up Multicast resources when multicast is disabled", func() { + app.Action = func(ctx *cli.Context) error { + testCleanupMulticastResourcesOnDisable(fakeOvn, getNetworkControllerName(netInfo.GetNetworkName()), netInfo, nad) + return nil + } + err := app.Run([]string{app.Name}) + Expect(err).NotTo(HaveOccurred()) + }) + It("creates namespace Multicast ACLs", func() { + app.Action = func(ctx *cli.Context) error { + testCreateNamespaceMulticastACLs(fakeOvn, getNetworkControllerName(netInfo.GetNetworkName()), netInfo, nad) + return nil + } + err := app.Run([]string{app.Name}) + Expect(err).NotTo(HaveOccurred()) + }) + It("updates stale namespace Multicast ACLs", func() { + app.Action = func(ctx *cli.Context) error { + testUpdateStaleNamespaceMulticastACLs(fakeOvn, getNetworkControllerName(netInfo.GetNetworkName()), netInfo, nad) + return nil + } + + err := app.Run([]string{app.Name}) + Expect(err).NotTo(HaveOccurred()) + }) + It("cleans up namespace Multicast ACLs when multicast is disabled for namespace", func() { + app.Action = func(ctx *cli.Context) error { + testCleanupNamespaceMulticastACLs(fakeOvn, getNetworkControllerName(netInfo.GetNetworkName()), netInfo, nad) + return nil + } + err := app.Run([]string{app.Name}) + Expect(err).NotTo(HaveOccurred()) + }) + }) + + /* + Context("during execution", func() { + for _, m := range getIpModes() { + m := m + It("tests enabling/disabling multicast in a namespace "+ipModeStr(m), func() { + app.Action = func(ctx *cli.Context) error { + namespace1 := *newNamespace(namespaceName1) + + fakeOvn.startWithDBSetup(libovsdb.TestSetup{}, + &v1.NamespaceList{ + Items: []v1.Namespace{ + namespace1, + }, + }, + ) + setIpMode(m) + + err := fakeOvn.controller.WatchNamespaces() + Expect(err).NotTo(HaveOccurred()) + ns, err := fakeOvn.fakeClient.KubeClient.CoreV1().Namespaces().Get(context.TODO(), namespace1.Name, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + Expect(ns).NotTo(BeNil()) + + // Multicast is denied by default. + _, ok := ns.Annotations[util.NsMulticastAnnotation] + Expect(ok).To(BeFalse()) + + // Enable multicast in the namespace. + updateMulticast(fakeOvn, ns, true) + expectedData := getMulticastPolicyExpectedData(namespace1.Name, nil) + Eventually(fakeOvn.nbClient).Should(libovsdb.HaveData(expectedData...)) + + // Disable multicast in the namespace. + updateMulticast(fakeOvn, ns, false) + + namespacePortGroup := getNamespacePG(namespaceName1, fakeOvn.controller.controllerName) + Eventually(fakeOvn.nbClient).Should(libovsdb.HaveData(namespacePortGroup)) + return nil + } + + err := app.Run([]string{app.Name}) + Expect(err).NotTo(HaveOccurred()) + }) + + It("tests enabling multicast in a namespace with a pod "+ipModeStr(m), func() { + app.Action = func(ctx *cli.Context) error { + namespace1 := *newNamespace(namespaceName1) + pods, tPods, tPodIPs := createTestPods(nodeName, namespaceName1, m) + + fakeOvn.startWithDBSetup(libovsdb.TestSetup{NBData: getNodeSwitch(nodeName)}, + &v1.NamespaceList{ + Items: []v1.Namespace{ + namespace1, + }, + }, + &v1.NodeList{ + Items: []v1.Node{ + *newNode("node1", "192.168.126.202/24"), + }, + }, + &v1.PodList{ + Items: pods, + }, + ) + setIpMode(m) + + for _, tPod := range tPods { + tPod.populateLogicalSwitchCache(fakeOvn) + } + + err := fakeOvn.controller.WatchNamespaces() + Expect(err).NotTo(HaveOccurred()) + err = fakeOvn.controller.WatchPods() + Expect(err).NotTo(HaveOccurred()) + ns, err := fakeOvn.fakeClient.KubeClient.CoreV1().Namespaces().Get(context.TODO(), namespace1.Name, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + Expect(ns).NotTo(BeNil()) + + // Enable multicast in the namespace + updateMulticast(fakeOvn, ns, true) + // calculate expected data + ports := []string{} + for _, tPod := range tPods { + ports = append(ports, tPod.portUUID) + } + expectedData := getMulticastPolicyExpectedData(namespace1.Name, ports) + expectedData = append(expectedData, getExpectedDataPodsAndSwitches(tPods, []string{nodeName})...) + Eventually(fakeOvn.nbClient).Should(libovsdb.HaveData(expectedData...)) + fakeOvn.asf.ExpectAddressSetWithAddresses(namespace1.Name, tPodIPs) + return nil + } + + err := app.Run([]string{app.Name}) + Expect(err).NotTo(HaveOccurred()) + }) + + It("tests enabling multicast in multiple namespaces with a long name > 42 characters "+ipModeStr(m), func() { + app.Action = func(ctx *cli.Context) error { + longNameSpace1Name := "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk" // create with 63 characters + namespace1 := *newNamespace(longNameSpace1Name) + longNameSpace2Name := "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijl" // create with 63 characters + namespace2 := *newNamespace(longNameSpace2Name) + + fakeOvn.startWithDBSetup(libovsdb.TestSetup{NBData: getNodeSwitch(nodeName)}, + &v1.NamespaceList{ + Items: []v1.Namespace{ + namespace1, + namespace2, + }, + }, + ) + setIpMode(m) + + err := fakeOvn.controller.WatchNamespaces() + Expect(err).NotTo(HaveOccurred()) + fakeOvn.controller.WatchPods() + ns1, err := fakeOvn.fakeClient.KubeClient.CoreV1().Namespaces().Get(context.TODO(), namespace1.Name, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + Expect(ns1).NotTo(BeNil()) + ns2, err := fakeOvn.fakeClient.KubeClient.CoreV1().Namespaces().Get(context.TODO(), namespace2.Name, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + Expect(ns2).NotTo(BeNil()) + + portsns1 := []string{} + expectedData := getMulticastPolicyExpectedData(longNameSpace1Name, portsns1) + acl := expectedData[0].(*nbdb.ACL) + // Post ACL indexing work, multicast ACL's don't have names + // We use externalIDs instead; so we can check if the expected IDs exist for the long namespace so that + // isEquivalent logic will be correct + Expect(acl.Name).To(BeNil()) + Expect(acl.ExternalIDs[libovsdbops.ObjectNameKey.String()]).To(Equal(longNameSpace1Name)) + expectedData = append(expectedData, getMulticastPolicyExpectedData(longNameSpace2Name, nil)...) + acl = expectedData[3].(*nbdb.ACL) + Expect(acl.Name).To(BeNil()) + Expect(acl.ExternalIDs[libovsdbops.ObjectNameKey.String()]).To(Equal(longNameSpace2Name)) + expectedData = append(expectedData, getExpectedDataPodsAndSwitches([]testPod{}, []string{"node1"})...) + // Enable multicast in the namespace. + updateMulticast(fakeOvn, ns1, true) + updateMulticast(fakeOvn, ns2, true) + Eventually(fakeOvn.nbClient).Should(libovsdb.HaveData(expectedData...)) + return nil + } + + err := app.Run([]string{app.Name}) + Expect(err).NotTo(HaveOccurred()) + }) + + It("tests adding a pod to a multicast enabled namespace "+ipModeStr(m), func() { + app.Action = func(ctx *cli.Context) error { + namespace1 := *newNamespace(namespaceName1) + _, tPods, tPodIPs := createTestPods(nodeName, namespaceName1, m) + + ports := []string{} + for _, pod := range tPods { + ports = append(ports, pod.portUUID) + } + + fakeOvn.startWithDBSetup(libovsdb.TestSetup{NBData: getNodeSwitch(nodeName)}, + &v1.NamespaceList{ + Items: []v1.Namespace{ + namespace1, + }, + }, + &v1.NodeList{ + Items: []v1.Node{ + *newNode("node1", "192.168.126.202/24"), + }, + }, + ) + setIpMode(m) + + err := fakeOvn.controller.WatchNamespaces() + Expect(err).NotTo(HaveOccurred()) + err = fakeOvn.controller.WatchPods() + Expect(err).NotTo(HaveOccurred()) + ns, err := fakeOvn.fakeClient.KubeClient.CoreV1().Namespaces().Get(context.TODO(), namespace1.Name, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + Expect(ns).NotTo(BeNil()) + + // Enable multicast in the namespace. + updateMulticast(fakeOvn, ns, true) + // Check expected data without pods + expectedDataWithoutPods := getMulticastPolicyExpectedData(namespace1.Name, nil) + expectedDataWithoutPods = append(expectedDataWithoutPods, getNodeSwitch(nodeName)...) + Eventually(fakeOvn.nbClient).Should(libovsdb.HaveData(expectedDataWithoutPods)) + + // Create pods + for _, tPod := range tPods { + tPod.populateLogicalSwitchCache(fakeOvn) + _, err = fakeOvn.fakeClient.KubeClient.CoreV1().Pods(tPod.namespace).Create(context.TODO(), newPod( + tPod.namespace, tPod.podName, tPod.nodeName, tPod.podIP), metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + } + // Check pods were added + fakeOvn.asf.EventuallyExpectAddressSetWithAddresses(namespace1.Name, tPodIPs) + expectedDataWithPods := getMulticastPolicyExpectedData(namespace1.Name, ports) + expectedDataWithPods = append(expectedDataWithPods, getExpectedDataPodsAndSwitches(tPods, []string{nodeName})...) + Eventually(fakeOvn.nbClient).Should(libovsdb.HaveData(expectedDataWithPods...)) + + // Delete the pod from the namespace. + for _, tPod := range tPods { + err = fakeOvn.fakeClient.KubeClient.CoreV1().Pods(tPod.namespace).Delete(context.TODO(), + tPod.podName, *metav1.NewDeleteOptions(0)) + Expect(err).NotTo(HaveOccurred()) + } + fakeOvn.asf.EventuallyExpectEmptyAddressSetExist(namespace1.Name) + Eventually(fakeOvn.nbClient).Should(libovsdb.HaveData(expectedDataWithoutPods)) + + return nil + } + + err := app.Run([]string{app.Name}) + Expect(err).NotTo(HaveOccurred()) + }) + } + }) + */ +})