blob: 4ab96ffdb70e882023380dfd18ce6dc850f2e526 [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 Battistone1cecb22022-03-21 10:05:25 +010025 "unsafe"
Elia Battistonac8d23f2022-03-14 17:54:56 +010026
27 "github.com/opencord/voltha-lib-go/v7/pkg/log"
Elia Battistone1cecb22022-03-21 10:05:25 +010028 "github.com/opencord/voltha-northbound-bbf-adapter/internal/core"
Elia Battistonac8d23f2022-03-14 17:54:56 +010029)
30
31type SysrepoPlugin struct {
32 connection *C.sr_conn_ctx_t
33 session *C.sr_session_ctx_t
34 subscription *C.sr_subscription_ctx_t
35}
36
37func errorMsg(code C.int) string {
38 return C.GoString(C.sr_strerror(code))
39}
40
Elia Battistone1cecb22022-03-21 10:05:25 +010041func freeCString(str *C.char) {
42 if str != nil {
43 C.free(unsafe.Pointer(str))
44 str = nil
45 }
46}
47
48func updateYangItems(ctx context.Context, session *C.sr_session_ctx_t, parent **C.lyd_node, items []core.YangItem) error {
49 conn := C.sr_session_get_connection(session)
50 if conn == nil {
51 return fmt.Errorf("null-connection")
52 }
53
54 //libyang context
Elia Battistonb244bb52022-03-24 15:47:16 +010055 ly_ctx := C.sr_acquire_context(conn)
Elia Battistone1cecb22022-03-21 10:05:25 +010056 if ly_ctx == nil {
57 return fmt.Errorf("null-libyang-context")
58 }
59
60 for _, item := range items {
61 logger.Debugw(ctx, "updating-yang-item", log.Fields{"item": item})
62
63 path := C.CString(item.Path)
64 value := C.CString(item.Value)
65
66 lyErr := C.lyd_new_path(*parent, ly_ctx, path, value, 0, nil)
67 if lyErr != C.LY_SUCCESS {
68 freeCString(path)
69 freeCString(value)
Elia Battistonb244bb52022-03-24 15:47:16 +010070
71 lyErrString := C.ly_errmsg(ly_ctx)
72 err := fmt.Errorf("libyang-new-path-failed: %d %s", lyErr, C.GoString(lyErrString))
73 freeCString(lyErrString)
74
75 return err
Elia Battistone1cecb22022-03-21 10:05:25 +010076 }
77
78 freeCString(path)
79 freeCString(value)
80 }
81
82 return nil
83}
84
Elia Battistonac8d23f2022-03-14 17:54:56 +010085//createPluginState populates a SysrepoPlugin struct by establishing
86//a connection and a session
87func (p *SysrepoPlugin) createSession(ctx context.Context) error {
Elia Battistonac8d23f2022-03-14 17:54:56 +010088 var errCode C.int
89
90 //Populates connection
91 errCode = C.sr_connect(C.SR_CONN_DEFAULT, &p.connection)
92 if errCode != C.SR_ERR_OK {
93 err := fmt.Errorf("sysrepo-connect-error")
94 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": errorMsg(errCode)})
95 return err
96 }
97
98 //Populates session
99 errCode = C.sr_session_start(p.connection, C.SR_DS_RUNNING, &p.session)
100 if errCode != C.SR_ERR_OK {
101 err := fmt.Errorf("sysrepo-session-error")
102 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": errorMsg(errCode)})
103
104 _ = p.Stop(ctx)
105
106 return err
107 }
108
109 return nil
110}
111
Elia Battistone1cecb22022-03-21 10:05:25 +0100112//export get_devices_cb
113func get_devices_cb(session *C.sr_session_ctx_t, parent **C.lyd_node) C.sr_error_t {
114 //This function is a callback for the retrieval of devices from sysrepo
Elia Battistonac8d23f2022-03-14 17:54:56 +0100115 //The "export" comment instructs CGO to create a C function for it
116
Elia Battistonac8d23f2022-03-14 17:54:56 +0100117 ctx := context.Background()
Elia Battistone1cecb22022-03-21 10:05:25 +0100118 logger.Debug(ctx, "processing-get-data-request")
119
120 if session == nil {
121 logger.Error(ctx, "sysrepo-get-data-null-session")
122 return C.SR_ERR_OPERATION_FAILED
123 }
124
125 if parent == nil {
126 logger.Error(ctx, "sysrepo-get-data-null-parent-node")
127 return C.SR_ERR_OPERATION_FAILED
128 }
129
130 if core.AdapterInstance == nil {
131 logger.Error(ctx, "sysrepo-get-data-nil-translator")
132 return C.SR_ERR_OPERATION_FAILED
133 }
134
135 devices, err := core.AdapterInstance.GetDevices(ctx)
136 if err != nil {
137 logger.Errorw(ctx, "sysrepo-get-data-translator-error", log.Fields{"err": err})
138 return C.SR_ERR_OPERATION_FAILED
139 }
140
141 err = updateYangItems(ctx, session, parent, devices)
142 if err != nil {
143 logger.Errorw(ctx, "sysrepo-get-data-update-error", log.Fields{"err": err})
144 return C.SR_ERR_OPERATION_FAILED
145 }
146
147 return C.SR_ERR_OK
Elia Battistonac8d23f2022-03-14 17:54:56 +0100148}
149
150func StartNewPlugin(ctx context.Context) (*SysrepoPlugin, error) {
151 plugin := &SysrepoPlugin{}
152
153 //Open a session to sysrepo
154 err := plugin.createSession(ctx)
155 if err != nil {
156 return nil, err
157 }
158
159 //TODO: could be useful to set it according to the adapter log level
160 C.sr_log_stderr(C.SR_LL_WRN)
161
162 //Set callbacks for events
163
164 //Subscribe with a callback to the request of data on a certain path
Elia Battistone1cecb22022-03-21 10:05:25 +0100165 module := C.CString(core.DeviceAggregationModel)
166 defer freeCString(module)
167
168 path := C.CString(core.DevicesPath + "/*")
169 defer freeCString(path)
170
Elia Battistonb244bb52022-03-24 15:47:16 +0100171 errCode := C.sr_oper_get_subscribe(
Elia Battistonac8d23f2022-03-14 17:54:56 +0100172 plugin.session,
Elia Battistone1cecb22022-03-21 10:05:25 +0100173 module,
174 path,
175 C.function(C.get_devices_cb_wrapper),
Elia Battistonac8d23f2022-03-14 17:54:56 +0100176 C.NULL,
Elia Battistonb244bb52022-03-24 15:47:16 +0100177 C.SR_SUBSCR_DEFAULT,
Elia Battistonac8d23f2022-03-14 17:54:56 +0100178 &plugin.subscription,
179 )
180 if errCode != C.SR_ERR_OK {
181 err := fmt.Errorf("sysrepo-failed-subscription-to-get-events")
182 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": errorMsg(errCode)})
183 return nil, err
184 }
185
186 logger.Debug(ctx, "sysrepo-plugin-started")
187
188 return plugin, nil
189}
190
191func (p *SysrepoPlugin) Stop(ctx context.Context) error {
192 var errCode C.int
193
194 //Frees subscription
195 if p.subscription != nil {
196 errCode = C.sr_unsubscribe(p.subscription)
197 if errCode != C.SR_ERR_OK {
198 err := fmt.Errorf("failed-to-close-sysrepo-subscription")
199 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": errorMsg(errCode)})
200 return err
201 }
202 p.subscription = nil
203 }
204
205 //Frees session
206 if p.session != nil {
207 errCode = C.sr_session_stop(p.session)
208 if errCode != C.SR_ERR_OK {
209 err := fmt.Errorf("failed-to-close-sysrepo-session")
210 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": errorMsg(errCode)})
211 return err
212 }
213 p.session = nil
214 }
215
216 //Frees connection
217 if p.connection != nil {
218 errCode = C.sr_disconnect(p.connection)
219 if errCode != C.SR_ERR_OK {
220 err := fmt.Errorf("failed-to-close-sysrepo-connection")
221 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": errorMsg(errCode)})
222 return err
223 }
224 p.connection = nil
225 }
226
227 logger.Debug(ctx, "sysrepo-plugin-stopped")
228
229 return nil
230}