Skip to content

Commit 66addaa

Browse files
committed
Add provider compatibility matrix
Add a compatibility matrix showing which API types each provider supports. The matrix is generated automatically using Go reflection to check interface implementations, so it stays accurate without manual updates. The matrix lives in docs/provider-compatibility.md with a summary in the README. Run 'make provider-matrix' to regenerate it. The script checks which provider interfaces (InterfaceProvider, BGPProvider, etc.) each provider implements. Currently shows 22 API types for NXOS, 1 for IOS-XR, and 1 for OpenConfig.
1 parent 43348c0 commit 66addaa

9 files changed

Lines changed: 320 additions & 2 deletions

File tree

.github/workflows/checks.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ jobs:
3737
with:
3838
version: latest
3939
- name: Delete pre-installed shellcheck
40-
run: sudo rm -f $(which shellcheck)
40+
run: sudo rm -f "$(which shellcheck)"
4141
- name: Run shellcheck
4242
run: make run-shellcheck
4343
- name: Dependency Licenses Review

.golangci.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,20 @@ linters:
136136
excludes:
137137
# gosec wants us to set a short ReadHeaderTimeout to avoid Slowloris attacks, but doing so would expose us to Keep-Alive race conditions (see https://iximiuz.com/en/posts/reverse-proxy-http-keep-alive-and-502s/
138138
- G112
139+
# if we put a password or token into a serialized payload, guess what, we probably did that on purpose
140+
- G117
141+
# this triggers on net/http.Request.ParseForm() and its callers, e.g. net/http.Request.FormValue(), complaining about potential memory exhaustion from unbounded form parsing;
142+
# but that is incorrect, ParseForm() by default never parses more than 10 MiB for this specific reason
143+
- G120
139144
# created file permissions are restricted by umask if necessary
140145
- G306
146+
# the following lints cause false-positives in many repositories, should be fixed with the next release. (see https://github.com/securego/gosec/issues/1500)
147+
- G701
148+
- G702
149+
- G703
150+
- G704
151+
- G705
152+
- G706
141153
govet:
142154
disable:
143155
- fieldalignment

.license-scan-overrides.jsonl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
{"name": "github.com/mattn/go-localereader", "licenceType": "MIT"}
77
{"name": "github.com/miekg/dns", "licenceType": "BSD-3-Clause"}
88
{"name": "github.com/pashagolub/pgxmock/v4", "licenceType": "BSD-3-Clause"}
9+
{"name": "github.com/pashagolub/pgxmock/v5", "licenceType": "BSD-3-Clause"}
910
{"name": "github.com/spdx/tools-golang", "licenceTextOverrideFile": "vendor/github.com/spdx/tools-golang/LICENSE.code"}
1011
{"name": "github.com/xeipuuv/gojsonpointer", "licenceType": "Apache-2.0"}
1112
{"name": "github.com/xeipuuv/gojsonreference", "licenceType": "Apache-2.0"}

.typos.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# SPDX-FileCopyrightText: 2026 SAP SE
2+
#
23
# SPDX-License-Identifier: Apache-2.0
4+
35
[default]
46
extend-ignore-re = [
57
"Cisco-IOS-XR.*"

Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,11 @@ docs: install-crd-ref-docs
189189
-e 's/#xrcisconetworkingmetalironcoredevv1alpha1/#xr-cisco-networking-metal-ironcore-dev-v1alpha1/g' \
190190
docs/api-reference/index.md
191191

192+
# Generate provider implementation matrix
193+
provider-matrix: FORCE
194+
@printf "\e[1;36m>> go run ./hack/generate-provider-matrix.go > docs/provider-compatibility.md\e[0m\n"
195+
@go run ./hack/generate-provider-matrix.go > docs/provider-compatibility.md
196+
192197
install-goimports: FORCE
193198
@if ! hash goimports 2>/dev/null; then printf "\e[1;36m>> Installing goimports (this may take a while)...\e[0m\n"; go install golang.org/x/tools/cmd/goimports@latest; fi
194199

@@ -310,7 +315,7 @@ license-headers: FORCE install-addlicense
310315
@printf "\e[1;36m>> addlicense (for license headers on source code files)\e[0m\n"
311316
@printf "%s\0" $(patsubst $(shell awk '$$1 == "module" {print $$2}' go.mod)%,.%/*.go,$(shell go list ./...)) | $(XARGS) -0 -I{} bash -c 'year="$$(grep 'Copyright' {} | head -n1 | grep -E -o '"'"'[0-9]{4}(-[0-9]{4})?'"'"')"; if [[ -z "$$year" ]]; then year=$$(date +%Y); fi; gawk -i inplace '"'"'{if (display) {print} else {!/^\/\*/ && !/^\*/}}; {if (!display && $$0 ~ /^(package |$$)/) {display=1} else { }}'"'"' {}; addlicense -c "SAP SE or an SAP affiliate company and IronCore contributors" -s=only -y "$$year" -- {}; $(SED) -i '"'"'1s+// Copyright +// SPDX-FileCopyrightText: +'"'"' {}; '
312317
@printf "\e[1;36m>> reuse annotate (for license headers on other files)\e[0m\n"
313-
@reuse lint -j | jq -r '.non_compliant.missing_licensing_info[]' | grep -vw vendor | $(XARGS) reuse annotate -c 'SAP SE or an SAP affiliate company and IronCore contributors' -l Apache-2.0 --skip-unrecognised
318+
@reuse lint -j | jq -r '.non_compliant.missing_licensing_info[]' | sed '/\<vendor\>/d' | $(XARGS) reuse annotate -c 'SAP SE or an SAP affiliate company and IronCore contributors' -l Apache-2.0 --skip-unrecognised
314319
@printf "\e[1;36m>> reuse download --all\e[0m\n"
315320
@reuse download --all
316321
@printf "\e[1;35mPlease review the changes. If *.license files were generated, consider instructing go-makefile-maker to add overrides to REUSE.toml instead.\e[0m\n"

Makefile.maker.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,3 +232,8 @@ verbatim: |
232232
-e 's/#xecisconetworkingmetalironcoredevv1alpha1/#xe-cisco-networking-metal-ironcore-dev-v1alpha1/g' \
233233
-e 's/#xrcisconetworkingmetalironcoredevv1alpha1/#xr-cisco-networking-metal-ironcore-dev-v1alpha1/g' \
234234
docs/api-reference/index.md
235+
236+
# Generate provider implementation matrix
237+
provider-matrix: FORCE
238+
@printf "\e[1;36m>> go run ./hack/generate-provider-matrix.go > docs/provider-compatibility.md\e[0m\n"
239+
@go run ./hack/generate-provider-matrix.go > docs/provider-compatibility.md

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,16 @@ Users can just run kubectl apply -f <URL for YAML BUNDLE> to install the project
9898
kubectl apply -f https://raw.githubusercontent.com/<org>/network-operator/<tag or branch>/dist/install.yaml
9999
```
100100

101+
## Supported Providers
102+
103+
network-operator supports multiple network device providers with varying levels of feature support:
104+
105+
- **Cisco NX-OS** - Full support (23+ API types including BGP, OSPF, ISIS, EVPN, VLANs, and more)
106+
- **Cisco IOS-XR** - Limited support (Interface configuration)
107+
- **OpenConfig** - Limited support (Interface configuration via vendor-neutral OpenConfig YANG models)
108+
109+
For a detailed compatibility matrix showing which API types are supported by each provider, see [Provider Compatibility Matrix](docs/provider-compatibility.md).
110+
101111
## Support, Feedback, Contributing
102112

103113
This project is open to feature requests/suggestions, bug reports etc. via [GitHub issues](https://github.com/ironcore-dev/network-operator/issues). Contribution and feedback are encouraged and always welcome. For more information about how to contribute, the project structure, as well as additional contribution information, see our [Contribution Guidelines](CONTRIBUTING.md).

docs/provider-compatibility.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<!--
2+
SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and IronCore contributors
3+
SPDX-License-Identifier: Apache-2.0
4+
-->
5+
6+
# Provider Compatibility Matrix
7+
8+
This document provides a detailed overview of which API types are supported by each network device provider.
9+
10+
<!-- To regenerate this matrix, run: make provider-matrix -->
11+
12+
## Cisco NX-OS
13+
14+
Cisco NX-OS provides comprehensive support for network configuration through the network-operator.
15+
16+
**Status**: Full support (22 API types)
17+
18+
## Cisco IOS-XR
19+
20+
Cisco IOS-XR support is currently in early development.
21+
22+
**Status**: Limited support (1 API type)
23+
24+
## OpenConfig
25+
26+
OpenConfig provides a vendor-neutral configuration interface using standard OpenConfig YANG models.
27+
28+
**Status**: Limited support (1 API type)
29+
30+
## Detailed Compatibility Matrix
31+
32+
| API Type | NXOS | IOS-XR | OpenConfig |
33+
|----------|--------|--------|--------|
34+
| ACL | ✔️ |||
35+
| Banner | ✔️ |||
36+
| BGP | ✔️ |||
37+
| BGP Peer | ✔️ |||
38+
| Certificate | ✔️ |||
39+
| DNS | ✔️ |||
40+
| EVPN Instance | ✔️ |||
41+
| Interface | ✔️ | ✔️ | ✔️ |
42+
| ISIS | ✔️ |||
43+
| LLDP | ✔️ |||
44+
| Management Access | ✔️ |||
45+
| NTP | ✔️ |||
46+
| NVE | ✔️ |||
47+
| OSPF | ✔️ |||
48+
| PIM | ✔️ |||
49+
| Prefix Set | ✔️ |||
50+
| Routing Policy | ✔️ |||
51+
| SNMP | ✔️ |||
52+
| Syslog | ✔️ |||
53+
| User | ✔️ |||
54+
| VLAN | ✔️ |||
55+
| VRF | ✔️ |||
56+
57+
**Legend:**
58+
- ✔️ Implemented
59+
- — Not implemented
60+
61+
## Contributing
62+
63+
To add support for a new API type or provider:
64+
65+
1. Define the provider interface in `internal/provider/`
66+
2. Implement the interface methods in your provider package
67+
3. Run `make provider-matrix` to regenerate this document
68+
4. Submit a pull request with your changes
69+
70+
The matrix is automatically generated by checking which provider interfaces
71+
each provider implements, ensuring accuracy and eliminating manual maintenance.

hack/generate-provider-matrix.go

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
// SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and IronCore contributors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package main
5+
6+
import (
7+
"fmt"
8+
"reflect"
9+
"sort"
10+
"strings"
11+
12+
"github.com/ironcore-dev/network-operator/internal/provider"
13+
"github.com/ironcore-dev/network-operator/internal/provider/cisco/iosxr"
14+
"github.com/ironcore-dev/network-operator/internal/provider/cisco/nxos"
15+
"github.com/ironcore-dev/network-operator/internal/provider/openconfig"
16+
)
17+
18+
// providerInfo holds information about a provider
19+
type providerInfo struct {
20+
name string
21+
instance provider.Provider
22+
}
23+
24+
// interfaceMap maps API type names to their provider interface types.
25+
//
26+
// Note: This map must list interfaces explicitly because Go's reflection system
27+
// requires compile-time type references. While we could use AST parsing to discover
28+
// interface names, we still need this map to get reflect.Type values.
29+
//
30+
// When adding a new API type:
31+
// 1. Define the interface in internal/provider/ (e.g., type FooProvider interface)
32+
// 2. Add it to this map
33+
// 3. The script will automatically detect implementations
34+
//
35+
// This approach provides:
36+
// - Compile-time safety (won't compile if interface doesn't exist)
37+
// - Runtime accuracy (checks actual interface implementation)
38+
// - Minimal maintenance (only update when adding new API types, not implementations)
39+
var interfaceMap = map[string]reflect.Type{
40+
"acl": reflect.TypeOf((*provider.ACLProvider)(nil)).Elem(),
41+
"banner": reflect.TypeOf((*provider.BannerProvider)(nil)).Elem(),
42+
"bgp": reflect.TypeOf((*provider.BGPProvider)(nil)).Elem(),
43+
"bgp_peer": reflect.TypeOf((*provider.BGPPeerProvider)(nil)).Elem(),
44+
"certificate": reflect.TypeOf((*provider.CertificateProvider)(nil)).Elem(),
45+
"dns": reflect.TypeOf((*provider.DNSProvider)(nil)).Elem(),
46+
"evpninstance": reflect.TypeOf((*provider.EVPNInstanceProvider)(nil)).Elem(),
47+
"interface": reflect.TypeOf((*provider.InterfaceProvider)(nil)).Elem(),
48+
"isis": reflect.TypeOf((*provider.ISISProvider)(nil)).Elem(),
49+
"lldp": reflect.TypeOf((*provider.LLDPProvider)(nil)).Elem(),
50+
"managementaccess": reflect.TypeOf((*provider.ManagementAccessProvider)(nil)).Elem(),
51+
"ntp": reflect.TypeOf((*provider.NTPProvider)(nil)).Elem(),
52+
"nve": reflect.TypeOf((*provider.NVEProvider)(nil)).Elem(),
53+
"ospf": reflect.TypeOf((*provider.OSPFProvider)(nil)).Elem(),
54+
"pim": reflect.TypeOf((*provider.PIMProvider)(nil)).Elem(),
55+
"prefixset": reflect.TypeOf((*provider.PrefixSetProvider)(nil)).Elem(),
56+
"routingpolicy": reflect.TypeOf((*provider.RoutingPolicyProvider)(nil)).Elem(),
57+
"snmp": reflect.TypeOf((*provider.SNMPProvider)(nil)).Elem(),
58+
"syslog": reflect.TypeOf((*provider.SyslogProvider)(nil)).Elem(),
59+
"user": reflect.TypeOf((*provider.UserProvider)(nil)).Elem(),
60+
"vlan": reflect.TypeOf((*provider.VLANProvider)(nil)).Elem(),
61+
"vrf": reflect.TypeOf((*provider.VRFProvider)(nil)).Elem(),
62+
}
63+
64+
func main() {
65+
// Initialize providers
66+
providers := []providerInfo{
67+
{name: "NXOS", instance: nxos.NewProvider()},
68+
{name: "IOS-XR", instance: iosxr.NewProvider()},
69+
{name: "OpenConfig", instance: openconfig.NewProvider()},
70+
}
71+
72+
// Get sorted list of API types
73+
apiTypes := make([]string, 0, len(interfaceMap))
74+
for apiType := range interfaceMap {
75+
apiTypes = append(apiTypes, apiType)
76+
}
77+
sort.Strings(apiTypes)
78+
79+
// Check implementations for each provider
80+
implementations := make(map[string]map[string]bool)
81+
counts := make(map[string]int)
82+
83+
for _, prov := range providers {
84+
implementations[prov.name] = make(map[string]bool)
85+
providerType := reflect.TypeOf(prov.instance)
86+
87+
for _, apiType := range apiTypes {
88+
interfaceType := interfaceMap[apiType]
89+
implements := providerType.Implements(interfaceType)
90+
implementations[prov.name][apiType] = implements
91+
92+
if implements {
93+
counts[prov.name]++
94+
}
95+
}
96+
}
97+
98+
// Generate markdown documentation
99+
fmt.Println("<!--")
100+
fmt.Println("SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and IronCore contributors")
101+
fmt.Println("SPDX-License-Identifier: Apache-2.0")
102+
fmt.Println("-->")
103+
fmt.Println()
104+
fmt.Println("# Provider Compatibility Matrix")
105+
fmt.Println()
106+
fmt.Println("This document provides a detailed overview of which API types are supported by each network device provider.")
107+
fmt.Println()
108+
fmt.Println("<!-- To regenerate this matrix, run: make provider-matrix -->")
109+
fmt.Println()
110+
fmt.Println("## Cisco NX-OS")
111+
fmt.Println()
112+
fmt.Println("Cisco NX-OS provides comprehensive support for network configuration through the network-operator.")
113+
fmt.Println()
114+
fmt.Printf("**Status**: Full support (%d API types)\n", counts["NXOS"])
115+
fmt.Println()
116+
fmt.Println("## Cisco IOS-XR")
117+
fmt.Println()
118+
fmt.Println("Cisco IOS-XR support is currently in early development.")
119+
fmt.Println()
120+
fmt.Printf("**Status**: Limited support (%d API type)\n", counts["IOS-XR"])
121+
fmt.Println()
122+
fmt.Println("## OpenConfig")
123+
fmt.Println()
124+
fmt.Println("OpenConfig provides a vendor-neutral configuration interface using standard OpenConfig YANG models.")
125+
fmt.Println()
126+
fmt.Printf("**Status**: Limited support (%d API type)\n", counts["OpenConfig"])
127+
fmt.Println()
128+
fmt.Println("## Detailed Compatibility Matrix")
129+
fmt.Println()
130+
131+
// Build table header
132+
fmt.Print("| API Type |")
133+
for _, prov := range providers {
134+
fmt.Printf(" %s |", prov.name)
135+
}
136+
fmt.Println()
137+
138+
fmt.Print("|----------|")
139+
for range providers {
140+
fmt.Print("--------|")
141+
}
142+
fmt.Println()
143+
144+
// Build table rows
145+
for _, apiType := range apiTypes {
146+
displayName := formatDisplayName(apiType)
147+
fmt.Printf("| %s |", displayName)
148+
149+
for _, prov := range providers {
150+
if implementations[prov.name][apiType] {
151+
fmt.Print(" ✔️ |")
152+
} else {
153+
fmt.Print(" — |")
154+
}
155+
}
156+
fmt.Println()
157+
}
158+
159+
fmt.Println()
160+
fmt.Println("**Legend:**")
161+
fmt.Println("- ✔️ Implemented")
162+
fmt.Println("- — Not implemented")
163+
fmt.Println()
164+
fmt.Println("## Contributing")
165+
fmt.Println()
166+
fmt.Println("To add support for a new API type or provider:")
167+
fmt.Println()
168+
fmt.Println("1. Define the provider interface in `internal/provider/`")
169+
fmt.Println("2. Implement the interface methods in your provider package")
170+
fmt.Println("3. Run `make provider-matrix` to regenerate this document")
171+
fmt.Println("4. Submit a pull request with your changes")
172+
fmt.Println()
173+
fmt.Println("The matrix is automatically generated by checking which provider interfaces")
174+
fmt.Println("each provider implements, ensuring accuracy and eliminating manual maintenance.")
175+
}
176+
177+
// formatDisplayName converts snake_case to space-separated Title Case for display
178+
func formatDisplayName(typeName string) string {
179+
// Handle special cases for better display
180+
special := map[string]string{
181+
"acl": "ACL",
182+
"bgp": "BGP",
183+
"bgp_peer": "BGP Peer",
184+
"dns": "DNS",
185+
"evpninstance": "EVPN Instance",
186+
"isis": "ISIS",
187+
"lldp": "LLDP",
188+
"ntp": "NTP",
189+
"nve": "NVE",
190+
"ospf": "OSPF",
191+
"pim": "PIM",
192+
"snmp": "SNMP",
193+
"vlan": "VLAN",
194+
"vrf": "VRF",
195+
"prefixset": "Prefix Set",
196+
"routingpolicy": "Routing Policy",
197+
"managementaccess": "Management Access",
198+
}
199+
200+
if display, ok := special[typeName]; ok {
201+
return display
202+
}
203+
204+
// Default: convert to Title Case with spaces
205+
parts := strings.Split(typeName, "_")
206+
for i, part := range parts {
207+
if len(part) > 0 {
208+
parts[i] = strings.ToUpper(part[:1]) + part[1:]
209+
}
210+
}
211+
return strings.Join(parts, " ")
212+
}

0 commit comments

Comments
 (0)