Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ dist/
clab-*
tests/robot/*-yang-models/
.idea/
docs/prd/
124 changes: 0 additions & 124 deletions docs/tree.mermaid

This file was deleted.

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ require (
github.com/sdcio/cache v0.0.38
github.com/sdcio/logger v0.0.3
github.com/sdcio/schema-server v0.0.34
github.com/sdcio/sdc-protos v0.0.54
github.com/sdcio/sdc-protos v0.0.55-0.20260601095759-67240812f373
github.com/sdcio/yang-parser v0.0.12
github.com/spf13/cobra v1.10.2
github.com/spf13/pflag v1.0.10
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,8 @@ github.com/sdcio/logger v0.0.3 h1:IFUbObObGry+S8lHGwOQKKRxJSuOphgRU/hxVhOdMOM=
github.com/sdcio/logger v0.0.3/go.mod h1:yWaOxK/G6vszjg8tKZiMqiEjlZouHsjFME4zSk+SAEA=
github.com/sdcio/schema-server v0.0.34 h1:NNDOkvtUMONtBA7cVvN96F+FWGD/Do6HNqfchy9B8eI=
github.com/sdcio/schema-server v0.0.34/go.mod h1:6t8HLXpqUqEJmE5yNZh29u/KZw0jlOICdNWns7zE4GE=
github.com/sdcio/sdc-protos v0.0.54 h1:1EbtU9ZbbJHFPOFGi5aW8Th79cuY9i+AJaP0ABVx8hw=
github.com/sdcio/sdc-protos v0.0.54/go.mod h1:YMLHbey0/aL1qtLW8csSYVPafsgnnn7aY54HkV5dbyQ=
github.com/sdcio/sdc-protos v0.0.55-0.20260601095759-67240812f373 h1:r/bNcNL7QSC1g4NVT3e3OptBAhvzeBLNk6WQjJ3jMS4=
github.com/sdcio/sdc-protos v0.0.55-0.20260601095759-67240812f373/go.mod h1:NsvzvHnTonLcwQ/WNzxzBCauQmqxpuviaW0wh7Lkrts=
github.com/sdcio/yang-parser v0.0.12 h1:RSSeqfAOIsJx5Lno5u4/ezyOmQYUduQ22rBfU/mtpJ4=
github.com/sdcio/yang-parser v0.0.12/go.mod h1:CBqn3Miq85qmFVGHxHXHLluXkaIOsTzV06IM4DW6+D4=
github.com/sirikothe/gotextfsm v1.0.1-0.20200816110946-6aa2cfd355e4 h1:FHUL2HofYJuslFOQdy/JjjP36zxqIpd/dcoiwLMIs7k=
Expand Down
8 changes: 4 additions & 4 deletions mocks/mocktarget/target.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions pkg/config/datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,21 @@ const (
ncCommitDatastoreCandidate = "candidate"
)

// DeviceProfile selects NOS-specific southbound behaviour. Wire values are YAML/JSON
// string scalars. [DeviceProfileNone] is the default (omitted or empty in config).
type DeviceProfile string

const (
// DeviceProfileNone selects generic southbound driver behaviour (no NOS-specific
// materialization). It is the zero value and serializes as omitted/empty in YAML/JSON.
DeviceProfileNone DeviceProfile = ""
// DeviceProfileCiscoIOSXR enables IOS-XR-specific gNMI materialization for
// type=gnmi (JSON / JSON_IETF granular path encoding; other encodings use the
// generic gNMI plan builder). For type=netconf the value is accepted for
// configuration consistency but currently has no profile-specific behaviour.
DeviceProfileCiscoIOSXR DeviceProfile = "cisco-ios-xr"
)

type DatastoreConfig struct {
Name string `yaml:"name,omitempty" json:"name,omitempty"`
Schema *SchemaConfig `yaml:"schema,omitempty" json:"schema,omitempty"`
Expand Down Expand Up @@ -86,6 +101,10 @@ type SBI struct {
ConnectRetry time.Duration `yaml:"connect-retry,omitempty" json:"connect-retry,omitempty"`
// Timeout
Timeout time.Duration `yaml:"timeout,omitempty" json:"timeout,omitempty"`
// DeviceProfile selects NOS-specific southbound behaviour. Use the
// [DeviceProfile] constants ([DeviceProfileNone] or [DeviceProfileCiscoIOSXR]);
// unknown values are rejected when the datastore configuration is validated.
DeviceProfile DeviceProfile `yaml:"device-profile,omitempty" json:"device-profile,omitempty"`
}

type SBIGnmiOptions struct {
Expand Down Expand Up @@ -164,7 +183,20 @@ func (ds *DatastoreConfig) ValidateSetDefaults() error {
return nil
}

// IsCiscoIOSXR reports whether this SBI uses the Cisco IOS-XR device profile.
// Callers outside pkg/config should use this predicate rather than comparing
// DeviceProfile directly, so the profile string stays contained here.
func (s *SBI) IsCiscoIOSXR() bool {
return s.DeviceProfile == DeviceProfileCiscoIOSXR
}

func (s *SBI) validateSetDefaults() error {
switch s.DeviceProfile {
case DeviceProfileNone, DeviceProfileCiscoIOSXR:
default:
return fmt.Errorf("unknown device-profile: %q", s.DeviceProfile)
}

switch s.Type {
case sbiNOOP:
return nil
Expand Down
79 changes: 79 additions & 0 deletions pkg/config/sbi_device_profile_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright 2024 Nokia
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

import "testing"

// validGNMISBI returns an SBI configured for gNMI with the given encoding and
// device-profile, with address/port filled in so validateSetDefaults passes all
// other checks.
func validGNMISBI(encoding string, deviceProfile DeviceProfile) *SBI {
return &SBI{
Type: sbiGNMI,
Address: "192.0.2.1",
Port: 57400,
GnmiOptions: &SBIGnmiOptions{Encoding: encoding},
DeviceProfile: deviceProfile,
}
}

func TestSBI_IsCiscoIOSXR(t *testing.T) {
if (&SBI{DeviceProfile: DeviceProfileCiscoIOSXR}).IsCiscoIOSXR() != true {
t.Fatal("expected true for cisco-ios-xr profile")
}
if (&SBI{DeviceProfile: DeviceProfileNone}).IsCiscoIOSXR() != false {
t.Fatal("expected false for DeviceProfileNone")
}
if (&SBI{}).IsCiscoIOSXR() != false {
t.Fatal("expected false for zero-value DeviceProfile (same as DeviceProfileNone)")
}
if (&SBI{DeviceProfile: DeviceProfile("other")}).IsCiscoIOSXR() != false {
t.Fatal("expected false for unrelated profile")
}
}

func TestSBI_validateSetDefaults_DeviceProfile_UnknownProfileIsRejected(t *testing.T) {
sbi := validGNMISBI("json_ietf", DeviceProfile("not-a-valid-profile"))
if err := sbi.validateSetDefaults(); err == nil {
t.Fatal("expected error for unknown device-profile, got nil")
}
}

func TestSBI_validateSetDefaults_DeviceProfile_CiscoIOSXRNetconfIsAccepted(t *testing.T) {
sbi := &SBI{
Type: sbiNETCONF,
Address: "192.0.2.1",
Port: 830,
NetconfOptions: &SBINetconfOptions{},
DeviceProfile: DeviceProfileCiscoIOSXR,
}
if err := sbi.validateSetDefaults(); err != nil {
t.Fatalf("unexpected error for cisco-ios-xr + netconf: %v", err)
}
}

func TestSBI_validateSetDefaults_DeviceProfile_CiscoIOSXRGNMIJSONIsAccepted(t *testing.T) {
sbi := validGNMISBI("json_ietf", DeviceProfileCiscoIOSXR)
if err := sbi.validateSetDefaults(); err != nil {
t.Fatalf("unexpected error for cisco-ios-xr + gnmi + json_ietf: %v", err)
}
}

func TestSBI_validateSetDefaults_DeviceProfile_CiscoIOSXRGNMIProtoIsAccepted(t *testing.T) {
sbi := validGNMISBI("proto", DeviceProfileCiscoIOSXR)
if err := sbi.validateSetDefaults(); err != nil {
t.Fatalf("unexpected error for cisco-ios-xr + gnmi + proto: %v", err)
}
}
16 changes: 8 additions & 8 deletions pkg/datastore/intent_rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ import (

"github.com/beevik/etree"

targettypes "github.com/sdcio/data-server/pkg/datastore/target/types"
"github.com/sdcio/data-server/pkg/datastore/target/materialize"
"github.com/sdcio/data-server/pkg/tree"
treeapi "github.com/sdcio/data-server/pkg/tree/api"
"github.com/sdcio/data-server/pkg/tree/api/adapter"
"github.com/sdcio/data-server/pkg/tree/consts"
"github.com/sdcio/data-server/pkg/tree/importer/proto"
Expand All @@ -34,22 +35,21 @@ import (

var ErrIntentNotFound = errors.New("intent not found")

func (d *Datastore) applyIntent(ctx context.Context, source targettypes.TargetSource) (*sdcpb.SetDataResponse, error) {
func (d *Datastore) applyIntent(ctx context.Context, entry treeapi.Entry, replace bool) (*sdcpb.SetDataResponse, error) {
log := logf.FromContext(ctx)
var err error

var rsp *sdcpb.SetDataResponse

// send set request only if there are updates and/or deletes
if containsChanges, _ := source.ContainsChanges(ctx); !containsChanges {
return &sdcpb.SetDataResponse{}, nil
}

if d.sbi == nil {
return nil, fmt.Errorf("%s is not connected", d.config.Name)
}

rsp, err = d.sbi.Set(ctx, source)
plan, err := materialize.BuildPlan(ctx, d.schemaClient, d.config.SBI, entry, replace)
if err != nil {
return nil, err
}
rsp, err = d.sbi.Set(ctx, plan)
if err != nil {
return nil, err
}
Expand Down
3 changes: 1 addition & 2 deletions pkg/datastore/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"sync"

"github.com/sdcio/data-server/pkg/tree"
"github.com/sdcio/data-server/pkg/tree/api/adapter"
"github.com/sdcio/data-server/pkg/tree/consts"
"github.com/sdcio/data-server/pkg/tree/importer"
"github.com/sdcio/data-server/pkg/tree/ops"
Expand Down Expand Up @@ -165,7 +164,7 @@ func (d *Datastore) performRevert(ctx context.Context, t *tree.RootEntry) error

if performApply {
log.Info("reverting after sync")
resp, err := d.applyIntent(ctx, adapter.NewEntryOutputAdapter(t.Entry))
resp, err := d.applyIntent(ctx, t.Entry, false)
if err != nil {
respJ := protojson.MarshalOptions{Multiline: false}
respStr, _ := respJ.Marshal(resp)
Expand Down
Loading
Loading