Skip to content

Commit fcd2b80

Browse files
Add EVPNInstance resource and controller
This change introduces a new custom resource to configure a VXLAN mapping for a VLAN (Bridge Domain) and it's corresponding EVPN control plane context as a MAC-VRF.
1 parent 0f14977 commit fcd2b80

40 files changed

+2236
-18
lines changed

PROJECT

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,4 +179,12 @@ resources:
179179
kind: VLAN
180180
path: github.com/ironcore-dev/network-operator/api/core/v1alpha1
181181
version: v1alpha1
182+
- api:
183+
crdVersion: v1
184+
namespaced: true
185+
controller: true
186+
domain: networking.metal.ironcore.dev
187+
kind: EVPNInstance
188+
path: github.com/ironcore-dev/network-operator/api/core/v1alpha1
189+
version: v1alpha1
182190
version: "3"

Tiltfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ k8s_resource(new_name='ospf-underlay', objects=['underlay:ospf'], resource_deps=
102102
k8s_yaml('./config/samples/v1alpha1_vlan.yaml')
103103
k8s_resource(new_name='vlan-10', objects=['vlan-10:vlan'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False)
104104

105+
k8s_yaml('./config/samples/v1alpha1_evi.yaml')
106+
k8s_resource(new_name='vxlan-100010', objects=['vxlan-100010:evpninstance'], resource_deps=['vlan-10'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False)
107+
105108
print('🚀 network-operator development environment')
106109
print('👉 Edit the code inside the api/, cmd/, or internal/ directories')
107110
print('👉 Tilt will automatically rebuild and redeploy when changes are detected')
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package v1alpha1
5+
6+
import (
7+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
8+
)
9+
10+
// EVPNInstanceSpec defines the desired state of EVPNInstance
11+
//
12+
// It models an EVPN instance (EVI) context on a single network device based on VXLAN encapsulation and the VLAN-based service type defined in [RFC 8365].
13+
// [RFC 8365]: https://datatracker.ietf.org/doc/html/rfc8365
14+
//
15+
// +kubebuilder:validation:XValidation:rule="self.type != 'Bridged' || has(self.vlanRef)",message="VLANRef must be specified when Type is Bridged"
16+
type EVPNInstanceSpec struct {
17+
// DeviceName is the name of the Device this object belongs to. The Device object must exist in the same namespace.
18+
// Immutable.
19+
// +required
20+
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="DeviceRef is immutable"
21+
DeviceRef LocalObjectReference `json:"deviceRef"`
22+
23+
// ProviderConfigRef is a reference to a resource holding the provider-specific configuration of this interface.
24+
// This reference is used to link the BGP to its provider-specific configuration.
25+
// +optional
26+
ProviderConfigRef *TypedLocalObjectReference `json:"providerConfigRef,omitempty"`
27+
28+
// VNI is the VXLAN Network Identifier.
29+
// Immutable.
30+
// +required
31+
// +kubebuilder:validation:Minimum=1
32+
// +kubebuilder:validation:Maximum=16777214
33+
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="VNI is immutable"
34+
VNI int32 `json:"vni"`
35+
36+
// Type specifies the EVPN instance type.
37+
// Immutable.
38+
// +required
39+
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Type is immutable"
40+
Type EVPNInstanceType `json:"type"`
41+
42+
// MulticastGroupAddress specifies the IPv4 multicast group address used for BUM (Broadcast, Unknown unicast, Multicast) traffic.
43+
// The address must be in the valid multicast range (224.0.0.0 - 239.255.255.255).
44+
// +optional
45+
// +kubebuilder:validation:Format=ipv4
46+
MulticastGroupAddress string `json:"multicastGroupAddress,omitempty"`
47+
48+
// RouteDistinguisher is the route distinguisher for the EVI.
49+
// Formats supported:
50+
// - Type 0: <asn(0-65535)>:<number(0-4294967295)>
51+
// - Type 1: <ipv4>:<number(0-65535)>
52+
// - Type 2: <asn(65536-4294967295)>:<number(0-65535)>
53+
// +optional
54+
RouteDistinguisher string `json:"routeDistinguisher,omitempty"`
55+
56+
// RouteTargets is the list of route targets for the EVI.
57+
// +optional
58+
// +listType=map
59+
// +listMapKey=value
60+
// +kubebuilder:validation:MinItems=1
61+
RouteTargets []EVPNRouteTarget `json:"routeTargets,omitempty"`
62+
63+
// VLANRef is a reference to a VLAN resource for which this EVPNInstance builds the MAC-VRF.
64+
// This field is only applicable when Type is Bridged (L2VNI).
65+
// The VLAN resource must exist in the same namespace.
66+
// Immutable.
67+
// +optional
68+
// +kubebuilder:validation:XValidation:rule="self.name == oldSelf.name",message="VLANRef is immutable"
69+
VLANRef *LocalObjectReference `json:"vlanRef,omitempty"`
70+
}
71+
72+
// EVPNInstanceType defines the type of EVPN instance.
73+
// +kubebuilder:validation:Enum=Bridged;Routed
74+
type EVPNInstanceType string
75+
76+
const (
77+
// EVPNInstanceTypeBridged represents an L2VNI (MAC-VRF) EVPN instance.
78+
// Corresponds to OpenConfig network-instance type L2VSI.
79+
EVPNInstanceTypeBridged EVPNInstanceType = "Bridged"
80+
81+
// EVPNInstanceTypeRouted represents an L3VNI (IP-VRF) EVPN instance.
82+
// Corresponds to OpenConfig network-instance type L3VRF.
83+
EVPNInstanceTypeRouted EVPNInstanceType = "Routed"
84+
)
85+
86+
type EVPNRouteTarget struct {
87+
// Value is the route target value, must have the format as RouteDistinguisher.
88+
// +required
89+
// +kubebuilder:validation:MinLength=1
90+
Value string `json:"value"`
91+
92+
// Action defines whether the route target is imported, exported, or both.
93+
// +required
94+
Action RouteTargetAction `json:"action"`
95+
}
96+
97+
// EVPNInstanceStatus defines the observed state of EVPNInstance.
98+
type EVPNInstanceStatus struct {
99+
// The conditions are a list of status objects that describe the state of the EVPNInstance.
100+
// +listType=map
101+
// +listMapKey=type
102+
// +patchStrategy=merge
103+
// +patchMergeKey=type
104+
// +optional
105+
Conditions []metav1.Condition `json:"conditions,omitempty"`
106+
}
107+
108+
// +kubebuilder:object:root=true
109+
// +kubebuilder:subresource:status
110+
// +kubebuilder:resource:path=evpninstances
111+
// +kubebuilder:resource:singular=evpninstance
112+
// +kubebuilder:resource:shortName=evi;vni
113+
// +kubebuilder:printcolumn:name="Device",type=string,JSONPath=`.spec.deviceRef.name`
114+
// +kubebuilder:printcolumn:name="Type",type=string,JSONPath=`.spec.type`
115+
// +kubebuilder:printcolumn:name="VNI",type=integer,JSONPath=`.spec.vni`
116+
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
117+
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
118+
119+
// EVPNInstance is the Schema for the evpninstances API
120+
type EVPNInstance struct {
121+
metav1.TypeMeta `json:",inline"`
122+
metav1.ObjectMeta `json:"metadata,omitempty"`
123+
124+
// Specification of the desired state of the resource.
125+
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
126+
// +required
127+
Spec EVPNInstanceSpec `json:"spec"`
128+
129+
// Status of the resource. This is set and updated automatically.
130+
// Read-only.
131+
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
132+
// +optional
133+
Status EVPNInstanceStatus `json:"status,omitempty,omitzero"`
134+
}
135+
136+
// GetConditions implements conditions.Getter.
137+
func (i *EVPNInstance) GetConditions() []metav1.Condition {
138+
return i.Status.Conditions
139+
}
140+
141+
// SetConditions implements conditions.Setter.
142+
func (i *EVPNInstance) SetConditions(conditions []metav1.Condition) {
143+
i.Status.Conditions = conditions
144+
}
145+
146+
// +kubebuilder:object:root=true
147+
148+
// EVPNInstanceList contains a list of EVPNInstance
149+
type EVPNInstanceList struct {
150+
metav1.TypeMeta `json:",inline"`
151+
metav1.ListMeta `json:"metadata,omitempty"`
152+
Items []EVPNInstance `json:"items"`
153+
}
154+
155+
func init() {
156+
SchemeBuilder.Register(&EVPNInstance{}, &EVPNInstanceList{})
157+
}

api/core/v1alpha1/groupversion_info.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ const AggregateLabel = "networking.metal.ironcore.dev/aggregate-name"
5050
// the name of the RoutedVLAN interface that provides Layer 3 routing for the VLAN.
5151
const RoutedVLANLabel = "networking.metal.ironcore.dev/routed-vlan-name"
5252

53+
// L2VNILabel is a label applied to VLANs to indicate
54+
// the name of the EVPNInstance that maps the VLAN to a L2VNI in the VXLAN fabric.
55+
const L2VNILabel = "networking.metal.ironcore.dev/evi-name"
56+
5357
// VRFLabel is a label applied to interfaces to indicate
5458
// the name of the VRF they belong to.
5559
const VRFLabel = "networking.metal.ironcore.dev/vrf-name"

api/core/v1alpha1/vlan_types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ type VLANStatus struct {
6666
// This field is set when an Interface of type RoutedVLAN references this VLAN.
6767
// +optional
6868
RoutedBy *LocalObjectReference `json:"routedBy,omitempty"`
69+
70+
// BridgedBy references the EVPNInstance that provides a L2VNI for this VLAN, if any.
71+
// This field is set when an EVPNInstance of type Bridged references this VLAN.
72+
// +optional
73+
BridgedBy *LocalObjectReference `json:"bridgedBy,omitempty"`
6974
}
7075

7176
// +kubebuilder:object:root=true

api/core/v1alpha1/zz_generated.deepcopy.go

Lines changed: 132 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)