Skip to content

Commit 945c6c4

Browse files
authored
Merge pull request #5010 from ormergi/cudn-localnet
crds,cudn: Support localnet topology
2 parents 309ce32 + 314f483 commit 945c6c4

25 files changed

+1946
-35
lines changed

dist/templates/k8s.ovn.org_clusteruserdefinednetworks.yaml.j2

Lines changed: 201 additions & 4 deletions
Large diffs are not rendered by default.

dist/templates/k8s.ovn.org_userdefinednetworks.yaml.j2

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ spec:
5151
description: |-
5252
Lifecycle controls IP addresses management lifecycle.
5353

54-
The only allowed value is Persistent. When set, OVN Kubernetes assigned IP addresses will be persisted in an
54+
The only allowed value is Persistent. When set, the IP addresses assigned by OVN Kubernetes will be persisted in an
5555
`ipamclaims.k8s.cni.cncf.io` object. These IP addresses will be reused by other pods if requested.
5656
Only supported when mode is `Enabled`.
5757
enum:
@@ -155,7 +155,7 @@ spec:
155155
- message: JoinSubnets is only supported for Primary network
156156
rule: '!has(self.joinSubnets) || has(self.role) && self.role ==
157157
''Primary'''
158-
- message: MTU should be greater than or equal to 1280 when IPv6 subent
158+
- message: MTU should be greater than or equal to 1280 when IPv6 subnet
159159
is used
160160
rule: '!has(self.subnets) || !has(self.mtu) || !self.subnets.exists_one(i,
161161
isCIDR(i) && cidr(i).ip().family() == 6) || self.mtu >= 1280'
@@ -255,7 +255,7 @@ spec:
255255
- message: JoinSubnets is only supported for Primary network
256256
rule: '!has(self.joinSubnets) || has(self.role) && self.role ==
257257
''Primary'''
258-
- message: MTU should be greater than or equal to 1280 when IPv6 subent
258+
- message: MTU should be greater than or equal to 1280 when IPv6 subnet
259259
is used
260260
rule: '!has(self.subnets) || !has(self.mtu) || !self.subnets.exists_one(i,
261261
isCIDR(i.cidr) && cidr(i.cidr).ip().family() == 6) || self.mtu

docs/api-reference/userdefinednetwork-api-spec.md

Lines changed: 91 additions & 15 deletions
Large diffs are not rendered by default.

go-controller/pkg/clustermanager/userdefinednetwork/template/net-attach-def-template.go

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"sigs.k8s.io/controller-runtime/pkg/client"
1414

1515
ovncnitypes "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/cni/types"
16+
"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/config"
1617
userdefinednetworkv1 "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/crd/userdefinednetwork/v1"
1718
"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types"
1819
"github.com/ovn-org/ovn-kubernetes/go-controller/pkg/util"
@@ -31,6 +32,7 @@ type SpecGetter interface {
3132
GetTopology() userdefinednetworkv1.NetworkTopology
3233
GetLayer3() *userdefinednetworkv1.Layer3Config
3334
GetLayer2() *userdefinednetworkv1.Layer2Config
35+
GetLocalnet() *userdefinednetworkv1.LocalnetConfig
3436
}
3537

3638
func RenderNetAttachDefManifest(obj client.Object, targetNamespace string) (*netv1.NetworkAttachmentDefinition, error) {
@@ -109,7 +111,8 @@ func renderNADLabels(obj client.Object) map[string]string {
109111

110112
func validateTopology(spec SpecGetter) error {
111113
if spec.GetTopology() == userdefinednetworkv1.NetworkTopologyLayer3 && spec.GetLayer3() == nil ||
112-
spec.GetTopology() == userdefinednetworkv1.NetworkTopologyLayer2 && spec.GetLayer2() == nil {
114+
spec.GetTopology() == userdefinednetworkv1.NetworkTopologyLayer2 && spec.GetLayer2() == nil ||
115+
spec.GetTopology() == userdefinednetworkv1.NetworkTopologyLocalnet && spec.GetLocalnet() == nil {
113116
return fmt.Errorf("topology %[1]s is specified but %[1]s config is nil", spec.GetTopology())
114117
}
115118
return nil
@@ -138,17 +141,30 @@ func renderCNINetworkConfig(networkName, nadName string, spec SpecGetter) (map[s
138141
if err := validateIPAM(cfg.IPAM); err != nil {
139142
return nil, err
140143
}
141-
netConfSpec.Role = strings.ToLower(string(cfg.Role))
142-
netConfSpec.MTU = int(cfg.MTU)
143-
netConfSpec.AllowPersistentIPs = cfg.IPAM != nil && cfg.IPAM.Lifecycle == userdefinednetworkv1.IPAMLifecyclePersistent
144144
if ipamEnabled(cfg.IPAM) && len(cfg.Subnets) == 0 {
145145
return nil, fmt.Errorf("subnets is required with ipam.mode is Enabled or unset")
146146
}
147147
if !ipamEnabled(cfg.IPAM) && len(cfg.Subnets) > 0 {
148148
return nil, fmt.Errorf("subnets must be unset when ipam.mode is Disabled")
149149
}
150+
151+
netConfSpec.Role = strings.ToLower(string(cfg.Role))
152+
netConfSpec.MTU = int(cfg.MTU)
153+
netConfSpec.AllowPersistentIPs = cfg.IPAM != nil && cfg.IPAM.Lifecycle == userdefinednetworkv1.IPAMLifecyclePersistent
150154
netConfSpec.Subnets = cidrString(cfg.Subnets)
151155
netConfSpec.JoinSubnet = cidrString(renderJoinSubnets(cfg.Role, cfg.JoinSubnets))
156+
case userdefinednetworkv1.NetworkTopologyLocalnet:
157+
cfg := spec.GetLocalnet()
158+
netConfSpec.Role = strings.ToLower(string(cfg.Role))
159+
netConfSpec.MTU = localnetMTU(cfg.MTU)
160+
netConfSpec.AllowPersistentIPs = cfg.IPAM != nil && cfg.IPAM.Lifecycle == userdefinednetworkv1.IPAMLifecyclePersistent
161+
netConfSpec.Subnets = cidrString(cfg.Subnets)
162+
netConfSpec.ExcludeSubnets = cidrString(cfg.ExcludeSubnets)
163+
netConfSpec.PhysicalNetworkName = cfg.PhysicalNetworkName
164+
165+
if cfg.VLAN != nil && cfg.VLAN.Access != nil {
166+
netConfSpec.VLANID = int(cfg.VLAN.Access.ID)
167+
}
152168
}
153169

154170
if err := util.ValidateNetConf(nadName, netConfSpec); err != nil {
@@ -184,10 +200,29 @@ func renderCNINetworkConfig(networkName, nadName string, spec SpecGetter) (map[s
184200
if netConfSpec.AllowPersistentIPs {
185201
cniNetConf["allowPersistentIPs"] = netConfSpec.AllowPersistentIPs
186202
}
187-
203+
if netConfSpec.PhysicalNetworkName != "" {
204+
cniNetConf["physicalNetworkName"] = netConfSpec.PhysicalNetworkName
205+
}
206+
if len(netConfSpec.ExcludeSubnets) > 0 {
207+
cniNetConf["excludeSubnets"] = netConfSpec.ExcludeSubnets
208+
}
209+
if netConfSpec.VLANID != 0 {
210+
cniNetConf["vlanID"] = netConfSpec.VLANID
211+
}
188212
return cniNetConf, nil
189213
}
190214

215+
func localnetMTU(desiredMTU int32) int {
216+
// The MTU for localnet topology should be as the default MTU (1500) because the underlay
217+
// is not part of the SDN and compensating for the SDN overhead (100) is not required.
218+
mtu := config.Default.MTU + 100
219+
if desiredMTU > 0 {
220+
mtu = int(desiredMTU)
221+
}
222+
223+
return mtu
224+
}
225+
191226
func ipamEnabled(ipam *userdefinednetworkv1.IPAMConfig) bool {
192227
return ipam == nil || ipam.Mode == "" || ipam.Mode == userdefinednetworkv1.IPAMEnabled
193228
}

go-controller/pkg/clustermanager/userdefinednetwork/template/net-attach-def-template_test.go

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ var _ = Describe("NetAttachDefTemplate", func() {
169169
),
170170
)
171171

172-
DescribeTable("should fail to render NAD, given",
172+
DescribeTable("should fail to render NAD manifest, given",
173173
func(obj client.Object) {
174174
_, err := RenderNetAttachDefManifest(obj, "test")
175175
Expect(err).To(HaveOccurred())
@@ -223,10 +223,35 @@ var _ = Describe("NetAttachDefTemplate", func() {
223223
&udnv1.ClusterUserDefinedNetwork{Spec: udnv1.ClusterUserDefinedNetworkSpec{Network: udnv1.NetworkSpec{
224224
Topology: udnv1.NetworkTopologyLayer2, Layer3: &udnv1.Layer3Config{}}}},
225225
),
226+
Entry("CUDN, invalid topology: topology layer2 & localnet config",
227+
&udnv1.ClusterUserDefinedNetwork{Spec: udnv1.ClusterUserDefinedNetworkSpec{Network: udnv1.NetworkSpec{
228+
Topology: udnv1.NetworkTopologyLayer2, Localnet: &udnv1.LocalnetConfig{}}}},
229+
),
226230
Entry("CUDN, invalid topology: topology layer3 & layer2 config",
227231
&udnv1.ClusterUserDefinedNetwork{Spec: udnv1.ClusterUserDefinedNetworkSpec{Network: udnv1.NetworkSpec{
228232
Topology: udnv1.NetworkTopologyLayer3, Layer2: &udnv1.Layer2Config{}}}},
229233
),
234+
Entry("CUDN, invalid topology: topology layer3 & localnet config",
235+
&udnv1.ClusterUserDefinedNetwork{Spec: udnv1.ClusterUserDefinedNetworkSpec{Network: udnv1.NetworkSpec{
236+
Topology: udnv1.NetworkTopologyLayer3, Localnet: &udnv1.LocalnetConfig{}}}},
237+
),
238+
Entry("CUDN, invalid topology: topology localnet & layer2 config",
239+
&udnv1.ClusterUserDefinedNetwork{Spec: udnv1.ClusterUserDefinedNetworkSpec{Network: udnv1.NetworkSpec{
240+
Topology: udnv1.NetworkTopologyLocalnet, Layer2: &udnv1.Layer2Config{}}}},
241+
),
242+
Entry("CUDN, invalid topology: topology localnet & layer3 config",
243+
&udnv1.ClusterUserDefinedNetwork{Spec: udnv1.ClusterUserDefinedNetworkSpec{Network: udnv1.NetworkSpec{
244+
Topology: udnv1.NetworkTopologyLocalnet, Layer3: &udnv1.Layer3Config{}}}},
245+
),
246+
Entry("CUDN, localnet: excludeSubnets not in range of subnets",
247+
&udnv1.ClusterUserDefinedNetwork{Spec: udnv1.ClusterUserDefinedNetworkSpec{Network: udnv1.NetworkSpec{
248+
Topology: udnv1.NetworkTopologyLocalnet,
249+
Localnet: &udnv1.LocalnetConfig{Role: udnv1.NetworkRoleSecondary, PhysicalNetworkName: "localnet1",
250+
Subnets: udnv1.DualStackCIDRs{"192.168.0.0/16", "2001:dbb::/64"},
251+
ExcludeSubnets: []udnv1.CIDR{"192.200.0.0/30", "2001:aaa::/127", "192.300.0.1/32", "2001:bbb::1/120"},
252+
},
253+
}}},
254+
),
230255
)
231256

232257
It("should return no error given no UDN", func() {
@@ -492,7 +517,7 @@ var _ = Describe("NetAttachDefTemplate", func() {
492517
"allowPersistentIPs": true
493518
}`,
494519
),
495-
Entry("secondary network",
520+
Entry("secondary network, layer2",
496521
udnv1.NetworkSpec{
497522
Topology: udnv1.NetworkTopologyLayer2,
498523
Layer2: &udnv1.Layer2Config{
@@ -516,5 +541,64 @@ var _ = Describe("NetAttachDefTemplate", func() {
516541
"allowPersistentIPs": true
517542
}`,
518543
),
544+
Entry("secondary network, localnet",
545+
udnv1.NetworkSpec{
546+
Topology: udnv1.NetworkTopologyLocalnet,
547+
Localnet: &udnv1.LocalnetConfig{
548+
Role: udnv1.NetworkRoleSecondary,
549+
PhysicalNetworkName: "mylocalnet1",
550+
MTU: 1600,
551+
VLAN: &udnv1.VLANConfig{Mode: udnv1.VLANModeAccess, Access: &udnv1.AccessVLANConfig{ID: 200}},
552+
Subnets: udnv1.DualStackCIDRs{"192.168.100.0/24", "2001:dbb::/64"},
553+
ExcludeSubnets: []udnv1.CIDR{"192.168.100.1/32", "2001:dbb::0/128"},
554+
IPAM: &udnv1.IPAMConfig{
555+
Lifecycle: udnv1.IPAMLifecyclePersistent,
556+
},
557+
},
558+
},
559+
`{
560+
"cniVersion": "1.0.0",
561+
"type": "ovn-k8s-cni-overlay",
562+
"name": "cluster_udn_test-net",
563+
"netAttachDefName": "mynamespace/test-net",
564+
"role": "secondary",
565+
"topology": "localnet",
566+
"physicalNetworkName": "mylocalnet1",
567+
"subnets": "192.168.100.0/24,2001:dbb::/64",
568+
"excludeSubnets": "192.168.100.1/32,2001:dbb::0/128",
569+
"mtu": 1600,
570+
"vlanID": 200,
571+
"allowPersistentIPs": true
572+
}`,
573+
),
574+
Entry("secondary network, localnet, when MTU is unset it should set default MTU",
575+
udnv1.NetworkSpec{
576+
Topology: udnv1.NetworkTopologyLocalnet,
577+
Localnet: &udnv1.LocalnetConfig{
578+
Role: udnv1.NetworkRoleSecondary,
579+
PhysicalNetworkName: "mylocalnet1",
580+
VLAN: &udnv1.VLANConfig{Mode: udnv1.VLANModeAccess, Access: &udnv1.AccessVLANConfig{ID: 200}},
581+
Subnets: udnv1.DualStackCIDRs{"192.168.100.0/24", "2001:dbb::/64"},
582+
ExcludeSubnets: []udnv1.CIDR{"192.168.100.1/32", "2001:dbb::0/128"},
583+
IPAM: &udnv1.IPAMConfig{
584+
Lifecycle: udnv1.IPAMLifecyclePersistent,
585+
},
586+
},
587+
},
588+
`{
589+
"cniVersion": "1.0.0",
590+
"type": "ovn-k8s-cni-overlay",
591+
"name": "cluster_udn_test-net",
592+
"netAttachDefName": "mynamespace/test-net",
593+
"role": "secondary",
594+
"topology": "localnet",
595+
"physicalNetworkName": "mylocalnet1",
596+
"subnets": "192.168.100.0/24,2001:dbb::/64",
597+
"excludeSubnets": "192.168.100.1/32,2001:dbb::0/128",
598+
"mtu": 1500,
599+
"vlanID": 200,
600+
"allowPersistentIPs": true
601+
}`,
602+
),
519603
)
520604
})

go-controller/pkg/crd/userdefinednetwork/v1/apis/applyconfiguration/userdefinednetwork/v1/accessvlanconfig.go

Lines changed: 38 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go-controller/pkg/crd/userdefinednetwork/v1/apis/applyconfiguration/userdefinednetwork/v1/localnetconfig.go

Lines changed: 98 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go-controller/pkg/crd/userdefinednetwork/v1/apis/applyconfiguration/userdefinednetwork/v1/networkspec.go

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)