Skip to content

feat(openconfig): Implement ManagementAccess provider - #485

Open
rgildein wants to merge 2 commits into
mainfrom
feat/openconfig-managementaccess
Open

feat(openconfig): Implement ManagementAccess provider#485
rgildein wants to merge 2 commits into
mainfrom
feat/openconfig-managementaccess

Conversation

@rgildein

@rgildein rgildein commented Aug 6, 2026

Copy link
Copy Markdown

Description

Add EnsureManagementAccess and DeleteManagementAccess to the OpenConfig provider using openconfig-system YANG paths:

  • gRPC server: /system/grpc-servers/grpc-server[name=gnmi]/config
  • SSH server: /system/ssh-server/config

Unsupported fields (spec.grpc.gnmi, spec.ssh.sessionLimit) are rejected with a terminal UnsupportedFieldError, following the DNS provider pattern.

My prompt
/plan Add Managementaccess to OpenConfig provider.
# context
The ManagementAccess CRD manages gRPC/gNMI and SSH server access on network devices. The NX-OS provider already have it, but OpenConfig not.
Use PR https://github.com/ironcore-dev/network-operator/pull/471 as inspiration.

# Goal
Add EnsureManagementAccess and DeleteManagementAccess to the OpenConfig provider using the standard OpenConfig YANG paths from:
 - openconfig-system-grpc (for gRPC server config)
 - openconfig-system (for SSH server config)

# Steps
1. check what parameters NetworkOperator consume for Managementaccess e.g. `ssh: timeout: 120s; sessionLimit: 10`
2. check OpenConfig paths https://openconfig.net/projects/models/schemadocs/yangdoc/openconfig-system.html#system-grpc-servers and https://openconfig.net/projects/models/schemadocs/yangdoc/openconfig-system.html#system-ssh-server
3. create EnsureManagementAccess and DeleteManagementAccess method
4. build and deploy Network-Operator
5. Use example configuretion @config/samples/v1alpha1_managementaccess.yaml to create new custom resource kind ManagementAccess
6. validate with gnmic that device was properly configured
7. @test/gnmi/testdata/openconfig/managementaccess.txt with config and expected output from gnmic
Claude plan

Plan: Add ManagementAccess to OpenConfig Provider

Context

The ManagementAccess CRD manages gRPC/gNMI and SSH server access on network devices. The NX-OS provider already implements EnsureManagementAccess and DeleteManagementAccess, but the OpenConfig provider does not. The controller already checks if a provider implements ManagementAccessProvider (defined in internal/provider/provider.go) and sets a NotImplementedReason status when it doesn't. This task wires up the OpenConfig provider to configure devices using standard OpenConfig YANG paths from openconfig-system-grpc and openconfig-system.


OpenConfig YANG Paths

gRPC server (named list, key: name):

openconfig-system:system/grpc-servers/grpc-server[name=gnmi]/config/enable           bool
openconfig-system:system/grpc-servers/grpc-server[name=gnmi]/config/port             uint16
openconfig-system:system/grpc-servers/grpc-server[name=gnmi]/config/certificate-id   string
openconfig-system:system/grpc-servers/grpc-server[name=gnmi]/config/network-instance string (VRF)

SSH server (singleton container):

openconfig-system:system/ssh-server/config/enable        bool
openconfig-system:system/ssh-server/config/timeout       uint32 (seconds)
openconfig-system:system/ssh-server/config/session-limit uint16

Implementation

New file: internal/provider/openconfig/managementaccess.go

Compile-time assertion:

var _ provider.ManagementAccessProvider = (*Provider)(nil)

Two structs implementing gnmiext.DataElement:

  1. GRPCServer — targets the grpc-server list item named "gnmi":

    • XPath: openconfig-system:system/grpc-servers/grpc-server[name=gnmi]/config
    • JSON fields: enable, port, certificate-id (omitempty), network-instance (omitempty)
    • Populated from spec.grpc.*
  2. SSHServer — targets the SSH server config container:

    • XPath: openconfig-system:system/ssh-server/config
    • JSON fields: enable, timeout (seconds, uint32), session-limit (uint16)
    • Populated from spec.ssh.*

Duration conversion: spec.grpc.gnmi.keepAliveTimeout and spec.ssh.timeout are metav1.Duration. Convert to seconds with .Duration.Seconds()uint32.

EnsureManagementAccess:

func (p *Provider) EnsureManagementAccess(ctx context.Context, req *provider.EnsureManagementAccessRequest) error {
    ma := req.ManagementAccess
    grpcServer := &GRPCServer{...from ma.Spec.GRPC...}
    sshServer  := &SSHServer{...from ma.Spec.SSH...}
    return p.client.Update(ctx, grpcServer, sshServer)
}

DeleteManagementAccess:

func (p *Provider) DeleteManagementAccess(ctx context.Context) error {
    return p.client.Delete(ctx, &GRPCServer{Name: grpcServerName}, &SSHServer{})
}

Test data file

New file: test/gnmi/testdata/openconfig/managementaccess.txt

Format mirrors banner.txt — Kubernetes CR in the first block, expected JSON state in the second block:

# ManagementAccess
-- managementaccesses/managementaccess --
apiVersion: networking.metal.ironcore.dev/v1alpha1
kind: ManagementAccess
metadata:
  name: managementaccess
  namespace: default
spec:
  deviceRef:
    name: device
  grpc:
    enabled: true
    port: 9339
    certificateId: mytrustpoint
    vrfName: mgmt
    gnmi:
      maxConcurrentCall: 16
      keepAliveTimeout: 600s
  ssh:
    enabled: true
    timeout: 120s
    sessionLimit: 10
-- state --
{
  "openconfig-system:system": {
    "grpc-servers": {
      "grpc-server": [
        {
          "name": "gnmi",
          "config": {
            "name": "gnmi",
            "enable": true,
            "port": 9339,
            "certificate-id": "mytrustpoint",
            "network-instance": "mgmt"
          }
        }
      ]
    },
    "ssh-server": {
      "config": {
        "enable": true,
        "timeout": 120
      }
    }
  }
}

Critical files

File Change
internal/provider/openconfig/managementaccess.go NewEnsureManagementAccess, DeleteManagementAccess, GRPCServer, SSHServer structs
test/gnmi/testdata/openconfig/managementaccess.txt New — test fixture

Reference implementations to follow:

  • internal/provider/openconfig/banner.go — pattern for DataElement structs and EnsureX/DeleteX methods
  • internal/provider/cisco/nxos/provider.goEnsureManagementAccess logic (field mapping)
  • internal/provider/provider.go:388ManagementAccessProvider interface definition

Verification

  1. Build & deploy via /netop-setup then /netop-test
  2. Apply CR config/samples/v1alpha1_managementaccess.yaml against an OpenConfig device
  3. Validate with gnmic:
    gnmic -a <device> get --path /system/grpc-servers/grpc-server[name=gnmi]/config
    gnmic -a <device> get --path /system/ssh-server/config
    
  4. gnmi test server — the test/gnmi/testdata/openconfig/managementaccess.txt fixture is exercised by the existing test runner in test/gnmi/main.go
  5. Confirm ManagementAccess CR reaches Ready=True condition
Claude Test results
---
    Test Report — ManagementAccess / OpenConfig Provider

    ┌──────────────────┬──────────────────┬───────────┬───────┬──────────────────────────────────────────────────────────────┬─────────────────┐
    │    CR Name       │      Kind        │ Namespace │ Ready │                         gNMI Path                           │     Result      │
    ├──────────────────┼──────────────────┼───────────┼───────┼──────────────────────────────────────────────────────────────┼─────────────────┤
    │ managementaccess │ ManagementAccess │ default   │ True  │ openconfig-system:system/grpc-servers/grpc-server[name=gnmi] │ ✓ value matches │
    │                  │                  │           │       │ /config                                                      │                 │
    ├──────────────────┼──────────────────┼───────────┼───────┼──────────────────────────────────────────────────────────────┼─────────────────┤
    │ managementaccess │ ManagementAccess │ default   │ True  │ openconfig-system:system/ssh-server/config                   │ ✓ value matches │
    └──────────────────┴──────────────────┴───────────┴───────┴──────────────────────────────────────────────────────────────┴─────────────────┘

  ---
  Applied spec (kubectl get managementaccess managementaccess -n default -o yaml):
  spec:
    deviceRef:
      name: leaf1
    grpc:
      certificateId: mytrustpoint
      enabled: true
      gnmi:
        keepAliveTimeout: 10m0s   # default — ignored by OpenConfig provider
        maxConcurrentCall: 8      # default — ignored by OpenConfig provider
      port: 9339
      vrfName: mgmt
    ssh:
      enabled: true
      sessionLimit: 32            # default — ignored by OpenConfig provider
      timeout: 120s

  ---
  gnmic get openconfig-system:system/grpc-servers/grpc-server[name=gnmi]/config
  ──────────────────────────────────────────────────────────────────────
  {
    "certificate-id": "mytrustpoint",
    "enable": true,
    "name": "gnmi",
    "network-instance": "mgmt",
    "port": 9339
  }

  gnmic get openconfig-system:system/ssh-server/config
  ──────────────────────────────────────────────────────────────────────
  {
    "enable": true,
    "timeout": 120
  }

  ---
  Operator logs — no errors on current CR. Previous session errors (now resolved):
  - leafref destination '.system.grpc-servers.grpc-server{.name=="gnmi"}.config.name' is not set — caused by wrong XPath format [gnmi] instead of [name=gnmi], fixed
  - schema '/system/ssh-server/config' has no local leaf 'session-limit' — field not in Nokia SRL model, dropped from SSHServer struct
  - leafref destination '.network-instance{.name=="default"}' does not exist — device only has mgmt network instance; sample updated to use vrfName: mgmt
  - UnsupportedField: spec.grpc.gnmi / spec.ssh.sessionLimit — validation correctly fires when user sets non-default values for unsupported fields

Manual test result

$ gnmic -a 172.20.20.2 --port 57400 -u admin -p 'NokiaSrl1!' --skip-verify --encoding JSON_IETF get --path openconfig-system:system/grpc-servers/grpc-server[name=gnmi]/config
[
  {
    "source": "172.20.20.2",
    "timestamp": 1786025519992248167,
    "time": "2026-08-06T16:11:59.992248167+02:00",
    "updates": [
      {
        "Path": "openconfig-system:system/grpc-servers/grpc-server[name=gnmi]/config",
        "values": {
          "openconfig-system:system/grpc-servers/grpc-server/config": {
            "certificate-id": "mytrustpoint",
            "enable": true,
            "name": "gnmi",
            "network-instance": "mgmt",
            "port": 9339
          }
        }
      }
    ]
  }
]
$ gnmic -a 172.20.20.2 --port 57400 -u admin -p 'NokiaSrl1!' --skip-verify --encoding JSON_IETF get --path openconfig-system:system/ssh-server/config
[
  {
    "source": "172.20.20.2",
    "timestamp": 1786025543195091526,
    "time": "2026-08-06T16:12:23.195091526+02:00",
    "updates": [
      {
        "Path": "openconfig-system:system/ssh-server/config",
        "values": {
          "openconfig-system:system/ssh-server/config": {
            "enable": true,
            "timeout": 120
          }
        }
      }
    ]
  }
]

Add EnsureManagementAccess and DeleteManagementAccess to the OpenConfig
provider using openconfig-system YANG paths:
- gRPC server: /system/grpc-servers/grpc-server[name=gnmi]/config
- SSH server:  /system/ssh-server/config

Unsupported fields (spec.grpc.gnmi, spec.ssh.sessionLimit) are rejected
with a terminal UnsupportedFieldError, following the DNS provider pattern.

Co-authored-by: Claude <noreply@anthropic.com>
Signed-off-by: Robert Gildein <rgildein@users.noreply.github.com>
@rgildein rgildein self-assigned this Aug 6, 2026
@github-actions github-actions Bot added the size/L label Aug 6, 2026
Timeout uint32 `json:"timeout,omitempty"`
}

func (s *SSHServer) XPath() string {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
func (s *SSHServer) XPath() string {
func (*SSHServer) XPath() string {

in such cases, we can omit the receiver variable name

Signed-off-by: Robert Gildein <rgildein@users.noreply.github.com>
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

Merging this branch will not change overall coverage

Impacted Packages Coverage Δ 🤖
github.com/ironcore-dev/network-operator/internal/provider/openconfig 0.00% (ø)

Coverage by file

Changed files (no unit tests)

Changed File Coverage Δ Total Covered Missed 🤖
github.com/ironcore-dev/network-operator/internal/provider/openconfig/managementaccess.go 0.00% (ø) 18 (+18) 0 18 (+18)

Please note that the "Total", "Covered", and "Missed" counts above refer to code statements instead of lines of code. The value in brackets refers to the test coverage of that file in the old version of the code.

@rgildein
rgildein marked this pull request as ready for review August 7, 2026 07:15
@rgildein
rgildein requested a review from felix-kaestner August 7, 2026 07:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants