Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -289,4 +289,12 @@ resources:
kind: DHCPRelay
path: github.com/ironcore-dev/network-operator/api/core/v1alpha1
version: v1alpha1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: networking.metal.ironcore.dev
kind: EthernetSegment
path: github.com/ironcore-dev/network-operator/api/core/v1alpha1
version: v1alpha1
version: "3"
3 changes: 3 additions & 0 deletions Tiltfile
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ k8s_resource(new_name='lldp', objects=['leaf1-lldp:lldp'], trigger_mode=TRIGGER_
k8s_yaml('./config/samples/v1alpha1_dhcprelay.yaml')
k8s_resource(new_name='dhcprelay', objects=['dhcprelay:dhcprelay'], resource_deps=['eth1-1'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False)

k8s_yaml('./config/samples/v1alpha1_ethernetsegment.yaml')
k8s_resource(new_name='ethernetsegment-sample', objects=['ethernetsegment-sample:ethernetsegment'], resource_deps=['lag-to-server1'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False)

print('🚀 network-operator development environment')
print('👉 Edit the code inside the api/, cmd/, or internal/ directories')
print('👉 Tilt will automatically rebuild and redeploy when changes are detected')
Expand Down
139 changes: 139 additions & 0 deletions api/core/v1alpha1/ethernetsegment_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and IronCore contributors
// SPDX-License-Identifier: Apache-2.0

package v1alpha1

import (
"sync"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)

// EthernetSegmentSpec defines the desired state of EthernetSegment.
//
// It models an EVPN Ethernet Segment for multihoming as defined in [RFC 7432] Section 5.
// An Ethernet Segment associates an Aggregate interface with a 10-byte Ethernet Segment
// Identifier (ESI), enabling multi-homed CE connectivity.
// [RFC 7432]: https://datatracker.ietf.org/doc/html/rfc7432
type EthernetSegmentSpec struct {
// DeviceRef is the name of the Device this object belongs to. The Device object must exist in the same namespace.
// Immutable.
// +required
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="DeviceRef is immutable"
DeviceRef LocalObjectReference `json:"deviceRef"`

// ProviderConfigRef is a reference to a resource holding the provider-specific configuration of this Ethernet Segment.
// +optional
ProviderConfigRef *TypedLocalObjectReference `json:"providerConfigRef,omitempty"`

// InterfaceRef is the name of the Interface this Ethernet Segment is associated with.
// The Interface must be of type Aggregate and belong to the same Device.
// Immutable.
// +required
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="InterfaceRef is immutable"
InterfaceRef LocalObjectReference `json:"interfaceRef"`

// ESI is the 10-byte Ethernet Segment Identifier in colon-separated hex notation
// (e.g., "00:11:22:33:44:55:66:77:88:01"). Must not be all-zeros or all-ones (reserved per RFC 7432).
// Immutable.
// +required
// +kubebuilder:validation:Pattern=`^([0-9a-fA-F]{2}:){9}[0-9a-fA-F]{2}$`
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="ESI is immutable"
ESI string `json:"esi"`

// RedundancyMode defines the multi-homing forwarding model for this Ethernet Segment
// as defined in RFC 7432 Section 14.1.
// +kubebuilder:validation:Enum=AllActive;SingleActive
// +kubebuilder:default=AllActive
// +optional
RedundancyMode RedundancyMode `json:"redundancyMode,omitempty"`
}

// RedundancyMode defines the forwarding model for a multi-homed Ethernet Segment.
// +kubebuilder:validation:Enum=AllActive;SingleActive
type RedundancyMode string

const (
// RedundancyModeAllActive enables all PE nodes in the segment to forward unicast
// traffic simultaneously (RFC 7432 Section 14.1.2).
RedundancyModeAllActive RedundancyMode = "AllActive"

// RedundancyModeSingleActive restricts forwarding to the elected Designated Forwarder
// only (RFC 7432 Section 14.1.1).
RedundancyModeSingleActive RedundancyMode = "SingleActive"
)

// EthernetSegmentStatus defines the observed state of EthernetSegment.
type EthernetSegmentStatus struct {
// +listType=map
// +listMapKey=type
// +patchStrategy=merge
// +patchMergeKey=type
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:path=ethernetsegments
// +kubebuilder:resource:singular=ethernetsegment
// +kubebuilder:resource:shortName=es
// +kubebuilder:printcolumn:name="ESI",type=string,JSONPath=`.spec.esi`
// +kubebuilder:printcolumn:name="Device",type=string,JSONPath=`.spec.deviceRef.name`
// +kubebuilder:printcolumn:name="Interface",type=string,JSONPath=`.spec.interfaceRef.name`
// +kubebuilder:printcolumn:name="Redundancy Mode",type=string,JSONPath=`.spec.redundancyMode`,priority=1
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
// +kubebuilder:printcolumn:name="Paused",type=string,JSONPath=`.status.conditions[?(@.type=="Paused")].status`,priority=1
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"

// EthernetSegment is the Schema for the ethernetsegments API.
type EthernetSegment struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

// Specification of the desired state of the resource.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
// +required
Spec EthernetSegmentSpec `json:"spec,omitempty"`

// Status of the resource. This is set and updated automatically.
// Read-only.
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
// +optional
Status EthernetSegmentStatus `json:"status,omitempty,omitzero"`
}

// GetConditions implements conditions.Getter.
func (e *EthernetSegment) GetConditions() []metav1.Condition {
return e.Status.Conditions
}

// SetConditions implements conditions.Setter.
func (e *EthernetSegment) SetConditions(conditions []metav1.Condition) {
e.Status.Conditions = conditions
}

// +kubebuilder:object:root=true

// EthernetSegmentList contains a list of EthernetSegment.
type EthernetSegmentList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitzero"`
Items []EthernetSegment `json:"items"`
}

var (
EthernetSegmentDependencies []schema.GroupVersionKind
ethernetSegmentDependenciesMu sync.Mutex
)

func RegisterEthernetSegmentDependency(gvk schema.GroupVersionKind) {
ethernetSegmentDependenciesMu.Lock()
defer ethernetSegmentDependenciesMu.Unlock()
EthernetSegmentDependencies = append(EthernetSegmentDependencies, gvk)
}

func init() {
SchemeBuilder.Register(&EthernetSegment{}, &EthernetSegmentList{})
}
3 changes: 3 additions & 0 deletions api/core/v1alpha1/groupversion_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ const (
// CrossDeviceReferenceReason indicates that a referenced interface belongs to a different device.
CrossDeviceReferenceReason = "CrossDeviceReference"

// InterfaceNotSwitchportReason indicates that a referenced interface does not have switchport configuration.
InterfaceNotSwitchportReason = "InterfaceNotSwitchport"

// MemberInterfaceAlreadyInUseReason indicates that a member interface is already part of another aggregate.
MemberInterfaceAlreadyInUseReason = "MemberInterfaceAlreadyInUse"

Expand Down
103 changes: 103 additions & 0 deletions api/core/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,18 @@ func main() { //nolint:gocyclo
os.Exit(1)
}

if err := (&corecontroller.EthernetSegmentReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Recorder: mgr.GetEventRecorder("ethernetsegment-controller"),
WatchFilterValue: watchFilterValue,
Provider: prov,
Locker: locker,
}).SetupWithManager(ctx, mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "EthernetSegment")
os.Exit(1)
}

if os.Getenv("ENABLE_WEBHOOKS") != "false" {
if err := webhookv1alpha1.SetupVRFWebhookWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "VRF")
Expand Down
Loading
Loading