Skip to content

Commit 218ebd5

Browse files
committed
feat: Cisco NXOS VPCs
Multi-Chassis Link Aggregation (MC-LAG) is quite vendor and platform specific. We don't see much intersection in their respective configuration to justify a common API type. Instead, we move forward with a platform specific API exclusive to Cisco NXOS devices. This commit adds new types, controller, and provider to configure virtual Port Channels (vPCs) via the operator. Implementation note: Consider the following information about the YANG model for configuring a vPC: * each vPC configured in the domain appears in the tree in this location: `vpc-items/inst-items/dom-items/if-items/If-list[id=30]` (where `30` is the vPC ID) * the peer-link interface is configured here: `vpc-items/inst-items/dom-items/keepalive-items/peerlink-items[id=po10]` The interfaces will be added to the vPC config by the LAG provider and not by this controller. Hence, if we apply a gNMI Replace operation on the xpath returned by VPC.XPath() we would remove any existing vPC interfaces. A gNMI Update operation will not modify the configuration introduced by the LAG provider.
1 parent f6f4df9 commit 218ebd5

25 files changed

+1373
-13
lines changed

PROJECT

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,13 @@ resources:
118118
kind: ISIS
119119
path: github.com/ironcore-dev/network-operator/api/v1alpha1
120120
version: v1alpha1
121+
- api:
122+
crdVersion: v1
123+
namespaced: true
124+
controller: true
125+
domain: cloud.sap
126+
group: networking
127+
kind: NXOSVPC
128+
path: github.com/ironcore-dev/network-operator/api/v1alpha1
129+
version: v1alpha1
121130
version: "3"

Tiltfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ k8s_resource(new_name='managementaccess', objects=['managementaccess:managementa
7878
k8s_yaml('./config/samples/v1alpha1_isis.yaml')
7979
k8s_resource(new_name='underlay', objects=['underlay:isis'], resource_deps=['lo0', 'lo1', 'eth1-1', 'eth1-2'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False)
8080

81+
# TODO: add resource_deps=['po1', 'po2'] when port-channel resource is implemented
82+
k8s_yaml('./config/samples/v1alpha1_nxosvpc.yaml')
83+
k8s_resource(new_name='vpc', objects=['nxosvpc:nxosvpc'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False)
84+
8185
print('🚀 network-operator development environment')
8286
print('👉 Edit the code inside the api/, cmd/, or internal/ directories')
8387
print('👉 Tilt will automatically rebuild and redeploy when changes are detected')

api/v1alpha1/nxosvpc_types.go

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+
// NXOSVPCSpec defines the desired state of NXOSVPC (Cisco's NXOS Virtual Port Channel)
11+
type NXOSVPCSpec struct {
12+
// DeviceName is the name of the Device this object belongs to. The Device object must exist in the same namespace.
13+
// Immutable.
14+
// +required
15+
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="DeviceRef is immutable"
16+
DeviceRef LocalObjectReference `json:"deviceRef"`
17+
18+
// DomainID is the vPC domain ID.
19+
// +required
20+
// +kubebuilder:validation:Minimum=1
21+
// +kubebuilder:validation:Maximum=1000
22+
DomainID uint16 `json:"domainId"`
23+
24+
// AdminState is the administrative state of the vPC domain.
25+
// +required
26+
// +kubebuilder:default="enabled"
27+
// +kubebuilder:validation:Enum=enabled;disabled
28+
AdminState string `json:"adminState"`
29+
30+
// RolePriority is the role priority for this vPC domain.
31+
// +required
32+
// +kubebuilder:validation:Minimum=1
33+
// +kubebuilder:validation:Maximum=65535
34+
RolePriority uint16 `json:"rolePriority"`
35+
36+
// SystemPriority is the system priority for this vPC domain.
37+
// +required
38+
// +kubebuilder:validation:Minimum=1
39+
// +kubebuilder:validation:Maximum=65535
40+
SystemPriority uint16 `json:"systemPriority"`
41+
42+
// DelayRestoreSVI is the delay in bringing up bringing-up the interface-vlan
43+
// +required
44+
// +kubebuilder:validation:Minimum=1
45+
// +kubebuilder:validation:Maximum=3600
46+
DelayRestoreSVI uint16 `json:"delayRestoreSVI"`
47+
48+
// DelayRestoreVPC is the delay in bringing up the vPC links after restoring the peer-link
49+
// +required
50+
// +kubebuilder:validation:Minimum=1
51+
// +kubebuilder:validation:Maximum=3600
52+
DelayRestoreVPC uint16 `json:"delayRestoreVPC"`
53+
54+
// +required
55+
FastConvergence AdminSt `json:"fastConvergence"`
56+
57+
// +required
58+
Peer Peer `json:"peer"`
59+
}
60+
61+
// AdminSt represents administrative state (enabled/disabled)
62+
type AdminSt struct {
63+
// +required
64+
Enabled bool `json:"enabled"`
65+
}
66+
67+
type Peer struct {
68+
// +required
69+
KeepAlive KeepAlive `json:"keepalive"`
70+
71+
// +required
72+
AutoRecovery AutoRecovery `json:"autoRecovery"`
73+
74+
// +required
75+
Switch AdminSt `json:"switch"`
76+
77+
// +required
78+
Gateway AdminSt `json:"gateway"`
79+
80+
// Router defines layer3 peer-router settings
81+
// +required
82+
Router AdminSt `json:"router"`
83+
}
84+
85+
type KeepAlive struct {
86+
// Destination is the destination IP address
87+
// +required
88+
Destination string `json:"destination"`
89+
90+
// Source is the source IP address
91+
// +required
92+
Source string `json:"source"`
93+
94+
// +optional
95+
VRF string `json:"vrf,omitempty"`
96+
}
97+
98+
// AutoRecovery holds autorecovery settings.
99+
// +kubebuilder:validation:XValidation:rule="self.enabled ? has(self.reloadDelay) : !has(self.reloadDelay)",message="reloadDelay must be set when enabled and absent when disabled"
100+
type AutoRecovery struct {
101+
// +required
102+
Enabled bool `json:"enabled,omitempty"`
103+
104+
// +optional
105+
// +kubebuilder:validation:Minimum=60
106+
// +kubebuilder:validation:Maximum=3600
107+
ReloadDelay uint32 `json:"reloadDelay,omitempty"`
108+
}
109+
110+
// NXOSVPCStatus defines the observed state of NXOSVPC.
111+
type NXOSVPCStatus struct {
112+
// The conditions are a list of status objects that describe the state of the VPC.
113+
//+listType=map
114+
//+listMapKey=type
115+
//+patchStrategy=merge
116+
//+patchMergeKey=type
117+
//+optional
118+
Conditions []metav1.Condition `json:"conditions,omitempty"`
119+
}
120+
121+
// +kubebuilder:object:root=true
122+
// +kubebuilder:subresource:status
123+
// +kubebuilder:resource:path=nxosvpcs
124+
// +kubebuilder:resource:singular=nxosvpc
125+
// +kubebuilder:resource:shortName=vpc
126+
// +kubebuilder:printcolumn:name="Domain",type=string,JSONPath=`.spec.domainId`
127+
// +kubebuilder:printcolumn:name="AdminState",type=string,JSONPath=`.spec.adminState`
128+
// +kubebuilder:printcolumn:name="Device",type=string,JSONPath=`.spec.deviceRef.name`
129+
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
130+
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
131+
132+
// NXOSVPC is the Schema for the nxosvpcs API
133+
type NXOSVPC struct {
134+
metav1.TypeMeta `json:",inline"`
135+
metav1.ObjectMeta `json:"metadata,omitempty"`
136+
137+
// spec defines the desired state of NXOSVPC
138+
// +required
139+
Spec NXOSVPCSpec `json:"spec,omitempty"`
140+
141+
// status defines the observed state of NXOSVPC
142+
// +optional
143+
Status NXOSVPCStatus `json:"status,omitempty,omitzero"`
144+
}
145+
146+
// +kubebuilder:object:root=true
147+
148+
// NXOSVPCList contains a list of NXOSVPC
149+
type NXOSVPCList struct {
150+
metav1.TypeMeta `json:",inline"`
151+
metav1.ListMeta `json:"metadata,omitempty"`
152+
Items []NXOSVPC `json:"items"`
153+
}
154+
155+
func init() {
156+
SchemeBuilder.Register(&NXOSVPC{}, &NXOSVPCList{})
157+
}

api/v1alpha1/zz_generated.deepcopy.go

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

cmd/main.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,17 @@ func main() {
337337
setupLog.Error(err, "unable to create controller", "controller", "ISIS")
338338
os.Exit(1)
339339
}
340+
341+
if err := (&controller.NXOSVPCReconciler{
342+
Client: mgr.GetClient(),
343+
Scheme: mgr.GetScheme(),
344+
Recorder: mgr.GetEventRecorderFor("nxosvpc-controller"),
345+
WatchFilterValue: watchFilterValue,
346+
Provider: prov,
347+
}).SetupWithManager(mgr); err != nil {
348+
setupLog.Error(err, "unable to create controller", "controller", "NXOSVPC")
349+
os.Exit(1)
350+
}
340351
// +kubebuilder:scaffold:builder
341352

342353
if metricsCertWatcher != nil {

0 commit comments

Comments
 (0)