diff --git a/go-controller/pkg/ovn/multihoming_test.go b/go-controller/pkg/ovn/multihoming_test.go index ee67dc1e6e..a0d31b3ebe 100644 --- a/go-controller/pkg/ovn/multihoming_test.go +++ b/go-controller/pkg/ovn/multihoming_test.go @@ -19,6 +19,7 @@ import ( "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/kube" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/nbdb" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing" + ovntest "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing" libovsdbtest "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing/libovsdb" ovntypes "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/util" @@ -122,14 +123,13 @@ func (em *secondaryNetworkExpectationMachine) expectedLogicalSwitchesAndPorts(is if !ok { continue } - - subnets := ocInfo.bnc.Subnets() + subnets := podInfo.nodeSubnet var ( - subnet config.CIDRNetworkEntry + subnet *net.IPNet hasSubnets bool ) if len(subnets) > 0 { - subnet = subnets[0] + subnet = ovntest.MustParseIPNet(subnets) hasSubnets = true } @@ -154,7 +154,7 @@ func (em *secondaryNetworkExpectationMachine) expectedLogicalSwitchesAndPorts(is switch ocInfo.bnc.TopologyType() { case ovntypes.Layer3Topology: switchName = ocInfo.bnc.GetNetworkScopedName(pod.nodeName) - managementIP := managementPortIP(subnet.CIDR) + managementIP := managementPortIP(subnet) switchToRouterPortName := "stor-" + switchName switchToRouterPortUUID := switchToRouterPortName + "-UUID" @@ -176,7 +176,7 @@ func (em *secondaryNetworkExpectationMachine) expectedLogicalSwitchesAndPorts(is } case ovntypes.Layer2Topology: switchName = ocInfo.bnc.GetNetworkScopedName(ovntypes.OVNLayer2Switch) - managementIP := managementPortIP(subnet.CIDR) + managementIP := managementPortIP(subnet) if em.gatewayConfig != nil { // there are multiple mgmt ports in the cluster, thus the ports must be scoped with the node name @@ -222,8 +222,8 @@ func (em *secondaryNetworkExpectationMachine) expectedLogicalSwitchesAndPorts(is var otherConfig map[string]string if hasSubnets { otherConfig = map[string]string{ - "exclude_ips": managementPortIP(subnet.CIDR).String(), - "subnet": subnet.CIDR.String(), + "exclude_ips": managementPortIP(subnet).String(), + "subnet": subnet.String(), } } @@ -247,8 +247,8 @@ func (em *secondaryNetworkExpectationMachine) expectedLogicalSwitchesAndPorts(is }) if em.gatewayConfig != nil { if ocInfo.bnc.TopologyType() == ovntypes.Layer3Topology { - data = append(data, expectedGWEntities(pod.nodeName, ocInfo.bnc, *em.gatewayConfig)...) - data = append(data, expectedLayer3EgressEntities(ocInfo.bnc, *em.gatewayConfig)...) + data = append(data, expectedGWEntities(pod.nodeName, subnets, ocInfo.bnc, *em.gatewayConfig)...) + data = append(data, expectedLayer3EgressEntities(ocInfo.bnc, *em.gatewayConfig, subnet)...) } else { data = append(data, expectedLayer2EgressEntities(ocInfo.bnc, *em.gatewayConfig, pod.nodeName)...) } @@ -464,7 +464,7 @@ func dummyOVNPodNetworkAnnotationForNetwork(netConfig secondaryNetInfo, tunnelID gateways []string ips []string ) - for _, subnetStr := range strings.Split(netConfig.subnets, ",") { + for _, subnetStr := range strings.Split(netConfig.clustersubnets, ",") { subnet := testing.MustParseIPNet(subnetStr) ips = append(ips, GetWorkloadSecondaryNetworkDummyIP(subnet).String()) gateways = append(gateways, util.GetNodeGatewayIfAddr(subnet).IP.String()) diff --git a/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go b/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go index 774f9b7803..69396c7d7d 100644 --- a/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go +++ b/go-controller/pkg/ovn/secondary_layer2_network_controller_test.go @@ -297,10 +297,10 @@ var _ = Describe("OVN Multi-Homed pod operations for layer2 network", func() { func dummySecondaryLayer2UserDefinedNetwork(subnets string) secondaryNetInfo { return secondaryNetInfo{ - netName: secondaryNetworkName, - nadName: namespacedName(ns, nadName), - topology: ovntypes.Layer2Topology, - subnets: subnets, + netName: secondaryNetworkName, + nadName: namespacedName(ns, nadName), + topology: ovntypes.Layer2Topology, + clustersubnets: subnets, } } @@ -329,7 +329,7 @@ func dummyL2TestPod(nsName string, info secondaryNetInfo) testPod { pod.addNetwork( info.netName, info.nadName, - info.subnets, + info.clustersubnets, "", "100.200.0.1", "100.200.0.3/16", @@ -353,7 +353,7 @@ func dummyL2TestPod(nsName string, info secondaryNetInfo) testPod { pod.addNetwork( info.netName, info.nadName, - info.subnets, + info.clustersubnets, "", "", "100.200.0.1/16", @@ -459,10 +459,10 @@ func ipv4DefaultRoute() *net.IPNet { func dummyLayer2SecondaryUserDefinedNetwork(subnets string) secondaryNetInfo { return secondaryNetInfo{ - netName: secondaryNetworkName, - nadName: namespacedName(ns, nadName), - topology: ovntypes.Layer2Topology, - subnets: subnets, + netName: secondaryNetworkName, + nadName: namespacedName(ns, nadName), + topology: ovntypes.Layer2Topology, + clustersubnets: subnets, } } diff --git a/go-controller/pkg/ovn/secondary_layer3_network_controller.go b/go-controller/pkg/ovn/secondary_layer3_network_controller.go index 71b3f476c3..a27428dd7b 100644 --- a/go-controller/pkg/ovn/secondary_layer3_network_controller.go +++ b/go-controller/pkg/ovn/secondary_layer3_network_controller.go @@ -682,7 +682,7 @@ func (oc *SecondaryLayer3NetworkController) addUpdateLocalNodeEvent(node *kapi.N gwConfig.config, gwConfig.hostSubnets, gwConfig.hostAddrs, - gwConfig.hostSubnets, + gwConfig.clusterSubnets, gwConfig.gwLRPIPs, oc.SCTPSupport, oc.ovnClusterLRPToJoinIfAddrs, @@ -858,11 +858,12 @@ func (oc *SecondaryLayer3NetworkController) gatherJoinSwitchIPs() error { } type SecondaryL3GatewayConfig struct { - config *util.L3GatewayConfig - hostSubnets []*net.IPNet - gwLRPIPs []*net.IPNet - hostAddrs []string - externalIPs []net.IP + config *util.L3GatewayConfig + hostSubnets []*net.IPNet + clusterSubnets []*net.IPNet + gwLRPIPs []*net.IPNet + hostAddrs []string + externalIPs []net.IP } func (oc *SecondaryLayer3NetworkController) nodeGatewayConfig(node *kapi.Node) (*SecondaryL3GatewayConfig, error) { @@ -898,10 +899,16 @@ func (oc *SecondaryLayer3NetworkController) nodeGatewayConfig(node *kapi.Node) ( hostAddrs = append(hostAddrs, externalIP.String()) } - // Use the host subnets present in the network attachment definition. - hostSubnets := make([]*net.IPNet, 0, len(oc.Subnets())) + // Use the cluster subnets present in the network attachment definition. + clusterSubnets := make([]*net.IPNet, 0, len(oc.Subnets())) for _, subnet := range oc.Subnets() { - hostSubnets = append(hostSubnets, subnet.CIDR) + clusterSubnets = append(clusterSubnets, subnet.CIDR) + } + + // Fetch the host subnets present in the node annotation for this network + hostSubnets, err := util.ParseNodeHostSubnetAnnotation(node, oc.GetNetworkName()) + if err != nil { + return nil, fmt.Errorf("failed to get node %q subnet annotation for network %q: %v", node.Name, oc.GetNetworkName(), err) } gwLRPIPs, err := util.ParseNodeGatewayRouterJoinAddrs(node, oc.GetNetworkName()) @@ -913,11 +920,12 @@ func (oc *SecondaryLayer3NetworkController) nodeGatewayConfig(node *kapi.Node) ( l3GatewayConfig.InterfaceID = oc.GetNetworkScopedExtPortName(l3GatewayConfig.BridgeID, node.Name) return &SecondaryL3GatewayConfig{ - config: l3GatewayConfig, - hostSubnets: hostSubnets, - gwLRPIPs: gwLRPIPs, - hostAddrs: hostAddrs, - externalIPs: externalIPs, + config: l3GatewayConfig, + hostSubnets: hostSubnets, + clusterSubnets: clusterSubnets, + gwLRPIPs: gwLRPIPs, + hostAddrs: hostAddrs, + externalIPs: externalIPs, }, nil } diff --git a/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go b/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go index 38c24b0ff3..27e1fc660c 100644 --- a/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go +++ b/go-controller/pkg/ovn/secondary_layer3_network_controller_test.go @@ -23,6 +23,7 @@ import ( "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/config" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/nbdb" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing" + ovntest "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing" libovsdbtest "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing/libovsdb" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types" ovntypes "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types" @@ -30,11 +31,12 @@ import ( ) type secondaryNetInfo struct { - netName string - nadName string - subnets string - topology string - isPrimary bool + netName string + nadName string + clustersubnets string + hostsubnets string // not used in layer2 tests + topology string + isPrimary bool } const ( @@ -190,23 +192,23 @@ var _ = Describe("OVN Multi-Homed pod operations", func() { Expect(app.Run([]string{app.Name})).To(Succeed()) }, table.Entry("pod on a user defined secondary network", - dummySecondaryUserDefinedNetwork("192.168.0.0/16"), + dummySecondaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"), nonICClusterTestConfiguration(), ), table.Entry("pod on a user defined primary network", - dummyPrimaryUserDefinedNetwork("192.168.0.0/16"), + dummyPrimaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"), nonICClusterTestConfiguration(), ), table.Entry("pod on a user defined secondary network on an interconnect cluster", - dummySecondaryUserDefinedNetwork("192.168.0.0/16"), + dummySecondaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"), icClusterTestConfiguration(), ), table.Entry("pod on a user defined primary network on an interconnect cluster", - dummyPrimaryUserDefinedNetwork("192.168.0.0/16"), + dummyPrimaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"), icClusterTestConfiguration(), ), table.Entry("pod on a user defined primary network on an interconnect cluster", - dummyPrimaryUserDefinedNetwork("192.168.0.0/16"), + dummyPrimaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"), icClusterWithDisableSNATTestConfiguration(), ), ) @@ -249,10 +251,10 @@ var _ = Describe("OVN Multi-Homed pod operations", func() { Expect(err).NotTo(HaveOccurred()) initialDB.NBData = append( initialDB.NBData, - expectedGWEntities(podInfo.nodeName, networkConfig, *gwConfig)...) + expectedGWEntities(podInfo.nodeName, netInfo.hostsubnets, networkConfig, *gwConfig)...) initialDB.NBData = append( initialDB.NBData, - expectedLayer3EgressEntities(networkConfig, *gwConfig)...) + expectedLayer3EgressEntities(networkConfig, *gwConfig, ovntest.MustParseIPNet(netInfo.hostsubnets))...) } initialDB.NBData = append(initialDB.NBData, nbZone) @@ -313,15 +315,15 @@ var _ = Describe("OVN Multi-Homed pod operations", func() { Expect(app.Run([]string{app.Name})).To(Succeed()) }, table.Entry("pod on a user defined primary network", - dummyPrimaryUserDefinedNetwork("192.168.0.0/16"), + dummyPrimaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"), nonICClusterTestConfiguration(), ), table.Entry("pod on a user defined primary network on an interconnect cluster", - dummyPrimaryUserDefinedNetwork("192.168.0.0/16"), + dummyPrimaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"), icClusterTestConfiguration(), ), table.Entry("pod on a user defined primary network on an interconnect cluster", - dummyPrimaryUserDefinedNetwork("192.168.0.0/16"), + dummyPrimaryLayer3UserDefinedNetwork("192.168.0.0/16", "192.168.1.0/24"), icClusterWithDisableSNATTestConfiguration(), ), ) @@ -349,25 +351,25 @@ func newPodWithPrimaryUDN( pod.addNetwork( primaryUDNConfig.netName, primaryUDNConfig.nadName, - primaryUDNConfig.subnets, + primaryUDNConfig.hostsubnets, "", nodeGWIP, - "192.168.0.3/16", - "0a:58:c0:a8:00:03", + "192.168.1.3/24", + "0a:58:c0:a8:01:03", "primary", 0, []util.PodRoute{ { Dest: testing.MustParseIPNet("192.168.0.0/16"), - NextHop: testing.MustParseIP("192.168.0.1"), + NextHop: testing.MustParseIP("192.168.1.1"), }, { Dest: testing.MustParseIPNet("172.16.1.0/24"), - NextHop: testing.MustParseIP("192.168.0.1"), + NextHop: testing.MustParseIP("192.168.1.1"), }, { Dest: testing.MustParseIPNet("100.65.0.0/16"), - NextHop: testing.MustParseIP("192.168.0.1"), + NextHop: testing.MustParseIP("192.168.1.1"), }, }, ) @@ -433,7 +435,7 @@ func (sni *secondaryNetInfo) netconf() *ovncnitypes.NetConf { }, Topology: sni.topology, NADName: sni.nadName, - Subnets: sni.subnets, + Subnets: sni.clustersubnets, Role: role, } } @@ -445,7 +447,7 @@ func dummyTestPod(nsName string, info secondaryNetInfo) testPod { nodeName, nodeSubnet, "10.128.1.2", - "192.168.0.1", + "192.168.1.1", "myPod", "10.128.1.3", "0a:58:0a:80:01:03", @@ -457,17 +459,17 @@ func dummyTestPod(nsName string, info secondaryNetInfo) testPod { pod.addNetwork( info.netName, info.nadName, - info.subnets, + info.hostsubnets, "", "", - "192.168.0.3/16", - "0a:58:c0:a8:00:03", + "192.168.1.3/24", + "0a:58:c0:a8:01:03", "secondary", 0, []util.PodRoute{ { - Dest: testing.MustParseIPNet("192.168.0.0/16"), - NextHop: testing.MustParseIP("192.168.0.1"), + Dest: testing.MustParseIPNet(info.clustersubnets), + NextHop: testing.MustParseIP("192.168.1.1"), }, }, ) @@ -479,23 +481,25 @@ func dummyTestPodAdditionalNetworkIP() string { return dummyTestPod(ns, secNetInfo).getNetworkPortInfo(secNetInfo.netName, secNetInfo.nadName).podIP } -func dummySecondaryUserDefinedNetwork(subnets string) secondaryNetInfo { +func dummySecondaryLayer3UserDefinedNetwork(clustersubnets, hostsubnets string) secondaryNetInfo { return secondaryNetInfo{ - netName: secondaryNetworkName, - nadName: namespacedName(ns, nadName), - topology: ovntypes.Layer3Topology, - subnets: subnets, + netName: secondaryNetworkName, + nadName: namespacedName(ns, nadName), + topology: ovntypes.Layer3Topology, + clustersubnets: clustersubnets, + hostsubnets: hostsubnets, } } -func dummyPrimaryUserDefinedNetwork(subnets string) secondaryNetInfo { - secondaryNet := dummySecondaryUserDefinedNetwork(subnets) +func dummyPrimaryLayer3UserDefinedNetwork(clustersubnets, hostsubnets string) secondaryNetInfo { + secondaryNet := dummySecondaryLayer3UserDefinedNetwork(clustersubnets, hostsubnets) secondaryNet.isPrimary = true return secondaryNet } +// This util is returning a network-name/hostSubnet for the node's node-subnets annotation func (sni *secondaryNetInfo) String() string { - return fmt.Sprintf("%q: %q", sni.netName, sni.subnets) + return fmt.Sprintf("%q: %q", sni.netName, sni.hostsubnets) } func newNodeWithSecondaryNets(nodeName string, nodeIPv4CIDR string, netInfos ...secondaryNetInfo) (*v1.Node, error) { @@ -562,7 +566,7 @@ func emptyDefaultClusterNetworkNodeSwitch(nodeName string) []libovsdbtest.TestDa return []libovsdbtest.TestData{&nbdb.LogicalSwitch{UUID: switchUUID, Name: nodeName}} } -func expectedGWEntities(nodeName string, netInfo util.NetInfo, gwConfig util.L3GatewayConfig) []libovsdbtest.TestData { +func expectedGWEntities(nodeName, nodeSubnet string, netInfo util.NetInfo, gwConfig util.L3GatewayConfig) []libovsdbtest.TestData { gwRouterName := fmt.Sprintf("GR_%s_%s", netInfo.GetNetworkName(), nodeName) expectedEntities := append( @@ -597,7 +601,6 @@ func expectedGWRouterPlusNATAndStaticRoutes( staticRouteOutputPort := ovntypes.GWRouterToExtSwitchPrefix + netInfo.GetNetworkScopedGWRouterName(nodeName) nextHopIP := gwConfig.NextHops[0].String() - ipv4Subnet := networkSubnet(netInfo) nextHopMasqIP := nextHopMasqueradeIP().String() masqSubnet := config.Gateway.V4MasqueradeSubnet var nat []string @@ -616,7 +619,7 @@ func expectedGWRouterPlusNATAndStaticRoutes( Nat: nat, StaticRoutes: []string{staticRoute1, staticRoute2, staticRoute3}, }, - expectedGRStaticRoute(staticRoute1, ipv4Subnet, dummyJoinIP().IP.String(), nil, nil, netInfo), + expectedGRStaticRoute(staticRoute1, netInfo.Subnets()[0].CIDR.String(), dummyJoinIP().IP.String(), nil, nil, netInfo), expectedGRStaticRoute(staticRoute2, ipv4DefaultRoute, nextHopIP, nil, &staticRouteOutputPort, netInfo), expectedGRStaticRoute(staticRoute3, masqSubnet, nextHopMasqIP, nil, &staticRouteOutputPort, netInfo), } @@ -624,7 +627,7 @@ func expectedGWRouterPlusNATAndStaticRoutes( expectedEntities = append(expectedEntities, newNATEntry(perPodSNAT, dummyJoinIP().IP.String(), dummyTestPodAdditionalNetworkIP(), nil)) } else { expectedEntities = append(expectedEntities, newNATEntry(nat1, dummyJoinIP().IP.String(), gwRouterIPAddress().IP.String(), standardNonDefaultNetworkExtIDs(netInfo))) - expectedEntities = append(expectedEntities, newNATEntry(nat2, dummyJoinIP().IP.String(), networkSubnet(netInfo), standardNonDefaultNetworkExtIDs(netInfo))) + expectedEntities = append(expectedEntities, newNATEntry(nat2, dummyJoinIP().IP.String(), netInfo.Subnets()[0].CIDR.String(), standardNonDefaultNetworkExtIDs(netInfo))) } return expectedEntities } @@ -679,7 +682,7 @@ func expectedLogicalRouterPort(lrpName string, netInfo util.NetInfo, options map } } -func expectedLayer3EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayConfig) []libovsdbtest.TestData { +func expectedLayer3EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayConfig, nodeSubnet *net.IPNet) []libovsdbtest.TestData { const ( routerPolicyUUID1 = "lrpol1-UUID" routerPolicyUUID2 = "lrpol2-UUID" @@ -691,8 +694,6 @@ func expectedLayer3EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayC rtosLRPName := fmt.Sprintf("%s%s", ovntypes.RouterToSwitchPrefix, netInfo.GetNetworkScopedName(nodeName)) rtosLRPUUID := rtosLRPName + "-UUID" nodeIP := gwConfig.IPAddresses[0].IP.String() - networkIPv4Subnet := networkSubnet(netInfo) - subnet := netInfo.Subnets()[0] // egress requires subnets. So far, these helpers do not work for dual-stack gatewayChassisUUID := fmt.Sprintf("%s-%s-UUID", rtosLRPName, gwConfig.ChassisID) expectedEntities := []libovsdbtest.TestData{ @@ -704,11 +705,11 @@ func expectedLayer3EgressEntities(netInfo util.NetInfo, gwConfig util.L3GatewayC Policies: []string{routerPolicyUUID1, routerPolicyUUID2}, ExternalIDs: standardNonDefaultNetworkExtIDs(netInfo), }, - &nbdb.LogicalRouterPort{UUID: rtosLRPUUID, Name: rtosLRPName, Networks: []string{"192.168.0.1/16"}, MAC: "0a:58:c0:a8:00:01", GatewayChassis: []string{gatewayChassisUUID}}, - expectedGRStaticRoute(staticRouteUUID1, networkIPv4Subnet, gwRouterIPAddress().IP.String(), &nbdb.LogicalRouterStaticRoutePolicySrcIP, nil, netInfo), + &nbdb.LogicalRouterPort{UUID: rtosLRPUUID, Name: rtosLRPName, Networks: []string{"192.168.1.1/24"}, MAC: "0a:58:c0:a8:01:01", GatewayChassis: []string{gatewayChassisUUID}}, + expectedGRStaticRoute(staticRouteUUID1, nodeSubnet.String(), gwRouterIPAddress().IP.String(), &nbdb.LogicalRouterStaticRoutePolicySrcIP, nil, netInfo), expectedGRStaticRoute(staticRouteUUID2, gwRouterIPAddress().IP.String(), gwRouterIPAddress().IP.String(), nil, nil, netInfo), - expectedLogicalRouterPolicy(routerPolicyUUID1, netInfo, nodeName, nodeIP, managementPortIP(subnet.CIDR).String()), - expectedLogicalRouterPolicy(routerPolicyUUID2, netInfo, nodeName, joinIPAddr, managementPortIP(subnet.CIDR).String()), + expectedLogicalRouterPolicy(routerPolicyUUID1, netInfo, nodeName, nodeIP, managementPortIP(nodeSubnet).String()), + expectedLogicalRouterPolicy(routerPolicyUUID2, netInfo, nodeName, joinIPAddr, managementPortIP(nodeSubnet).String()), } return expectedEntities } @@ -862,10 +863,6 @@ func gwRouterIPAddress() *net.IPNet { } } -func networkSubnet(netInfo util.NetInfo) string { - return strings.TrimSuffix(subnetsAsString(netInfo.Subnets())[0], "/24") -} - func gwRouterOptions(gwConfig util.L3GatewayConfig) map[string]string { return map[string]string{ "lb_force_snat_ip": "router_ip",