diff --git a/.claude/skills/provider-matrix/SKILL.md b/.claude/skills/provider-matrix/SKILL.md new file mode 100644 index 000000000..4b76ac66c --- /dev/null +++ b/.claude/skills/provider-matrix/SKILL.md @@ -0,0 +1,286 @@ +--- +name: provider-matrix +description: Generate or update the provider compatibility matrix at docs/provider-compatibility.md. Reads API types and provider source to determine what each provider supports, what is partially supported (with unsupported fields), and what is platform-exclusive. +allowed-tools: [Read, Bash, Edit, Write] +--- + +# provider-matrix + +Generate the provider compatibility matrix at `docs/provider-compatibility.md`. + +The matrix shows which Kubernetes API types (Kinds) each network device provider supports, +with partial support annotations and platform-exclusive kinds clearly marked. + +> **Important:** Always read the current `docs/provider-compatibility.md` first before making +> any changes. After generating the new content, present it to the user for review. +> **Do not write to the file without explicit user approval.** + +--- + +## Step 1 — Discover all API Kinds + +Scan all `*_types.go` files for types marked `+kubebuilder:object:root=true`. +Exclude `*List` types. Organise by package: + +**Core kinds** — `api/core/v1alpha1/` +These are provider-neutral and map 1:1 to provider interfaces in `internal/provider/provider.go`. +Each core kind has a corresponding `*Provider` interface (e.g. `DNS` → `DNSProvider`). + +**Platform-exclusive kinds** — `api/cisco/nx/v1alpha1/` (and any future `api//*/`) +These are vendor-specific CRDs with no core provider interface. +Scan the directory for kinds and split them into two groups: +- **Config extensions**: have a `Register*Dependency` call in their `init()` — belong in the per-provider "Provider-specific types" table, not the main matrix. +- **Standalone**: no `Register*Dependency` call — appear as rows in the main matrix with N/A for all other providers. + +**Non-provider API packages** — `api/evpn/`, `api/pool/`, `api/cisco/xr/`, `api/cisco/xe/` +These are infrastructure/orchestration types, not provider capability CRDs. +Do **not** include them in the matrix. + +--- + +## Step 2 — Discover provider implementations + +There are three registered providers. For each, check which core `*Provider` interfaces +it implements by looking for compile-time assertions or method presence. + +### Cisco NX-OS +- Source: `internal/provider/cisco/nxos/` +- API: `api/cisco/nx/v1alpha1/` +- **Note:** All interface implementations live in a single large `provider.go` file. To find `NewUnsupportedFieldError` calls for a given interface, identify the method name from the interface definition and search for it in `provider.go`. Helper functions called from that method may also contain violations — read the called functions too. + +### Cisco IOS-XR +- Source: `internal/provider/cisco/iosxr/` +- API: `api/cisco/xr/v1alpha1/` (may be empty — check for any CRDs) +- Read compile-time assertions in `provider.go` to discover implemented interfaces. + +### Nokia SRL (OpenConfig) +- Source: `internal/provider/openconfig/` +- API: none (no OpenConfig-specific CRDs) +- **Note:** Each interface is implemented in its own dedicated file (e.g. `dns.go` → `DNSProvider`). The `var _ provider.XxxProvider` assertion at the top of each file tells you which interface it implements. Scan all files in the directory to collect the full list. + +--- + +## Step 3 — Detect partial support (unsupported fields) + +For each provider+interface combination that is implemented, check whether any +fields are reported as unsupported at runtime. + +### How to find unsupported fields + +Search for `apistatus.NewUnsupportedFieldError` calls in the provider source. +Each call contains one or more `apistatus.FieldViolation{Field: "...", Description: "..."}` literals. +Always read **both** `Field` and `Description`. Use the description to understand what is actually +unsupported — sometimes only specific values of a field are unsupported (e.g. a specific enum value), +not the field itself. In that case, the footnote should mention the specific constraint from the +description, not just the field path. + +**Pattern A — inline:** +```go +return apistatus.NewUnsupportedFieldError(apistatus.FieldViolation{ + Field: "spec.type", + Description: "unsupported interface type: ...", +}) +``` + +**Pattern B — accumulated slice:** +```go +var violations []apistatus.FieldViolation +if condition { + violations = append(violations, apistatus.FieldViolation{ + Field: "spec.adminState", + Description: "adminState Down is not supported", + }) +} +if len(violations) > 0 { + return apistatus.NewUnsupportedFieldError(violations...) +} +``` + +**Pattern C — helper function:** +The `FieldViolation` literals may be in a helper function called from the interface method +(e.g. `validateDNSSpec` called from `EnsureDNS`). Read the called function too. + +**Important:** Only `NewUnsupportedFieldError` indicates a missing feature. +`NewInvalidArgumentError` indicates a bad value for a supported field — do NOT include these. +Common traps that are always `NewInvalidArgumentError`, not unsupported: +- Length or line limits +- Format constraints (e.g. a field must be a specific type or pattern) +- Invalid combinations of supported fields +If the provider's `Ensure*` method reads and uses the field, the field is supported. + +Additionally, do NOT include a field if only some values of it are unsupported — whether that's +a `default:` case for unknown enum values, or a named case for a specific known value. +**Only include a field if the field itself is entirely unsupported** (the provider never reads +or acts on it regardless of its value). + +For `fmt.Sprintf` field values like `fmt.Sprintf("spec.servers[%s].vrfName", addr)`, +normalise the dynamic part to `*`: → `spec.servers[*].vrfName`. + +**Attribution:** To map a `FieldViolation` to an interface, find which interface method +contains (or calls into) the function with the violation. For NX-OS (single `provider.go`), +match by method name (e.g. `EnsureInterface` → `InterfaceProvider`). For OpenConfig, +each file is one interface. + +### How to find ignored fields + +Some fields are silently ignored (not configured on the device) rather than rejected. +These appear as code comments like: +```go +// PrependLocalAS / PrependGlobalAS not supported via OC local-as leaf +// spec.grpc.serverName is not configurable on this platform +``` +Search for `// not supported`, `// unsupported`, `// ignored` comments near field access code. +These should also be listed as partial support notes. + +--- + +## Step 4 — Write the matrix + +Work through Steps 1–3 by reading the source files directly, then write `docs/provider-compatibility.md` +using the output format below. + +**Commands to gather data:** + +```bash +# Core kinds +grep -rn "kubebuilder:object:root=true" api/core/v1alpha1/ | grep -v List + +# NX-OS standalone kinds (no Register*Dependency) +grep -rn "kubebuilder:object:root=true" api/cisco/nx/v1alpha1/ | grep -v List + +# NX-OS config extensions (have Register*Dependency) +grep -rn "Register.*Dependency" api/cisco/nx/v1alpha1/ + +# IOS-XR interface assertions +grep "_ provider\." internal/provider/cisco/iosxr/provider.go + +# OpenConfig interface assertions +grep -rn "var _ provider\." internal/provider/openconfig/ | grep -v _test + +# All NewUnsupportedFieldError field+description values — OpenConfig +grep -A5 "FieldViolation{" internal/provider/openconfig/*.go | grep -E "Field:|Description:" + +# All NewUnsupportedFieldError field+description values — NX-OS +grep -A5 "FieldViolation{" internal/provider/cisco/nxos/provider.go | grep -E "Field:|Description:" +``` + +After collecting the data, write the full markdown directly to `docs/provider-compatibility.md`. +The file header should say ``. + +--- + +## Output format + +### File header + +```markdown + + +# Provider Compatibility Matrix + +This document provides a detailed overview of which API types are supported by each network device provider. + + +``` + +### Compatibility Matrix table + +Columns: `Core Kind` + one column per provider (in registration order). +Rows: all core kinds + platform-exclusive standalone kinds, sorted alphabetically by Kind name. + +```markdown +## Compatibility Matrix + +| Core Kind | Cisco NX-OS | Cisco IOS-XR | Nokia SRL (OpenConfig) | +|-----------|--------|--------|--------| +| `AAA` | ✅ | — | ⚠️ [^1] | +| `AccessControlList` | ✅ | — | ✅ | +| `BorderGateway` | ✅ | N/A | N/A | +| `DNS` | ✅ | — | ⚠️ [^2] | +... +``` + +Cell values: +- `✅` — fully supported (implements the interface, no unsupported fields) +- `⚠️ [^N]` — partially supported (implements the interface but has unsupported fields; footnote lists them) +- `—` — not supported (interface not implemented) +- `N/A` — not applicable (platform-exclusive kind, other providers cannot support it by design) + +Legend (always include): +```markdown +**Legend:** +- ✅ Supported +- ⚠️ Partial support (see footnotes) +- — Not supported +- N/A Not applicable (platform-exclusive feature) +``` + +### Footnotes + +Immediately after the legend, list footnotes in order 1..N (matching their appearance in the table). +Use the description to phrase each entry accurately: +- If the whole field is unsupported: `unsupported fields: \`field\`` + +```markdown +[^1]: **Nokia SRL (OpenConfig) — AAA**: unsupported fields: `spec.authorization`, `spec.serverGroups[].type` +``` + +### Per-provider sections + +One `##` section per provider, in registration order. Each section contains: +- The provider's display name as heading +- A one-line description of the provider +- `**Core API types:** X / Y` where X is the count of implemented core interfaces and Y is the total. Append `+ Z platform-exclusive` if the provider has standalone kinds. +- A `**Provider-specific types:**` table (only if the provider has config extensions or standalone kinds), with columns `Kind` and `Category`. Config extensions show `Extends core type \`\``. Standalone kinds show `Provider-exclusive`. + +Example structure: +```markdown +## + + + +**Core API types:** X / Y (+ Z platform-exclusive if applicable) + +**Provider-specific types:** (omit section if none) + +| Kind | Category | +|------|----------| +| `` | Extends core type `` | +| `` | Provider-exclusive | +``` + +### Contributing section + +```markdown +## Contributing + +To add support for a new API type or provider: + +1. Define the provider interface in `internal/provider/` +2. Implement the interface methods in your provider package +3. Use the `/provider-matrix` skill to regenerate this document +4. Submit a pull request with your changes + +The matrix is automatically generated by checking which provider interfaces +each provider implements, ensuring accuracy and eliminating manual maintenance. +``` + +--- + +## Verification checklist + +After generating, verify: +- [ ] Every `+kubebuilder:object:root=true` non-List kind from `api/core/v1alpha1/` appears as a row (excluding `Device` which is internal) +- [ ] Every standalone kind from each vendor API dir (no `Register*Dependency`) appears as a row with N/A for all providers that don't own it +- [ ] Each provider's core count matches the number of capability interfaces it implements (excluding base interfaces: `Provider`, `ProvisioningProvider`, `DeviceProvider`) +- [ ] Each provider's platform-exclusive count matches the number of standalone kinds in its API dir +- [ ] Footnote numbers are sequential 1..N in left-to-right, top-to-bottom table order +- [ ] Every footnoted field came from `NewUnsupportedFieldError` — not `NewInvalidArgumentError` + +## Final step — Human review + +Present the generated content to the user and wait for approval before writing to `docs/provider-compatibility.md`. +Do not write the file automatically. diff --git a/Makefile b/Makefile index d1d223278..8686a38f8 100644 --- a/Makefile +++ b/Makefile @@ -313,6 +313,14 @@ crd-ref-docs: $(CRD_REF_DOCS) ## Download crd-ref-docs locally if necessary. $(CRD_REF_DOCS): $(LOCALBIN) $(call go-install-tool,$(CRD_REF_DOCS),github.com/elastic/crd-ref-docs,$(CRD_REF_DOCS_VERSION)) +license-headers: FORCE install-addlicense + @printf "\e[1;36m>> addlicense (for license headers on source code files)\e[0m\n" + @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: +'"'"' {}; ' + @printf "\e[1;36m>> reuse annotate (for license headers on other files)\e[0m\n" + @reuse lint -j | jq -r '.non_compliant.missing_licensing_info[]' | sed '/\/d' | $(XARGS) reuse annotate -c 'SAP SE or an SAP affiliate company and IronCore contributors' -l Apache-2.0 --skip-unrecognised + @printf "\e[1;36m>> reuse download --all\e[0m\n" + @reuse download --all + @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" .PHONY: golangci-lint golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary. $(GOLANGCI_LINT): $(LOCALBIN) diff --git a/README.md b/README.md index 7f360df88..9ad6f1dc1 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,10 @@ Users can just run kubectl apply -f to install the project kubectl apply -f https://raw.githubusercontent.com//network-operator//dist/install.yaml ``` +## Supported Providers + +For a detailed compatibility matrix showing which API types are supported by each provider, see [Provider Compatibility Matrix](docs/provider-compatibility.md). + ## Support, Feedback, Contributing 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). diff --git a/docs/provider-compatibility.md b/docs/provider-compatibility.md new file mode 100644 index 000000000..cd097a16d --- /dev/null +++ b/docs/provider-compatibility.md @@ -0,0 +1,106 @@ + + +# Provider Compatibility Matrix + +This document provides a detailed overview of which API types are supported by each network device provider. + + + +## Compatibility Matrix + +| Core Kind | Cisco NX-OS | Cisco IOS-XR | Nokia SRL (OpenConfig) | +|-----------|--------|--------|--------| +| `AAA` | ✅ | — | ⚠️ [^1] | +| `AccessControlList` | ✅ | — | ✅ | +| `BGP` | ✅ | ✅ | ⚠️ [^2] | +| `BGPPeer` | ✅ | ✅ | ⚠️ [^3] | +| `Banner` | ✅ | — | ✅ | +| `BorderGateway` | ✅ | N/A | N/A | +| `Certificate` | ✅ | — | — | +| `ConfigBackup` | ✅ | — | — | +| `DHCPRelay` | ✅ | — | — | +| `DNS` | ✅ | — | ⚠️ [^4] | +| `EVPNInstance` | ✅ | — | — | +| `EthernetSegment` | ✅ | — | — | +| `ISIS` | ✅ | — | ✅ | +| `Interface` | ✅ | ✅ | ⚠️ [^5] | +| `LLDP` | ✅ | — | ✅ | +| `Maintenance` | ✅ | ✅ | — | +| `ManagementAccess` | ⚠️ [^6] | — | ⚠️ [^7] | +| `NTP` | ✅ | — | — | +| `NetworkVirtualizationEdge` | ✅ | — | — | +| `OSPF` | ✅ | — | — | +| `PIM` | ✅ | — | — | +| `PrefixSet` | ✅ | ✅ | ✅ | +| `RoutingPolicy` | ✅ | — | ⚠️ [^8] | +| `SNMP` | ✅ | — | — | +| `Syslog` | ✅ | — | ⚠️ [^9] | +| `System` | ✅ | N/A | N/A | +| `User` | ✅ | — | — | +| `VLAN` | ✅ | — | — | +| `VPCDomain` | ✅ | N/A | N/A | +| `VRF` | ✅ | ✅ | ⚠️ [^10] | + +**Legend:** +- ✅ Supported +- ⚠️ Partial support (see footnotes) +- — Not supported +- N/A Not applicable (platform-exclusive feature) + +[^1]: **Nokia SRL (OpenConfig) — AAA**: unsupported fields: `spec.authorization` +[^2]: **Nokia SRL (OpenConfig) — BGP**: unsupported fields: `spec.addressFamilies.l2vpnEvpn` +[^3]: **Nokia SRL (OpenConfig) — BGPPeer**: unsupported fields: `spec.addressFamilies.l2vpnEvpn` +[^4]: **Nokia SRL (OpenConfig) — DNS**: unsupported fields: `spec.adminState`, `spec.servers[*].vrfName`, `spec.sourceInterfaceName` +[^5]: **Nokia SRL (OpenConfig) — Interface**: unsupported fields: `spec.aggregation.multichassis`, `spec.bfd` +[^6]: **Cisco NX-OS — ManagementAccess**: unsupported fields: `spec.grpc.serverName` +[^7]: **Nokia SRL (OpenConfig) — ManagementAccess**: unsupported fields: `spec.grpc.gnmi`, `spec.ssh.sessionLimit` +[^8]: **Nokia SRL (OpenConfig) — RoutingPolicy**: unsupported fields: `spec.statements[].actions.bgpActions.setASPath`, `spec.statements[].actions.bgpActions.setCommunity`, `spec.statements[].actions.bgpActions.setExtCommunity` +[^9]: **Nokia SRL (OpenConfig) — Syslog**: unsupported fields: `spec.facilities`, `spec.servers[].vrfName` +[^10]: **Nokia SRL (OpenConfig) — VRF**: unsupported fields: `spec.routeDistinguisher`, `spec.routeTargets`, `spec.vni` + +## Cisco NX-OS + +Cisco NX-OS provides comprehensive support for network configuration through the network-operator. + +**Core API types:** 27 / 27 + 3 platform-exclusive + +**Provider-specific types:** + +| Kind | Category | +|------|----------| +| `AAAConfig` | Extends core type `AAA` | +| `BGPConfig` | Extends core type `BGP` | +| `InterfaceConfig` | Extends core type `Interface` | +| `LLDPConfig` | Extends core type `LLDP` | +| `ManagementAccessConfig` | Extends core type `ManagementAccess` | +| `NetworkVirtualizationEdgeConfig` | Extends core type `NetworkVirtualizationEdge` | +| `BorderGateway` | Provider-exclusive | +| `System` | Provider-exclusive | +| `VPCDomain` | Provider-exclusive | + +## Cisco IOS-XR + +Cisco IOS-XR support is currently in early development. + +**Core API types:** 6 / 27 + +## Nokia SRL (OpenConfig) + +OpenConfig provides a vendor-neutral configuration interface using standard OpenConfig YANG models, tested against Nokia SR Linux devices. + +**Core API types:** 14 / 27 + +## Contributing + +To add support for a new API type or provider: + +1. Define the provider interface in `internal/provider/` +2. Implement the interface methods in your provider package +3. Use the `/provider-matrix` skill to regenerate this document +4. Submit a pull request with your changes + +The matrix is automatically generated by checking which provider interfaces +each provider implements, ensuring accuracy and eliminating manual maintenance.