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
92 changes: 87 additions & 5 deletions pkg/datastore/target/gnmi.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ func (t *gnmiTarget) Get(ctx context.Context, req *sdcpb.GetDataRequest) (*sdcpb
return schemaRsp, nil
}

var nsMap = make(map[string]string)

func (t *gnmiTarget) Set(ctx context.Context, source TargetSource) (*sdcpb.SetDataResponse, error) {
var upds []*sdcpb.Update
var deletes []*sdcpb.Path
Expand Down Expand Up @@ -193,11 +195,91 @@ func (t *gnmiTarget) Set(ctx context.Context, source TargetSource) (*sdcpb.SetDa
return nil, err
}
if jsonData != nil {
jsonBytes, err := json.Marshal(jsonData)
if err != nil {
return nil, err
upds = []*sdcpb.Update{}
for key, value := range jsonData.(map[string]any) {
keyParts := strings.Split(key, ":")
rootNamespace := ""
rootNodeName := ""
if len(keyParts) == 2 {
rootNamespace = keyParts[0]
rootNodeName = keyParts[1]
}

updsPerNs := make(map[string]map[string]any)

// check second level of data to distinguish namespaces
for k, v := range value.(map[string]any) {
kP := strings.Split(k, ":")
var leafNamespace string
var leafNodeName string
if len(kP) == 2 {
leafNodeName = kP[1]
if len(kP) == 2 && kP[0] != rootNamespace {
// diverged namespace
leafNamespace = kP[0]
nsMap[rootNodeName+"/"+leafNodeName] = rootNamespace
} else {
// root namespace
leafNamespace = rootNamespace
}
} else {
leafNodeName = kP[0]
leafNamespace = rootNamespace
nsMap[rootNodeName] = rootNamespace
}
_, found := updsPerNs[leafNamespace]
if !found {
updsPerNs[leafNamespace] = make(map[string]any)
}
updsPerNs[leafNamespace][leafNodeName] = v
}

for k, v := range updsPerNs {
updPath := &sdcpb.Path{Origin: k}

updKey := k + ":" + rootNodeName

singleUpd := map[string]any{}
singleUpd[updKey] = v

singleUpdBytes, err := json.Marshal(singleUpd)
if err != nil {
return nil, err
}
updValue := &sdcpb.TypedValue{
Value: &sdcpb.TypedValue_JsonIetfVal{
JsonIetfVal: singleUpdBytes,
},
}

upds = append(upds, &sdcpb.Update{
Path: updPath,
Value: updValue,
})
}
}
}

for _, delete := range deletes {
if len(delete.Elem) > 0 {
completePath := ""
found := false
var mostSpecificOrigin string
for _, elem := range delete.Elem {
if completePath != "" {
completePath += "/"
}
completePath += elem.Name
origin, f := nsMap[completePath]
if f {
found = true
mostSpecificOrigin = origin
}
}
if found {
delete.Origin = mostSpecificOrigin
}
}
upds = []*sdcpb.Update{{Path: &sdcpb.Path{}, Value: &sdcpb.TypedValue{Value: &sdcpb.TypedValue_JsonIetfVal{JsonIetfVal: jsonBytes}}}}
}

case "proto":
Expand Down Expand Up @@ -368,7 +450,7 @@ func (t *gnmiTarget) getSync(ctx context.Context, gnmiSync *config.SyncProtocol,
req := &sdcpb.GetDataRequest{
Name: gnmiSync.Name,
Path: paths,
DataType: sdcpb.DataType_CONFIG,
DataType: sdcpb.DataType_ALL,
Encoding: sdcpb.Encoding(sdcpbEncoding(gnmiSync.Encoding)),
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/tree/validation_entry_leafref.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ func (s *sharedEntryAttributes) validateLeafRefs(ctx context.Context, resultChan
entry, err := s.NavigateLeafRef(ctx)
if err != nil || len(entry) == 0 {
// check if the OptionalInstance (!require-instances [https://datatracker.ietf.org/doc/html/rfc7950#section-9.9.3])
if s.schema.GetField().GetType().GetOptionalInstance() {
if !s.schema.GetField().IsMandatory {
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is two different things. Mandatory is a container option, mandates, that the mandatory attributes of that container have to be present.
The OptionalInstance is a leafref based option. So the default behaviour is that the referenced attribute must exist. Say under some routing protocol you reference an interface. So that interface must be created. but if the optional flag is set, it is ok, that the interface is reference, without being config-wise created. We will create a warnign for that, just lettings the user know that the referenced instance does not exist.

generateOptionalWarning(ctx, s, lref, resultChan)
return
}
Expand Down
8 changes: 7 additions & 1 deletion pkg/utils/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,13 @@ func convertStringToTv(schemaType *sdcpb.SchemaLeafType, v string, ts uint64) (*
case "boolean":
b, err := strconv.ParseBool(v)
if err != nil {
return nil, err
if strings.Contains(strings.ToLower(v), "true") {
Copy link
Collaborator

Choose a reason for hiding this comment

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

What's the issue here? Why contains? we can maybe change to

strconv.ParseBool(strings.ToLower(v))

But the other stuff does not make much sense to me. Because thats bassically what strings.ParseBool() does.

b = true
} else if strings.Contains(strings.ToLower(v), "false") {
b = false
} else {
return nil, err
}
}
return &sdcpb.TypedValue{
Timestamp: ts,
Expand Down