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
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.51
github.com/sdcio/sdc-protos v0.0.52-0.20260420093658-100270c40f0c
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 @@ -193,8 +193,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.51 h1:sFc2ct8v4D7rBgFdg/fmXgJffcFhupfx4QJKsSAoolA=
github.com/sdcio/sdc-protos v0.0.51/go.mod h1:FkJMZWtp7Rcc/EedbX2mt1tET/j8KdavNl2BsHf03+o=
github.com/sdcio/sdc-protos v0.0.52-0.20260420093658-100270c40f0c h1:N6iwnjWWfv00tOC02XkpTH9G4AMBX83UxVe+Z0taGA4=
github.com/sdcio/sdc-protos v0.0.52-0.20260420093658-100270c40f0c/go.mod h1:FkJMZWtp7Rcc/EedbX2mt1tET/j8KdavNl2BsHf03+o=
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
1 change: 1 addition & 0 deletions pkg/datastore/intent_rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,5 @@ type GetIntentResponse interface {
ToJsonIETF(ctx context.Context) (any, error)
ToXML(ctx context.Context) (*etree.Document, error)
ToProtoUpdates(ctx context.Context) ([]*sdcpb.Update, error)
ToXPath(ctx context.Context) (*sdcpb.PathValues, error)
}
16 changes: 16 additions & 0 deletions pkg/server/intent.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
sdcpb "github.com/sdcio/sdc-protos/sdcpb"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/encoding/protojson"
)

func (s *Server) ListIntent(ctx context.Context, req *sdcpb.ListIntentRequest) (*sdcpb.ListIntentResponse, error) {
Expand Down Expand Up @@ -82,6 +83,20 @@ func (s *Server) GetIntent(ctx context.Context, req *sdcpb.GetIntentRequest) (*s
}

switch req.GetFormat() {
case sdcpb.Format_Intent_Format_XPATH:
j, err := rsp.ToXPath(ctx)
if err != nil {
return nil, err
}

b, err := protojson.Marshal(j)
if err != nil {
return nil, err
}
getIntentResponse.Intent = &sdcpb.GetIntentResponse_Blob{
Blob: b,
}
return getIntentResponse, nil
case sdcpb.Format_Intent_Format_JSON, sdcpb.Format_Intent_Format_JSON_IETF:
var j any
switch req.GetFormat() {
Expand All @@ -95,6 +110,7 @@ func (s *Server) GetIntent(ctx context.Context, req *sdcpb.GetIntentRequest) (*s
if err != nil {
return nil, err
}

}
b, err := json.Marshal(j)
if err != nil {
Expand Down
4 changes: 4 additions & 0 deletions pkg/tree/api/adapter/intentresponse.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,7 @@ func (t *IntentResponseAdapter) ToProtoUpdates(ctx context.Context) ([]*sdcpb.Up
func (t *IntentResponseAdapter) ToProtoDeletes(ctx context.Context) ([]*sdcpb.Path, error) {
return ops.ToProtoDeletes(ctx, t.Entry)
}

func (t *IntentResponseAdapter) ToXPath(ctx context.Context) (*sdcpb.PathValues, error) {
return ops.ToXPath(ctx, t.Entry, false, false)
}
23 changes: 23 additions & 0 deletions pkg/tree/ops/xpath.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package ops

import (
"context"

"github.com/sdcio/data-server/pkg/tree/api"
"github.com/sdcio/sdc-protos/sdcpb"
)

// ToXPath converts the branch under e to a list of XPath entries. It returns only the highest precedence value for each path.
func ToXPath(_ context.Context, e api.Entry, onlyNewOrUpdated, includeDefaults bool) (*sdcpb.PathValues, error) {
lvs := GetHighestPrecedence(e, onlyNewOrUpdated, includeDefaults, false)

xpaths := make([]*sdcpb.PathValue, 0, len(lvs))

for _, lv := range lvs {
xpaths = append(xpaths, &sdcpb.PathValue{
Path: lv.SdcpbPath(),
Value: lv.Value(),
})
}
return &sdcpb.PathValues{PathValues: xpaths}, nil
}
140 changes: 140 additions & 0 deletions pkg/tree/ops/xpath_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package ops_test

import (
"context"
"runtime"
"testing"

"github.com/sdcio/data-server/pkg/pool"
"github.com/sdcio/data-server/pkg/tree"
"github.com/sdcio/data-server/pkg/tree/ops"
"github.com/sdcio/data-server/pkg/utils/testhelper"
"go.uber.org/mock/gomock"
)

func TestToXPath_FromConfig1(t *testing.T) {
ctx := context.Background()

mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()

scb, err := testhelper.GetSchemaClientBound(t, mockCtrl)
if err != nil {
t.Fatal(err)
}

tc := tree.NewTreeContext(scb, pool.NewSharedTaskPool(ctx, runtime.GOMAXPROCS(0)))
root, err := tree.NewTreeRoot(ctx, tc)
if err != nil {
t.Fatal(err)
}

_, err = testhelper.LoadYgotStructIntoTreeRoot(ctx, testhelper.Config1(), root.Entry, "owner1", 5, false, testhelper.FlagsNew)
if err != nil {
t.Fatal(err)
}

err = root.FinishInsertionPhase(ctx)
if err != nil {
t.Fatal(err)
}

pvs, err := ops.ToXPath(ctx, root.Entry, false, false)
if err != nil {
t.Fatal(err)
}

if len(pvs.GetPathValues()) == 0 {
t.Fatal("ToXPath() returned no path values")
}

got := make(map[string]string, len(pvs.GetPathValues()))
for _, pv := range pvs.GetPathValues() {
if pv.GetPath() == nil {
t.Fatal("ToXPath() returned a path value with nil path")
}
if pv.GetValue() == nil {
t.Fatalf("ToXPath() returned nil value for path %s", pv.GetPath().ToXPath(false))
}

xPath := pv.GetPath().ToXPath(false)
if strVal := pv.GetValue().GetStringVal(); strVal != "" {
got[xPath] = strVal
continue
}
got[xPath] = pv.GetValue().String()
}

if got["/interface[name=ethernet-1/1]/description"] != "Foo" {
t.Fatalf("missing or mismatching interface description, got %q", got["/interface[name=ethernet-1/1]/description"])
}

if got["/network-instance[name=default]/description"] != "Default NI" {
t.Fatalf("missing or mismatching network-instance description, got %q", got["/network-instance[name=default]/description"])
}

if got["/patterntest"] != "hallo 00" {
t.Fatalf("missing or mismatching patterntest, got %q", got["/patterntest"])
}
}

func TestToXPath_OnlyNewOrUpdated_WithSameNewAndExistingConfig1(t *testing.T) {
ctx := context.Background()

mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()

scb, err := testhelper.GetSchemaClientBound(t, mockCtrl)
if err != nil {
t.Fatal(err)
}

tc := tree.NewTreeContext(scb, pool.NewSharedTaskPool(ctx, runtime.GOMAXPROCS(0)))
root, err := tree.NewTreeRoot(ctx, tc)
if err != nil {
t.Fatal(err)
}

owner := "owner1"
prio := int32(5)

_, err = testhelper.LoadYgotStructIntoTreeRoot(ctx, testhelper.Config1(), root.Entry, owner, prio, false, testhelper.FlagsExisting)
if err != nil {
t.Fatal(err)
}

_, err = testhelper.LoadYgotStructIntoTreeRoot(ctx, testhelper.Config1(), root.Entry, owner, prio, false, testhelper.FlagsNew)
if err != nil {
t.Fatal(err)
}

err = root.FinishInsertionPhase(ctx)
if err != nil {
t.Fatal(err)
}

onlyNewOrUpdated, err := ops.ToXPath(ctx, root.Entry, true, false)
if err != nil {
t.Fatal(err)
}

allValues, err := ops.ToXPath(ctx, root.Entry, false, false)
if err != nil {
t.Fatal(err)
}

if got, want := len(onlyNewOrUpdated.GetPathValues()), len(allValues.GetPathValues()); got != want {
t.Fatalf("ToXPath() onlyNewOrUpdated count mismatch: got %d, want %d", got, want)
}

containsPatternTest := false
for _, pv := range onlyNewOrUpdated.GetPathValues() {
if pv.GetPath() != nil && pv.GetPath().ToXPath(false) == "/patterntest" {
containsPatternTest = true
break
}
}
if !containsPatternTest {
t.Fatal("ToXPath() did not include expected /patterntest path")
}
}
Loading