blob: 4db9a53a665fac979de62c99d4a92490ef5178e9 [file] [log] [blame]
Elia Battiston4750d3c2022-07-14 13:24:56 +00001/*
2* Copyright 2022-present Open Networking Foundation
3
4* Licensed under the Apache License, Version 2.0 (the "License");
5* you may not use this file except in compliance with the License.
6* You may obtain a copy of the License at
7
8* http://www.apache.org/licenses/LICENSE-2.0
9
10* Unless required by applicable law or agreed to in writing, software
11* distributed under the License is distributed on an "AS IS" BASIS,
12* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13* See the License for the specific language governing permissions and
14* limitations under the License.
15 */
16
17package sysrepo
18
19//#cgo LDFLAGS: -lsysrepo -lyang -Wl,--allow-multiple-definition
20//#include "plugin.c"
21import "C"
22import (
23 "context"
24 "fmt"
25 "unsafe"
26
27 "github.com/opencord/voltha-lib-go/v7/pkg/log"
28 "github.com/opencord/voltha-northbound-bbf-adapter/internal/core"
29)
30
31//srErrorMsg provides a description of a sysrepo error code
32func srErrorMsg(code C.int) string {
33 return C.GoString(C.sr_strerror(code))
34}
35
36//lyErrorMsg provides the last libyang error message
37func lyErrorMsg(ly_ctx *C.ly_ctx) string {
38 lyErrString := C.ly_errmsg(ly_ctx)
39 defer freeCString(lyErrString)
40
41 return C.GoString(lyErrString)
42}
43
44func freeCString(str *C.char) {
45 if str != nil {
46 C.free(unsafe.Pointer(str))
47 str = nil
48 }
49}
50
51//Creates a new libyang nodes tree from a set of new paths.
52//The tree must bee manually freed after its use with C.lyd_free_all or
53//an equivalent function
54func createYangTree(ctx context.Context, session *C.sr_session_ctx_t, items []core.YangItem) (*C.lyd_node, error) {
55 if len(items) == 0 {
56 return nil, fmt.Errorf("no-items")
57 }
58
59 conn := C.sr_session_get_connection(session)
60 if conn == nil {
61 return nil, fmt.Errorf("null-connection")
62 }
63
64 //libyang context
65 ly_ctx := C.sr_acquire_context(conn)
66 if ly_ctx == nil {
67 return nil, fmt.Errorf("null-libyang-context")
68 }
69 defer C.sr_release_context(conn)
70
71 //Create parent node
72 parentPath := C.CString(items[0].Path)
73 parentValue := C.CString(items[0].Value)
74
75 var parent *C.lyd_node
76 lyErr := C.lyd_new_path(nil, ly_ctx, parentPath, parentValue, 0, &parent)
77 if lyErr != C.LY_SUCCESS {
78 err := fmt.Errorf("libyang-new-path-failed: %d %s", lyErr, lyErrorMsg(ly_ctx))
79 return nil, err
80 }
81 logger.Debugw(ctx, "creating-yang-item", log.Fields{"item": items[0]})
82
83 freeCString(parentPath)
84 freeCString(parentValue)
85
86 //Add remaining nodes
87 for _, item := range items[1:] {
88 logger.Debugw(ctx, "creating-yang-item", log.Fields{"item": item})
89
90 path := C.CString(item.Path)
91 value := C.CString(item.Value)
92
93 lyErr := C.lyd_new_path(parent, ly_ctx, path, value, 0, nil)
94 if lyErr != C.LY_SUCCESS {
95 freeCString(path)
96 freeCString(value)
97
98 //Free the partially created tree
99 C.lyd_free_all(parent)
100
101 err := fmt.Errorf("libyang-new-path-failed: %d %s", lyErr, lyErrorMsg(ly_ctx))
102
103 return nil, err
104 }
105
106 freeCString(path)
107 freeCString(value)
108 }
109
110 return parent, nil
111}
112
113//Creates a set of new paths under an existing libyang tree parent node
114func updateYangTree(ctx context.Context, session *C.sr_session_ctx_t, parent **C.lyd_node, items []core.YangItem) error {
115 if len(items) == 0 {
116 //Nothing to do
117 return nil
118 }
119
120 conn := C.sr_session_get_connection(session)
121 if conn == nil {
122 return fmt.Errorf("null-connection")
123 }
124
125 //libyang context
126 ly_ctx := C.sr_acquire_context(conn)
127 if ly_ctx == nil {
128 return fmt.Errorf("null-libyang-context")
129 }
130 defer C.sr_release_context(conn)
131
132 for _, item := range items {
133 logger.Debugw(ctx, "updating-yang-item", log.Fields{"item": item})
134
135 path := C.CString(item.Path)
136 value := C.CString(item.Value)
137
Elia Battistona1333642022-07-27 12:17:24 +0000138 lyErr := C.lyd_new_path(*parent, ly_ctx, path, value, C.LYD_NEW_PATH_UPDATE, nil)
Elia Battiston4750d3c2022-07-14 13:24:56 +0000139 if lyErr != C.LY_SUCCESS {
140 freeCString(path)
141 freeCString(value)
142
143 err := fmt.Errorf("libyang-new-path-failed: %d %s", lyErr, lyErrorMsg(ly_ctx))
144
145 return err
146 }
147
148 freeCString(path)
149 freeCString(value)
150 }
151
152 return nil
153}
154
155//Merges the content of a yang tree with the content of the datastore.
156//The target datastore is the one on which the session has been created
157func editDatastore(ctx context.Context, session *C.sr_session_ctx_t, editsTree *C.lyd_node) error {
158 errCode := C.sr_edit_batch(session, editsTree, C.mergeOperation)
159 if errCode != C.SR_ERR_OK {
160 err := fmt.Errorf("failed-to-edit-datastore")
161 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
162 return err
163 }
164
165 errCode = C.sr_apply_changes(session, 0)
166 if errCode != C.SR_ERR_OK {
167 err := fmt.Errorf("failed-to-apply-datastore-changes")
168 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
169 return err
170 }
171
172 return nil
173}
Elia Battistona1333642022-07-27 12:17:24 +0000174
175type YangChange struct {
176 Path string
177 Value string
178 Operation C.sr_change_oper_t
179 /* Operation values:
180 SR_OP_CREATED
181 SR_OP_MODIFIED
182 SR_OP_DELETED
183 SR_OP_MOVED
184 */
185}
186
187//Provides a list of the changes occured under a specific path
188//Should only be used on the session from an sr_module_change_subscribe callback
189func getChangesList(ctx context.Context, editSession *C.sr_session_ctx_t, path string) ([]YangChange, error) {
190 result := []YangChange{}
191
192 changesPath := C.CString(path)
193 defer freeCString(changesPath)
194
195 var changesIterator *C.sr_change_iter_t
196 errCode := C.sr_get_changes_iter(editSession, changesPath, &changesIterator)
197 if errCode != C.SR_ERR_OK {
198 return nil, fmt.Errorf("cannot-get-iterator: %d %s", errCode, srErrorMsg(errCode))
199 }
200 defer C.sr_free_change_iter(changesIterator)
201
202 //Iterate over the changes
203 var operation C.sr_change_oper_t
204 var prevValue, prevList *C.char
205 var prevDefault C.int
206
207 var node *C.lyd_node
208 defer C.lyd_free_all(node)
209
210 errCode = C.sr_get_change_tree_next(editSession, changesIterator, &operation, &node, &prevValue, &prevList, &prevDefault)
211 for errCode != C.SR_ERR_NOT_FOUND {
212 if errCode != C.SR_ERR_OK {
213 return nil, fmt.Errorf("next-change-error: %d %s", errCode, srErrorMsg(errCode))
214 }
215
216 currentChange := YangChange{}
217 currentChange.Operation = operation
218
219 nodePath := C.lyd_path(node, C.LYD_PATH_STD, nil, 0)
220 if nodePath == nil {
221 return nil, fmt.Errorf("cannot-get-change-path")
222 }
223 currentChange.Path = C.GoString(nodePath)
224 freeCString(nodePath)
225
226 nodeValue := C.lyd_get_value(node)
227 if nodeValue != nil {
228 currentChange.Value = C.GoString(nodeValue)
229 result = append(result, currentChange)
230 }
231
232 errCode = C.sr_get_change_tree_next(editSession, changesIterator, &operation, &node, &prevValue, &prevList, &prevDefault)
233 }
234
235 return result, nil
236}
237
238//Verify that only one change occured under the specified path, and return its value
239//Should only be used on the session from an sr_module_change_subscribe callback
240func getSingleChangeValue(ctx context.Context, session *C.sr_session_ctx_t, path string) (string, error) {
241 changesList, err := getChangesList(ctx, session, path)
242 if err != nil {
243 return "", err
244 }
245
246 if len(changesList) != 1 {
247 logger.Errorw(ctx, "unexpected-number-of-yang-changes", log.Fields{
248 "changes": changesList,
249 })
250 return "", fmt.Errorf("unexpected-number-of-yang-changes")
251 }
252
253 return changesList[0].Value, nil
254}
255
256//Get the value of a leaf from the datastore
257//The target datastore is the one on which the session has been created
258func getDatastoreLeafValue(ctx context.Context, session *C.sr_session_ctx_t, path string) (string, error) {
259 cPath := C.CString(path)
260 defer freeCString(cPath)
261
262 var data *C.sr_data_t
263 defer C.sr_release_data(data)
264
265 errCode := C.sr_get_subtree(session, cPath, 0, &data)
266 if errCode != C.SR_ERR_OK {
267 return "", fmt.Errorf("cannot-get-data-from-datastore: %d %s", errCode, srErrorMsg(errCode))
268 }
269
270 if data == nil {
271 return "", fmt.Errorf("no-data-found-for-path: %s", path)
272 }
273
274 nodeValue := C.lyd_get_value(data.tree)
275 if nodeValue == nil {
276 return "", fmt.Errorf("cannot-get-value-from-data: %s", path)
277 }
278
279 return C.GoString(nodeValue), nil
280}