blob: 3a4dc7917552b3f771f83755290e4afa2b35e9a6 [file] [log] [blame]
Elia Battistonac8d23f2022-03-14 17:54:56 +01001/*
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
Elia Battistone1cecb22022-03-21 10:05:25 +010019//#cgo LDFLAGS: -lsysrepo -lyang -Wl,--allow-multiple-definition
Elia Battistonac8d23f2022-03-14 17:54:56 +010020//#include "plugin.c"
21import "C"
22import (
23 "context"
24 "fmt"
Elia Battiston589addb2022-04-04 16:40:01 +020025 "io/ioutil"
26 "os"
Elia Battistone1cecb22022-03-21 10:05:25 +010027 "unsafe"
Elia Battistonac8d23f2022-03-14 17:54:56 +010028
29 "github.com/opencord/voltha-lib-go/v7/pkg/log"
Elia Battistone1cecb22022-03-21 10:05:25 +010030 "github.com/opencord/voltha-northbound-bbf-adapter/internal/core"
Elia Battistonac8d23f2022-03-14 17:54:56 +010031)
32
33type SysrepoPlugin struct {
Elia Battiston589addb2022-04-04 16:40:01 +020034 connection *C.sr_conn_ctx_t
35 session *C.sr_session_ctx_t
36 subscription *C.sr_subscription_ctx_t
37 schemaMountData *C.lyd_node
Elia Battistonac8d23f2022-03-14 17:54:56 +010038}
39
Elia Battiston589addb2022-04-04 16:40:01 +020040func srErrorMsg(code C.int) string {
Elia Battistonac8d23f2022-03-14 17:54:56 +010041 return C.GoString(C.sr_strerror(code))
42}
43
Elia Battiston589addb2022-04-04 16:40:01 +020044func lyErrorMsg(ly_ctx *C.ly_ctx) string {
45 lyErrString := C.ly_errmsg(ly_ctx)
46 defer freeCString(lyErrString)
47
48 return C.GoString(lyErrString)
49}
50
Elia Battistone1cecb22022-03-21 10:05:25 +010051func freeCString(str *C.char) {
52 if str != nil {
53 C.free(unsafe.Pointer(str))
54 str = nil
55 }
56}
57
58func updateYangItems(ctx context.Context, session *C.sr_session_ctx_t, parent **C.lyd_node, items []core.YangItem) error {
59 conn := C.sr_session_get_connection(session)
60 if conn == nil {
61 return fmt.Errorf("null-connection")
62 }
63
64 //libyang context
Elia Battistonb244bb52022-03-24 15:47:16 +010065 ly_ctx := C.sr_acquire_context(conn)
Elia Battiston589addb2022-04-04 16:40:01 +020066 defer C.sr_release_context(conn)
Elia Battistone1cecb22022-03-21 10:05:25 +010067 if ly_ctx == nil {
68 return fmt.Errorf("null-libyang-context")
69 }
70
71 for _, item := range items {
Elia Battiston589addb2022-04-04 16:40:01 +020072 if item.Value == "" {
73 continue
74 }
75
Elia Battistone1cecb22022-03-21 10:05:25 +010076 logger.Debugw(ctx, "updating-yang-item", log.Fields{"item": item})
77
78 path := C.CString(item.Path)
79 value := C.CString(item.Value)
80
81 lyErr := C.lyd_new_path(*parent, ly_ctx, path, value, 0, nil)
82 if lyErr != C.LY_SUCCESS {
83 freeCString(path)
84 freeCString(value)
Elia Battistonb244bb52022-03-24 15:47:16 +010085
Elia Battiston589addb2022-04-04 16:40:01 +020086 err := fmt.Errorf("libyang-new-path-failed: %d %s", lyErr, lyErrorMsg(ly_ctx))
Elia Battistonb244bb52022-03-24 15:47:16 +010087
88 return err
Elia Battistone1cecb22022-03-21 10:05:25 +010089 }
90
91 freeCString(path)
92 freeCString(value)
93 }
94
95 return nil
96}
97
Elia Battistonac8d23f2022-03-14 17:54:56 +010098//createPluginState populates a SysrepoPlugin struct by establishing
99//a connection and a session
100func (p *SysrepoPlugin) createSession(ctx context.Context) error {
Elia Battistonac8d23f2022-03-14 17:54:56 +0100101 var errCode C.int
102
103 //Populates connection
104 errCode = C.sr_connect(C.SR_CONN_DEFAULT, &p.connection)
105 if errCode != C.SR_ERR_OK {
106 err := fmt.Errorf("sysrepo-connect-error")
Elia Battiston589addb2022-04-04 16:40:01 +0200107 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
Elia Battistonac8d23f2022-03-14 17:54:56 +0100108 return err
109 }
110
111 //Populates session
112 errCode = C.sr_session_start(p.connection, C.SR_DS_RUNNING, &p.session)
113 if errCode != C.SR_ERR_OK {
114 err := fmt.Errorf("sysrepo-session-error")
Elia Battiston589addb2022-04-04 16:40:01 +0200115 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
Elia Battistonac8d23f2022-03-14 17:54:56 +0100116
117 _ = p.Stop(ctx)
118
119 return err
120 }
121
122 return nil
123}
124
Elia Battistone1cecb22022-03-21 10:05:25 +0100125//export get_devices_cb
126func get_devices_cb(session *C.sr_session_ctx_t, parent **C.lyd_node) C.sr_error_t {
127 //This function is a callback for the retrieval of devices from sysrepo
Elia Battistonac8d23f2022-03-14 17:54:56 +0100128 //The "export" comment instructs CGO to create a C function for it
129
Elia Battistonac8d23f2022-03-14 17:54:56 +0100130 ctx := context.Background()
Elia Battistone1cecb22022-03-21 10:05:25 +0100131 logger.Debug(ctx, "processing-get-data-request")
132
133 if session == nil {
134 logger.Error(ctx, "sysrepo-get-data-null-session")
135 return C.SR_ERR_OPERATION_FAILED
136 }
137
138 if parent == nil {
139 logger.Error(ctx, "sysrepo-get-data-null-parent-node")
140 return C.SR_ERR_OPERATION_FAILED
141 }
142
143 if core.AdapterInstance == nil {
144 logger.Error(ctx, "sysrepo-get-data-nil-translator")
145 return C.SR_ERR_OPERATION_FAILED
146 }
147
148 devices, err := core.AdapterInstance.GetDevices(ctx)
149 if err != nil {
150 logger.Errorw(ctx, "sysrepo-get-data-translator-error", log.Fields{"err": err})
151 return C.SR_ERR_OPERATION_FAILED
152 }
153
154 err = updateYangItems(ctx, session, parent, devices)
155 if err != nil {
156 logger.Errorw(ctx, "sysrepo-get-data-update-error", log.Fields{"err": err})
157 return C.SR_ERR_OPERATION_FAILED
158 }
159
160 return C.SR_ERR_OK
Elia Battistonac8d23f2022-03-14 17:54:56 +0100161}
162
Elia Battiston589addb2022-04-04 16:40:01 +0200163func StartNewPlugin(ctx context.Context, schemaMountFilePath string) (*SysrepoPlugin, error) {
Elia Battistonac8d23f2022-03-14 17:54:56 +0100164 plugin := &SysrepoPlugin{}
165
166 //Open a session to sysrepo
167 err := plugin.createSession(ctx)
168 if err != nil {
169 return nil, err
170 }
171
Elia Battiston589addb2022-04-04 16:40:01 +0200172 //Read the schema-mount file
173 if _, err := os.Stat(schemaMountFilePath); err != nil {
174 //The file cannot be found
175 return nil, fmt.Errorf("plugin-startup-schema-mount-file-not-found: %v", err)
176 }
177
178 smBuffer, err := ioutil.ReadFile(schemaMountFilePath)
179 if err != nil {
180 return nil, fmt.Errorf("plugin-startup-cannot-read-schema-mount-file: %v", err)
181 }
182
183 smString := C.CString(string(smBuffer))
184 defer freeCString(smString)
185
186 ly_ctx := C.sr_acquire_context(plugin.connection)
187 defer C.sr_release_context(plugin.connection)
188 if ly_ctx == nil {
189 return nil, fmt.Errorf("plugin-startup-null-libyang-context")
190 }
191
192 //Parse the schema-mount file into libyang nodes, and save them into the plugin data
193 lyErrCode := C.lyd_parse_data_mem(ly_ctx, smString, C.LYD_XML, C.LYD_PARSE_STRICT, C.LYD_VALIDATE_PRESENT, &plugin.schemaMountData)
194 if lyErrCode != C.LY_SUCCESS {
195 return nil, fmt.Errorf("plugin-startup-cannot-parse-schema-mount: %v", lyErrorMsg(ly_ctx))
196 }
197
198 //Bind the callback needed to support schema-mount
199 C.sr_set_ext_data_cb(plugin.connection, C.function(C.mountpoint_ext_data_clb), unsafe.Pointer(plugin.schemaMountData))
Elia Battistonac8d23f2022-03-14 17:54:56 +0100200
201 //Set callbacks for events
202
203 //Subscribe with a callback to the request of data on a certain path
Elia Battistone1cecb22022-03-21 10:05:25 +0100204 module := C.CString(core.DeviceAggregationModel)
205 defer freeCString(module)
206
207 path := C.CString(core.DevicesPath + "/*")
208 defer freeCString(path)
209
Elia Battistonb244bb52022-03-24 15:47:16 +0100210 errCode := C.sr_oper_get_subscribe(
Elia Battistonac8d23f2022-03-14 17:54:56 +0100211 plugin.session,
Elia Battistone1cecb22022-03-21 10:05:25 +0100212 module,
213 path,
214 C.function(C.get_devices_cb_wrapper),
Elia Battistonac8d23f2022-03-14 17:54:56 +0100215 C.NULL,
Elia Battistonb244bb52022-03-24 15:47:16 +0100216 C.SR_SUBSCR_DEFAULT,
Elia Battistonac8d23f2022-03-14 17:54:56 +0100217 &plugin.subscription,
218 )
219 if errCode != C.SR_ERR_OK {
220 err := fmt.Errorf("sysrepo-failed-subscription-to-get-events")
Elia Battiston589addb2022-04-04 16:40:01 +0200221 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
Elia Battistonac8d23f2022-03-14 17:54:56 +0100222 return nil, err
223 }
224
225 logger.Debug(ctx, "sysrepo-plugin-started")
226
227 return plugin, nil
228}
229
230func (p *SysrepoPlugin) Stop(ctx context.Context) error {
231 var errCode C.int
232
Elia Battiston589addb2022-04-04 16:40:01 +0200233 //Free the libyang nodes for external schema-mount data
234 C.lyd_free_all(p.schemaMountData)
235
Elia Battistonac8d23f2022-03-14 17:54:56 +0100236 //Frees subscription
237 if p.subscription != nil {
238 errCode = C.sr_unsubscribe(p.subscription)
239 if errCode != C.SR_ERR_OK {
240 err := fmt.Errorf("failed-to-close-sysrepo-subscription")
Elia Battiston589addb2022-04-04 16:40:01 +0200241 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
Elia Battistonac8d23f2022-03-14 17:54:56 +0100242 return err
243 }
244 p.subscription = nil
245 }
246
247 //Frees session
248 if p.session != nil {
249 errCode = C.sr_session_stop(p.session)
250 if errCode != C.SR_ERR_OK {
251 err := fmt.Errorf("failed-to-close-sysrepo-session")
Elia Battiston589addb2022-04-04 16:40:01 +0200252 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
Elia Battistonac8d23f2022-03-14 17:54:56 +0100253 return err
254 }
255 p.session = nil
256 }
257
258 //Frees connection
259 if p.connection != nil {
260 errCode = C.sr_disconnect(p.connection)
261 if errCode != C.SR_ERR_OK {
262 err := fmt.Errorf("failed-to-close-sysrepo-connection")
Elia Battiston589addb2022-04-04 16:40:01 +0200263 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
Elia Battistonac8d23f2022-03-14 17:54:56 +0100264 return err
265 }
266 p.connection = nil
267 }
268
269 logger.Debug(ctx, "sysrepo-plugin-stopped")
270
271 return nil
272}