-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathselect.go
138 lines (125 loc) · 3.13 KB
/
select.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package gnmi
import (
"errors"
"fmt"
"strings"
"github.com/freeconf/restconf/device"
"github.com/freeconf/yang/meta"
"github.com/freeconf/yang/node"
pb_gnmi "github.com/openconfig/gnmi/proto/gnmi"
)
var errModelOrOrigin = errors.New("you must use models or use origin as model")
// TBH, i don't understand the use case for multiple models
var errOnlyOneModel = errors.New("only one model is currently supported")
var errNoSelection = errors.New("no prefix or path found")
var errKeysWhenNoList = errors.New("found keys when model is not a list")
func selectPath(device device.Device, models []*pb_gnmi.ModelData, path *pb_gnmi.Path) (*node.Selection, error) {
var model string
if len(models) > 0 {
if len(models) > 1 {
return nil, errOnlyOneModel
}
model = models[0].Name
} else if path == nil {
return nil, nil
} else if path.Origin == "" && len(path.Elem) > 0 {
return nil, errModelOrOrigin
} else {
model = path.Origin
}
b, err := device.Browser(model)
if err != nil {
return nil, err
}
if b == nil {
return nil, fmt.Errorf("no module with name '%s' found", models[0].Name)
}
s := b.Root()
ptr := s
if path != nil && len(path.Elem) > 0 {
s, err = advanceSelection(device, ptr, path)
if err != nil {
return nil, err
}
ptr = s
}
return ptr, nil
}
func advanceSelection(device device.Device, prefix *node.Selection, path *pb_gnmi.Path) (*node.Selection, error) {
if prefix == nil && path == nil {
return nil, errNoSelection
}
ptr := prefix
if prefix == nil {
if path == nil {
return nil, errNoSelection
}
if path.Origin == "" {
return nil, errModelOrOrigin
}
var err error
if ptr, err = selectPath(device, nil, path); err != nil {
return nil, err
}
return ptr, nil
}
if ptr == nil {
return nil, errNoSelection
}
if path != nil {
for _, seg := range path.Elem {
if seg == nil || seg.Name == "" {
continue
}
ident := seg.Name
if len(seg.Key) > 0 {
lmeta, valid := ptr.Meta().(*meta.List)
if !valid {
return nil, errKeysWhenNoList
}
ident = ident + "=" + encodeKey(lmeta, seg.Key)
}
s, err := ptr.Find(ident) // should find take keys to avoid encode/decoding step?
if err != nil || s == nil {
return nil, err
}
ptr = s
}
}
return ptr, nil
}
// Posted on 4/3/23 asking question on openconfig google group about how
// set is only method that doesn't have a use_model
func selectFullPath(device device.Device, prefix *pb_gnmi.Path, path *pb_gnmi.Path) (*node.Selection, error) {
var ptr *node.Selection
var err error
if prefix != nil {
if ptr, err = selectPath(device, nil, prefix); err != nil {
return nil, err
}
}
if path != nil {
if ptr == nil {
if ptr, err = selectPath(device, nil, path); err != nil {
return nil, err
}
} else {
s, err := advanceSelection(device, ptr, path)
if err != nil {
return nil, err
}
ptr = s
}
}
if ptr == nil {
return nil, errNoSelection
}
return ptr, nil
}
func encodeKey(m *meta.List, keys map[string]string) string {
vals := make([]string, len(m.KeyMeta()))
for i, k := range m.KeyMeta() {
vals[i] = keys[k.Ident()]
}
return strings.Join(vals, ",")
}