Skip to content

Commit c72dc98

Browse files
committed
rfc: IGP resource, controller, and provider
Add a Custom Resource for Interior Gateway Protocol (IGP) spec. While we plan support for ISIS and OSPF, we consider only ISIS here. We also include a `cisco-nxos-gnmi` provider with an `isis` pkg to configure ISIS over gNMI using ygot objects from openconfig. This provider does not implement yet the `interface` resource. Both the resource and the provider implementation DO NOT support multiple processes on the same device.
1 parent ec4e7a9 commit c72dc98

22 files changed

Lines changed: 1097 additions & 125 deletions

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ install-kustomize: FORCE
4242

4343
fmt: FORCE install-gofumpt
4444
@printf "\e[1;36m>> gofumpt -l -w .\e[0m\n"
45-
@gofumpt -l -w $(shell git ls-files '*.go' | grep -v '^internal/provider/openconfig|internal/provider/cisco/nxos/genyang')
45+
@gofumpt -l -w $(shell git ls-files '*.go' | grep -v '^internal/provider/openconfig|internal/provider/cisco/nxos/genyang|internal/provider/cisco/nxos/isis')
4646

4747
# Run the e2e tests against a k8s cluster.
4848
test-e2e: FORCE
@@ -59,7 +59,7 @@ test-e2e: FORCE
5959

6060
docker-build: FORCE
6161
@printf "\e[1;36m>> $(CONTAINER_TOOL) build --tag=$(IMG) .\e[0m\n"
62-
@$(CONTAINER_TOOL) build --build-arg=BININFO_BUILD_DATE=$(BININFO_BUILD_DATE) --build-arg=BININFO_COMMIT_HASH=$(BININFO_COMMIT_HASH) --build-arg=BININFO_VERSION=$(BININFO_VERSION) --tag=$(IMG) .
62+
@$(CONTAINER_TOOL) build --no-cache --build-arg=BININFO_BUILD_DATE=$(BININFO_BUILD_DATE) --build-arg=BININFO_COMMIT_HASH=$(BININFO_COMMIT_HASH) --build-arg=BININFO_VERSION=$(BININFO_VERSION) --tag=$(IMG) .
6363

6464
docker-push: FORCE
6565
@printf "\e[1;36m>> $(CONTAINER_TOOL) push $(IMG)\e[0m\n"
@@ -188,7 +188,7 @@ ifeq ($(GO_TESTPKGS),)
188188
GO_TESTPKGS := ./...
189189
endif
190190
# which packages to measure coverage for
191-
GO_COVERPKGS := $(shell go list ./... | grep -E '/internal' | grep -Ev '/internal/provider/openconfig|internal/provider/cisco/nxos/genyang')
191+
GO_COVERPKGS := $(shell go list ./... | grep -E '/internal' | grep -Ev '/internal/provider/openconfig|internal/provider/cisco/nxos/genyang|internal/provider/cisco/nxos/isis')
192192
# to get around weird Makefile syntax restrictions, we need variables containing nothing, a space and comma
193193
null :=
194194
space := $(null) $(null)

Makefile.maker.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ controllerGen:
1616

1717
coverageTest:
1818
only: "/internal"
19-
except: "/internal/provider/openconfig|internal/provider/cisco/nxos/genyang"
19+
except: "/internal/provider/openconfig|internal/provider/cisco/nxos/genyang|internal/provider/cisco/nxos/isis"
2020

2121
dockerfile:
2222
enabled: false

Tiltfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ analytics_settings(False)
88
allow_k8s_contexts(['minikube', 'kind-network'])
99

1010
docker_build('controller:latest', '.', ignore=['*/*/zz_generated.deepcopy.go', 'config/crd/bases/*'], only=[
11-
'api/', 'cmd/', 'hack/', 'internal/', 'go.mod', 'go.sum', 'Makefile',
11+
'api/', 'cmd/', 'hack/', 'internal/', 'go.mod', 'go.sum', 'Makefile', 'vendor/', ".force-rebuild"
1212
])
1313

1414
local_resource('controller-gen', 'make generate', ignore=['*/*/zz_generated.deepcopy.go', 'config/crd/bases/*'], deps=[
@@ -25,6 +25,8 @@ k8s_resource(new_name='lo1', objects=['lo1:interface'], trigger_mode=TRIGGER_MOD
2525
k8s_resource(new_name='eth1-1', objects=['eth1-1:interface'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False)
2626
k8s_resource(new_name='eth1-2', objects=['eth1-2:interface'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False)
2727
k8s_resource(new_name='eth1-10', objects=['eth1-10:interface'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False)
28+
k8s_yaml('./config/samples/v1alpha1_igp.yaml')
29+
k8s_resource(new_name='underlay-isis-area-0', objects=['underlay-isis-area-0:igp'], trigger_mode=TRIGGER_MODE_MANUAL, auto_init=False)
2830

2931
print('🚀 network-operator development environment')
3032
print('👉 Edit the code inside the api/, cmd/, or internal/ directories')

api/v1alpha1/igp_types.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
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+
// +kubebuilder:validation:Enum=isis
11+
type IGPType string
12+
13+
// +kubebuilder:object:root=true
14+
// +kubebuilder:subresource:status
15+
// +kubebuilder:resource:path=igps
16+
// +kubebuilder:resource:singular=igp
17+
// +kubebuilder:resource:shortName=igp
18+
// +kubebuilder:printcolumn:name="Process name",type=string,JSONPath=`.spec.name`
19+
20+
// IGP is the Schema for the igp API.
21+
type IGP struct {
22+
metav1.TypeMeta `json:",inline"`
23+
metav1.ObjectMeta `json:"metadata,omitempty"`
24+
25+
// Specification of the desired state of the resource.
26+
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
27+
Spec IGPSpec `json:"spec"`
28+
29+
// Status of the resource. This is set and updated automatically.
30+
// Read-only.
31+
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
32+
Status IGPStatus `json:"status"`
33+
}
34+
35+
// IGProcessSpec defines the desired state of Interior Gateway Protocol (IGP) process on a device.
36+
type IGPSpec struct {
37+
// The name of the routing instance.
38+
//+kubebuilder:validation:Required
39+
Name string `json:"name"`
40+
//+kubebuilder:validation:Required
41+
ISIS *ISISSpec `json:"isis,omitempty"`
42+
}
43+
44+
// IGPStatus defines the observed state of an IGP process.
45+
type IGPStatus struct {
46+
// The conditions are a list of status objects that describe the state of the Interface.
47+
//+listType=map
48+
//+listMapKey=type
49+
//+patchStrategy=merge
50+
//+patchMergeKey=type
51+
//+optional
52+
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`
53+
}
54+
55+
// +kubebuilder:object:root=true
56+
57+
// IGPList contains a list of IGP (processes).
58+
type IGPList struct {
59+
metav1.TypeMeta `json:",inline"`
60+
metav1.ListMeta `json:"metadata,omitempty"`
61+
Items []IGP `json:"items"`
62+
}
63+
64+
// +kubebuilder:validation:Enum=Level1;Level2;Level12
65+
type ISISLevel string
66+
67+
const (
68+
Level1 ISISLevel = "Level1"
69+
Level2 ISISLevel = "Level2"
70+
Level12 ISISLevel = "Level12"
71+
)
72+
73+
// +kubebuilder:validation:Enum=v4-unicast;v6-unicast
74+
type ISISAF string
75+
76+
const (
77+
IPv4Unicast ISISAF = "v4-unicast"
78+
IPv6Unicast ISISAF = "v6-unicast"
79+
)
80+
81+
type ISISSpec struct {
82+
// The Network Entity Title (NET) for the ISIS instance.
83+
//+kubebuilder:validation:Required
84+
NET string `json:"net"`
85+
// The is-type of the process (the level)
86+
//+kubebuilder:validation:Required
87+
Level ISISLevel `json:"level"`
88+
// Overload bit configuration for this ISIS instance
89+
//+kubebuilder:validation:Optional
90+
OverloadBit *OverloadBit `json:"overloadBit"`
91+
//+kubebuilder:validation:Required
92+
//+kubebuilder:validation:MinItems=1
93+
//+kubebuilder:validation:MaxItems=2
94+
AddressFamilies []ISISAF `json:"addressFamilies,omitempty"`
95+
}
96+
97+
type OverloadBit struct {
98+
// Duration of the OverloadBit in seconds.
99+
//+kubebuilder:validation:Required
100+
//+kubebuilder:validation:Minimum=576
101+
//+kubebuilder:validation:Maximum=86400
102+
OnStartup uint32 `json:"onStartup"`
103+
}
104+
105+
func init() {
106+
SchemeBuilder.Register(&IGP{}, &IGPList{})
107+
}

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 141 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: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535
"sigs.k8s.io/controller-runtime/pkg/webhook"
3636

3737
// Import all supported provider implementations.
38+
_ "github.com/ironcore-dev/network-operator/internal/provider/cisco/nxos"
3839
_ "github.com/ironcore-dev/network-operator/internal/provider/openconfig"
3940

4041
"github.com/ironcore-dev/network-operator/api/v1alpha1"
@@ -79,8 +80,8 @@ func main() {
7980
flag.StringVar(&metricsCertName, "metrics-cert-name", "tls.crt", "The name of the metrics server certificate file.")
8081
flag.StringVar(&metricsCertKey, "metrics-cert-key", "tls.key", "The name of the metrics server key file.")
8182
flag.BoolVar(&enableHTTP2, "enable-http2", false, "If set, HTTP/2 will be enabled for the metrics and webhook servers")
82-
flag.StringVar(&watchFilterValue, "watch-filter", "", fmt.Sprintf("Label value that the controller watches to reconcile api objects. Label key is always %q. If unspecified, the controller watches for all api objects.", v1alpha1.WatchLabel))
83-
flag.StringVar(&providerName, "provider", "openconfig", "The provider to use for the controller. If not specified, the default provider is used. Available providers: "+strings.Join(provider.Providers(), ", "))
83+
flag.StringVar(&watchFilterValue, "watch-filter", "", fmt.Sprintf("Label value that the controllers watch to reconcile api objects. Label key is always %q. If unspecified, the controller watches for all api objects.", v1alpha1.WatchLabel))
84+
flag.StringVar(&providerName, "provider", "openconfig", "The provider to use for the controllers. If not specified, the default provider is used. Available providers: "+strings.Join(provider.Providers(), ", "))
8485
opts := zap.Options{
8586
Development: true,
8687
TimeEncoder: zapcore.ISO8601TimeEncoder,
@@ -220,6 +221,23 @@ func main() {
220221
os.Exit(1)
221222
}
222223

224+
prov, err = provider.Get("cisco-nxos-gnmi")
225+
if err != nil {
226+
setupLog.Error(err, "failed to get provider", "provider", providerName)
227+
os.Exit(1)
228+
}
229+
230+
if err = (&controller.IGPReconciler{
231+
Client: mgr.GetClient(),
232+
Scheme: mgr.GetScheme(),
233+
Recorder: mgr.GetEventRecorderFor("igp-controller"),
234+
WatchFilterValue: watchFilterValue,
235+
Provider: prov,
236+
}).SetupWithManager(mgr); err != nil {
237+
setupLog.Error(err, "unable to create controller", "controller", "IGP")
238+
os.Exit(1)
239+
}
240+
223241
// +kubebuilder:scaffold:builder
224242

225243
if metricsCertWatcher != nil {

0 commit comments

Comments
 (0)