blob: a2b19af4154649da91fd9f9eb7a14c483a531645 [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 Battiston4750d3c2022-07-14 13:24:56 +000034 connection *C.sr_conn_ctx_t
35 operationalSession *C.sr_session_ctx_t
36 runningSession *C.sr_session_ctx_t
37 subscription *C.sr_subscription_ctx_t
38 schemaMountData *C.lyd_node
Elia Battistone1cecb22022-03-21 10:05:25 +010039}
40
Elia Battistonac8d23f2022-03-14 17:54:56 +010041//createPluginState populates a SysrepoPlugin struct by establishing
42//a connection and a session
Elia Battiston4750d3c2022-07-14 13:24:56 +000043func (p *SysrepoPlugin) createSessions(ctx context.Context) error {
Elia Battistonac8d23f2022-03-14 17:54:56 +010044 var errCode C.int
45
46 //Populates connection
47 errCode = C.sr_connect(C.SR_CONN_DEFAULT, &p.connection)
48 if errCode != C.SR_ERR_OK {
49 err := fmt.Errorf("sysrepo-connect-error")
Elia Battiston589addb2022-04-04 16:40:01 +020050 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
Elia Battistonac8d23f2022-03-14 17:54:56 +010051 return err
52 }
53
Elia Battiston4750d3c2022-07-14 13:24:56 +000054 //Populates sessions
55 //The session on the operation datastore will be used for most operations
56 //The session on the running datastore will be used for the subscription to edits
57 //since the operational datastore can't be edited by the client
58 errCode = C.sr_session_start(p.connection, C.SR_DS_OPERATIONAL, &p.operationalSession)
Elia Battistonac8d23f2022-03-14 17:54:56 +010059 if errCode != C.SR_ERR_OK {
Elia Battiston4750d3c2022-07-14 13:24:56 +000060 err := fmt.Errorf("sysrepo-operational-session-error")
61 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
62
63 _ = p.Stop(ctx)
64
65 return err
66 }
67
68 errCode = C.sr_session_start(p.connection, C.SR_DS_RUNNING, &p.runningSession)
69 if errCode != C.SR_ERR_OK {
70 err := fmt.Errorf("sysrepo-running-session-error")
Elia Battiston589addb2022-04-04 16:40:01 +020071 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
Elia Battistonac8d23f2022-03-14 17:54:56 +010072
73 _ = p.Stop(ctx)
74
75 return err
76 }
77
78 return nil
79}
80
Elia Battiston589addb2022-04-04 16:40:01 +020081func StartNewPlugin(ctx context.Context, schemaMountFilePath string) (*SysrepoPlugin, error) {
Elia Battistonac8d23f2022-03-14 17:54:56 +010082 plugin := &SysrepoPlugin{}
83
Elia Battiston4750d3c2022-07-14 13:24:56 +000084 //Set sysrepo and libyang log level
85 if logger.GetLogLevel() == log.DebugLevel {
86 C.sr_log_stderr(C.SR_LL_INF)
87 C.ly_log_level(C.LY_LLVRB)
88 } else {
89 C.sr_log_stderr(C.SR_LL_ERR)
90 C.ly_log_level(C.LY_LLERR)
91 }
92
Elia Battistonac8d23f2022-03-14 17:54:56 +010093 //Open a session to sysrepo
Elia Battiston4750d3c2022-07-14 13:24:56 +000094 err := plugin.createSessions(ctx)
Elia Battistonac8d23f2022-03-14 17:54:56 +010095 if err != nil {
96 return nil, err
97 }
98
Elia Battiston589addb2022-04-04 16:40:01 +020099 //Read the schema-mount file
100 if _, err := os.Stat(schemaMountFilePath); err != nil {
101 //The file cannot be found
102 return nil, fmt.Errorf("plugin-startup-schema-mount-file-not-found: %v", err)
103 }
104
105 smBuffer, err := ioutil.ReadFile(schemaMountFilePath)
106 if err != nil {
107 return nil, fmt.Errorf("plugin-startup-cannot-read-schema-mount-file: %v", err)
108 }
109
110 smString := C.CString(string(smBuffer))
111 defer freeCString(smString)
112
113 ly_ctx := C.sr_acquire_context(plugin.connection)
114 defer C.sr_release_context(plugin.connection)
115 if ly_ctx == nil {
116 return nil, fmt.Errorf("plugin-startup-null-libyang-context")
117 }
118
119 //Parse the schema-mount file into libyang nodes, and save them into the plugin data
120 lyErrCode := C.lyd_parse_data_mem(ly_ctx, smString, C.LYD_XML, C.LYD_PARSE_STRICT, C.LYD_VALIDATE_PRESENT, &plugin.schemaMountData)
121 if lyErrCode != C.LY_SUCCESS {
122 return nil, fmt.Errorf("plugin-startup-cannot-parse-schema-mount: %v", lyErrorMsg(ly_ctx))
123 }
124
125 //Bind the callback needed to support schema-mount
126 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 +0100127
128 //Set callbacks for events
129
130 //Subscribe with a callback to the request of data on a certain path
Elia Battiston4750d3c2022-07-14 13:24:56 +0000131 devicesModule := C.CString(core.DeviceAggregationModule)
132 devicesPath := C.CString(core.DevicesPath + "/*")
133 defer freeCString(devicesModule)
134 defer freeCString(devicesPath)
Elia Battistone1cecb22022-03-21 10:05:25 +0100135
Elia Battistona1333642022-07-27 12:17:24 +0000136 servicesModule := C.CString(core.ServiceProfileModule)
137 servicesPath := C.CString(core.ServiceProfilesPath + "/*")
138 defer freeCString(servicesModule)
139 defer freeCString(servicesPath)
140
141 vlansModule := C.CString(core.VlansModule)
142 vlansPath := C.CString(core.VlansPath + "/*")
143 defer freeCString(vlansModule)
144 defer freeCString(vlansPath)
145
146 bwProfilesModule := C.CString(core.BandwidthProfileModule)
147 bwProfilesPath := C.CString(core.BandwidthProfilesPath + "/*")
148 defer freeCString(bwProfilesModule)
149 defer freeCString(bwProfilesPath)
150
151 //Get devices
Elia Battistonb244bb52022-03-24 15:47:16 +0100152 errCode := C.sr_oper_get_subscribe(
Elia Battiston4750d3c2022-07-14 13:24:56 +0000153 plugin.operationalSession,
154 devicesModule,
155 devicesPath,
Elia Battistone1cecb22022-03-21 10:05:25 +0100156 C.function(C.get_devices_cb_wrapper),
Elia Battistonac8d23f2022-03-14 17:54:56 +0100157 C.NULL,
Elia Battistonb244bb52022-03-24 15:47:16 +0100158 C.SR_SUBSCR_DEFAULT,
Elia Battistonac8d23f2022-03-14 17:54:56 +0100159 &plugin.subscription,
160 )
161 if errCode != C.SR_ERR_OK {
Elia Battistona1333642022-07-27 12:17:24 +0000162 err := fmt.Errorf("sysrepo-failed-subscription-to-get-devices")
163 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
164 return nil, err
165 }
166
167 //Get services
168 errCode = C.sr_oper_get_subscribe(
169 plugin.operationalSession,
170 servicesModule,
171 servicesPath,
172 C.function(C.get_services_cb_wrapper),
173 C.NULL,
174 C.SR_SUBSCR_DEFAULT,
175 &plugin.subscription,
176 )
177 if errCode != C.SR_ERR_OK {
178 err := fmt.Errorf("sysrepo-failed-subscription-to-get-services")
179 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
180 return nil, err
181 }
182
183 //Get vlans
184 errCode = C.sr_oper_get_subscribe(
185 plugin.operationalSession,
186 vlansModule,
187 vlansPath,
188 C.function(C.get_vlans_cb_wrapper),
189 C.NULL,
190 C.SR_SUBSCR_DEFAULT,
191 &plugin.subscription,
192 )
193 if errCode != C.SR_ERR_OK {
194 err := fmt.Errorf("sysrepo-failed-subscription-to-get-services")
195 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
196 return nil, err
197 }
198
199 //Get bandwidth profiles
200 errCode = C.sr_oper_get_subscribe(
201 plugin.operationalSession,
202 bwProfilesModule,
203 bwProfilesPath,
204 C.function(C.get_bandwidth_profiles_cb_wrapper),
205 C.NULL,
206 C.SR_SUBSCR_DEFAULT,
207 &plugin.subscription,
208 )
209 if errCode != C.SR_ERR_OK {
210 err := fmt.Errorf("sysrepo-failed-subscription-to-get-services")
211 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
212 return nil, err
213 }
214
215 //Subscribe with a callback to changes of configuration in the services modules
216 //Changes to services
217 errCode = C.sr_module_change_subscribe(
218 plugin.runningSession,
219 servicesModule,
220 servicesPath,
221 C.function(C.edit_service_profiles_cb_wrapper),
222 unsafe.Pointer(plugin.runningSession), //Pass session for running datastore to get current data
223 0,
224 C.SR_SUBSCR_DEFAULT,
225 &plugin.subscription,
226 )
227 if errCode != C.SR_ERR_OK {
228 err := fmt.Errorf("sysrepo-failed-subscription-to-change-services")
229 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
230 return nil, err
231 }
232
233 //Changes to VLANs
234 errCode = C.sr_module_change_subscribe(
235 plugin.runningSession,
236 vlansModule,
237 vlansPath,
238 C.function(C.edit_vlans_cb_wrapper),
239 C.NULL,
240 0,
241 C.SR_SUBSCR_DEFAULT,
242 &plugin.subscription,
243 )
244 if errCode != C.SR_ERR_OK {
245 err := fmt.Errorf("sysrepo-failed-subscription-to-change-vlans")
Elia Battiston589addb2022-04-04 16:40:01 +0200246 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
Elia Battistonac8d23f2022-03-14 17:54:56 +0100247 return nil, err
248 }
249
250 logger.Debug(ctx, "sysrepo-plugin-started")
251
252 return plugin, nil
253}
254
255func (p *SysrepoPlugin) Stop(ctx context.Context) error {
256 var errCode C.int
257
Elia Battiston589addb2022-04-04 16:40:01 +0200258 //Free the libyang nodes for external schema-mount data
259 C.lyd_free_all(p.schemaMountData)
260
Elia Battistonac8d23f2022-03-14 17:54:56 +0100261 //Frees subscription
262 if p.subscription != nil {
263 errCode = C.sr_unsubscribe(p.subscription)
264 if errCode != C.SR_ERR_OK {
265 err := fmt.Errorf("failed-to-close-sysrepo-subscription")
Elia Battiston589addb2022-04-04 16:40:01 +0200266 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
Elia Battistonac8d23f2022-03-14 17:54:56 +0100267 return err
268 }
269 p.subscription = nil
270 }
271
Elia Battiston4750d3c2022-07-14 13:24:56 +0000272 //Frees sessions
273 if p.operationalSession != nil {
274 errCode = C.sr_session_stop(p.operationalSession)
Elia Battistonac8d23f2022-03-14 17:54:56 +0100275 if errCode != C.SR_ERR_OK {
Elia Battiston4750d3c2022-07-14 13:24:56 +0000276 err := fmt.Errorf("failed-to-close-operational-session")
Elia Battiston589addb2022-04-04 16:40:01 +0200277 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
Elia Battistonac8d23f2022-03-14 17:54:56 +0100278 return err
279 }
Elia Battiston4750d3c2022-07-14 13:24:56 +0000280 p.operationalSession = nil
281 }
282
283 if p.runningSession != nil {
284 errCode = C.sr_session_stop(p.runningSession)
285 if errCode != C.SR_ERR_OK {
286 err := fmt.Errorf("failed-to-close-running-session")
287 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
288 return err
289 }
290 p.runningSession = nil
Elia Battistonac8d23f2022-03-14 17:54:56 +0100291 }
292
293 //Frees connection
294 if p.connection != nil {
295 errCode = C.sr_disconnect(p.connection)
296 if errCode != C.SR_ERR_OK {
297 err := fmt.Errorf("failed-to-close-sysrepo-connection")
Elia Battiston589addb2022-04-04 16:40:01 +0200298 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
Elia Battistonac8d23f2022-03-14 17:54:56 +0100299 return err
300 }
301 p.connection = nil
302 }
303
304 logger.Debug(ctx, "sysrepo-plugin-stopped")
305
306 return nil
307}