blob: 973dbb93c438d723e0b924a21a87197731c05a33 [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 Battistonb244bb52022-03-24 15:47:16 +0100136 errCode := C.sr_oper_get_subscribe(
Elia Battiston4750d3c2022-07-14 13:24:56 +0000137 plugin.operationalSession,
138 devicesModule,
139 devicesPath,
Elia Battistone1cecb22022-03-21 10:05:25 +0100140 C.function(C.get_devices_cb_wrapper),
Elia Battistonac8d23f2022-03-14 17:54:56 +0100141 C.NULL,
Elia Battistonb244bb52022-03-24 15:47:16 +0100142 C.SR_SUBSCR_DEFAULT,
Elia Battistonac8d23f2022-03-14 17:54:56 +0100143 &plugin.subscription,
144 )
145 if errCode != C.SR_ERR_OK {
146 err := fmt.Errorf("sysrepo-failed-subscription-to-get-events")
Elia Battiston589addb2022-04-04 16:40:01 +0200147 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
Elia Battistonac8d23f2022-03-14 17:54:56 +0100148 return nil, err
149 }
150
151 logger.Debug(ctx, "sysrepo-plugin-started")
152
153 return plugin, nil
154}
155
156func (p *SysrepoPlugin) Stop(ctx context.Context) error {
157 var errCode C.int
158
Elia Battiston589addb2022-04-04 16:40:01 +0200159 //Free the libyang nodes for external schema-mount data
160 C.lyd_free_all(p.schemaMountData)
161
Elia Battistonac8d23f2022-03-14 17:54:56 +0100162 //Frees subscription
163 if p.subscription != nil {
164 errCode = C.sr_unsubscribe(p.subscription)
165 if errCode != C.SR_ERR_OK {
166 err := fmt.Errorf("failed-to-close-sysrepo-subscription")
Elia Battiston589addb2022-04-04 16:40:01 +0200167 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
Elia Battistonac8d23f2022-03-14 17:54:56 +0100168 return err
169 }
170 p.subscription = nil
171 }
172
Elia Battiston4750d3c2022-07-14 13:24:56 +0000173 //Frees sessions
174 if p.operationalSession != nil {
175 errCode = C.sr_session_stop(p.operationalSession)
Elia Battistonac8d23f2022-03-14 17:54:56 +0100176 if errCode != C.SR_ERR_OK {
Elia Battiston4750d3c2022-07-14 13:24:56 +0000177 err := fmt.Errorf("failed-to-close-operational-session")
Elia Battiston589addb2022-04-04 16:40:01 +0200178 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
Elia Battistonac8d23f2022-03-14 17:54:56 +0100179 return err
180 }
Elia Battiston4750d3c2022-07-14 13:24:56 +0000181 p.operationalSession = nil
182 }
183
184 if p.runningSession != nil {
185 errCode = C.sr_session_stop(p.runningSession)
186 if errCode != C.SR_ERR_OK {
187 err := fmt.Errorf("failed-to-close-running-session")
188 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
189 return err
190 }
191 p.runningSession = nil
Elia Battistonac8d23f2022-03-14 17:54:56 +0100192 }
193
194 //Frees connection
195 if p.connection != nil {
196 errCode = C.sr_disconnect(p.connection)
197 if errCode != C.SR_ERR_OK {
198 err := fmt.Errorf("failed-to-close-sysrepo-connection")
Elia Battiston589addb2022-04-04 16:40:01 +0200199 logger.Errorw(ctx, err.Error(), log.Fields{"errCode": errCode, "errMsg": srErrorMsg(errCode)})
Elia Battistonac8d23f2022-03-14 17:54:56 +0100200 return err
201 }
202 p.connection = nil
203 }
204
205 logger.Debug(ctx, "sysrepo-plugin-stopped")
206
207 return nil
208}