blob: b7c75700282aedfde139f0aa60f4988c22aa9ba4 [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"
Elia Battistona1333642022-07-27 12:17:24 +000024 "fmt"
25 "strconv"
Elia Battiston4750d3c2022-07-14 13:24:56 +000026
27 "github.com/opencord/voltha-lib-go/v7/pkg/log"
28 "github.com/opencord/voltha-northbound-bbf-adapter/internal/core"
29)
30
31//export get_devices_cb
32func get_devices_cb(session *C.sr_session_ctx_t, parent **C.lyd_node) C.sr_error_t {
33 //This function is a callback for the retrieval of devices from sysrepo
34 //The "export" comment instructs CGO to create a C function for it
35
36 ctx := context.Background()
37 logger.Debug(ctx, "processing-get-devices-request")
38
39 if session == nil {
40 logger.Error(ctx, "sysrepo-get-devices-null-session")
41 return C.SR_ERR_OPERATION_FAILED
42 }
43
44 if parent == nil {
45 logger.Error(ctx, "sysrepo-get-devices-null-parent-node")
46 return C.SR_ERR_OPERATION_FAILED
47 }
48
49 if core.AdapterInstance == nil {
50 logger.Error(ctx, "sysrepo-get-devices-nil-translator")
51 return C.SR_ERR_OPERATION_FAILED
52 }
53
54 devices, err := core.AdapterInstance.GetDevices(ctx)
55 if err != nil {
56 logger.Errorw(ctx, "sysrepo-get-devices-translator-error", log.Fields{"err": err})
57 return C.SR_ERR_OPERATION_FAILED
58 }
59
60 err = updateYangTree(ctx, session, parent, devices)
61 if err != nil {
62 logger.Errorw(ctx, "sysrepo-get-devices-update-error", log.Fields{"err": err})
63 return C.SR_ERR_OPERATION_FAILED
64 }
65
66 logger.Info(ctx, "devices-information-request-served")
67
68 return C.SR_ERR_OK
69}
Elia Battistona1333642022-07-27 12:17:24 +000070
71//export get_services_cb
72func get_services_cb(session *C.sr_session_ctx_t, parent **C.lyd_node) C.sr_error_t {
73 //This function is a callback for the retrieval of devices from sysrepo
74 //The "export" comment instructs CGO to create a C function for it
75
76 ctx := context.Background()
77 logger.Debug(ctx, "processing-get-services-request")
78
79 if session == nil {
80 logger.Error(ctx, "sysrepo-get-services-null-session")
81 return C.SR_ERR_OPERATION_FAILED
82 }
83
84 if parent == nil {
85 logger.Error(ctx, "sysrepo-get-services-null-parent-node")
86 return C.SR_ERR_OPERATION_FAILED
87 }
88
89 if core.AdapterInstance == nil {
90 logger.Error(ctx, "sysrepo-get-services-nil-translator")
91 return C.SR_ERR_OPERATION_FAILED
92 }
93
94 services, err := core.AdapterInstance.GetServices(ctx)
95 if err != nil {
96 logger.Errorw(ctx, "sysrepo-get-services-translation-error", log.Fields{"err": err})
97 return C.SR_ERR_OPERATION_FAILED
98 }
99
100 err = updateYangTree(ctx, session, parent, services)
101 if err != nil {
102 logger.Errorw(ctx, "sysrepo-get-services-update-error", log.Fields{"err": err})
103 return C.SR_ERR_OPERATION_FAILED
104 }
105
106 logger.Info(ctx, "services-information-request-served")
107
108 return C.SR_ERR_OK
109}
110
111//export get_vlans_cb
112func get_vlans_cb(session *C.sr_session_ctx_t, parent **C.lyd_node) C.sr_error_t {
113 //This function is a callback for the retrieval of vlans from sysrepo
114 //The "export" comment instructs CGO to create a C function for it
115
116 ctx := context.Background()
117 logger.Debug(ctx, "processing-get-vlans-request")
118
119 if session == nil {
120 logger.Error(ctx, "sysrepo-get-vlans-null-session")
121 return C.SR_ERR_OPERATION_FAILED
122 }
123
124 if parent == nil {
125 logger.Error(ctx, "sysrepo-get-vlans-null-parent-node")
126 return C.SR_ERR_OPERATION_FAILED
127 }
128
129 if core.AdapterInstance == nil {
130 logger.Error(ctx, "sysrepo-get-vlans-nil-translator")
131 return C.SR_ERR_OPERATION_FAILED
132 }
133
134 vlans, err := core.AdapterInstance.GetVlans(ctx)
135 if err != nil {
136 logger.Errorw(ctx, "sysrepo-get-vlans-translation-error", log.Fields{"err": err})
137 return C.SR_ERR_OPERATION_FAILED
138 }
139
140 err = updateYangTree(ctx, session, parent, vlans)
141 if err != nil {
142 logger.Errorw(ctx, "sysrepo-get-vlans-update-error", log.Fields{"err": err})
143 return C.SR_ERR_OPERATION_FAILED
144 }
145
146 logger.Info(ctx, "vlans-information-request-served")
147
148 return C.SR_ERR_OK
149}
150
151//export get_bandwidth_profiles_cb
152func get_bandwidth_profiles_cb(session *C.sr_session_ctx_t, parent **C.lyd_node) C.sr_error_t {
153 //This function is a callback for the retrieval of bandwidth profiles from sysrepo
154 //The "export" comment instructs CGO to create a C function for it
155
156 ctx := context.Background()
157 logger.Debug(ctx, "processing-get-bandwidth-profiles-request")
158
159 if session == nil {
160 logger.Error(ctx, "sysrepo-get-bandwidth-profiles-null-session")
161 return C.SR_ERR_OPERATION_FAILED
162 }
163
164 if parent == nil {
165 logger.Error(ctx, "sysrepo-get-bandwidth-profiles-null-parent-node")
166 return C.SR_ERR_OPERATION_FAILED
167 }
168
169 if core.AdapterInstance == nil {
170 logger.Error(ctx, "sysrepo-get-bandwidth-profiles-nil-translator")
171 return C.SR_ERR_OPERATION_FAILED
172 }
173
174 bwProfiles, err := core.AdapterInstance.GetBandwidthProfiles(ctx)
175 if err != nil {
176 logger.Errorw(ctx, "sysrepo-get-bandwidth-profiles-translation-error", log.Fields{"err": err})
177 return C.SR_ERR_OPERATION_FAILED
178 }
179
180 err = updateYangTree(ctx, session, parent, bwProfiles)
181 if err != nil {
182 logger.Errorw(ctx, "sysrepo-get-bandwidth-profiles-update-error", log.Fields{"err": err})
183 return C.SR_ERR_OPERATION_FAILED
184 }
185
186 logger.Info(ctx, "bandwidth-profiles-information-request-served")
187
188 return C.SR_ERR_OK
189}
190
191//export edit_service_profiles_cb
192func edit_service_profiles_cb(editSession *C.sr_session_ctx_t, runningSession *C.sr_session_ctx_t, event C.sr_event_t) C.sr_error_t {
193 //This function is a callback for changes on service profiles
194 //The "export" comment instructs CGO to create a C function for it
195
196 if event != C.SR_EV_CHANGE {
197 return C.SR_ERR_OK
198 }
199
200 ctx := context.Background()
201 logger.Debug(ctx, "processing-service-profile-changes")
202
203 serviceNamesChanges, err := getChangesList(ctx, editSession, core.ServiceProfilesPath+"/service-profile/name")
204 if err != nil {
205 logger.Errorw(ctx, "cannot-get-service-profile-names-changes", log.Fields{"err": err})
206 return C.SR_ERR_OPERATION_FAILED
207 }
208
209 for _, n := range serviceNamesChanges {
210 switch n.Operation {
211 case C.SR_OP_CREATED:
212 if errCode := edit_service_create(ctx, editSession, runningSession, n.Value); errCode != C.SR_ERR_OK {
213 return errCode
214 }
215 case C.SR_OP_DELETED:
216 if errCode := edit_service_delete(ctx, editSession, runningSession, n.Value); errCode != C.SR_ERR_OK {
217 return errCode
218 }
219 default:
220 return C.SR_ERR_UNSUPPORTED
221 }
222 }
223
224 return C.SR_ERR_OK
225}
226
227func edit_service_create(ctx context.Context, editSession *C.sr_session_ctx_t, runningSession *C.sr_session_ctx_t, serviceName string) C.sr_error_t {
228 portName, err := getSingleChangeValue(ctx, editSession, fmt.Sprintf("%s/service-profile[name='%s']/ports/port/name", core.ServiceProfilesPath, serviceName))
229 if err != nil {
230 logger.Errorw(ctx, "cannot-get-service-profile-port-changes", log.Fields{"err": err, "service": serviceName})
231 return C.SR_ERR_OPERATION_FAILED
232 }
233
234 servicePortPath := core.GetServicePortPath(serviceName, portName)
235
236 tpId, err := getSingleChangeValue(ctx, editSession, servicePortPath+"/bbf-nt-service-profile-voltha:technology-profile-id")
237 if err != nil {
238 logger.Errorw(ctx, "cannot-get-service-profile-tp-id-change", log.Fields{"err": err, "service": serviceName})
239 return C.SR_ERR_OPERATION_FAILED
240 }
241
242 vlanName, err := getSingleChangeValue(ctx, editSession, servicePortPath+"/port-vlans/port-vlan/name")
243 if err != nil {
244 logger.Errorw(ctx, "cannot-get-service-profile-vlan-change", log.Fields{"err": err, "service": serviceName})
245 return C.SR_ERR_OPERATION_FAILED
246 }
247
248 vlansPath := core.GetVlansPath(vlanName)
249
250 sTag, err := getSingleChangeValue(ctx, editSession, vlansPath+"/ingress-rewrite/push-outer-tag/vlan-id")
251 if err != nil {
252 logger.Errorw(ctx, "cannot-get-service-profile-stag-changes", log.Fields{"err": err, "service": serviceName})
253 return C.SR_ERR_OPERATION_FAILED
254 }
255 if sTag == core.YangVlanIdAny {
256 sTag = strconv.Itoa(core.VolthaVlanIdAny)
257 }
258
259 cTag, err := getSingleChangeValue(ctx, editSession, vlansPath+"/ingress-rewrite/push-second-tag/vlan-id")
260 if err != nil {
261 logger.Errorw(ctx, "cannot-get-service-profile-stag-changes", log.Fields{"err": err, "service": serviceName})
262 return C.SR_ERR_OPERATION_FAILED
263 }
264 if cTag == core.YangVlanIdAny {
265 cTag = strconv.Itoa(core.VolthaVlanIdAny)
266 }
267
Elia Battistonaa7a0482022-08-17 12:24:02 +0000268 alias := core.ServiceAlias{
269 Key: core.ServiceKey{
270 Port: portName,
271 CTag: cTag,
272 STag: sTag,
273 TpId: tpId,
274 },
275 ServiceName: serviceName,
276 VlansName: vlanName,
277 }
Elia Battistona1333642022-07-27 12:17:24 +0000278 logger.Infow(ctx, "new-service-profile-information", log.Fields{
Elia Battistonaa7a0482022-08-17 12:24:02 +0000279 "serviceInfo": alias,
Elia Battistona1333642022-07-27 12:17:24 +0000280 })
281
282 if core.AdapterInstance == nil {
283 logger.Error(ctx, "sysrepo-service-changes-nil-translator")
284 return C.SR_ERR_OPERATION_FAILED
285 }
286
287 if err := core.AdapterInstance.ProvisionService(portName, sTag, cTag, tpId); err != nil {
288 logger.Errorw(ctx, "service-provisioning-error", log.Fields{
289 "service": serviceName,
290 "err": err,
291 })
292 return C.SR_ERR_OPERATION_FAILED
293 }
294
Elia Battistonaa7a0482022-08-17 12:24:02 +0000295 if err := core.AdapterInstance.StoreServiceAlias(ctx, alias); err != nil {
296 //Log the error but don't make the callback fail
297 //The service in ONOS has been provisioned succesfully and the datastore has to stay aligned
298 //A fallback alias will be created if service data is requested later
299 logger.Errorw(ctx, "cannot-store-service-alias-in-kvstore", log.Fields{"err": err, "service": serviceName})
300 }
301
Elia Battistona1333642022-07-27 12:17:24 +0000302 logger.Infow(ctx, "service-profile-creation-request-served", log.Fields{
303 "service": serviceName,
304 })
305
306 return C.SR_ERR_OK
307}
308
309func edit_service_delete(ctx context.Context, editSession *C.sr_session_ctx_t, runningSession *C.sr_session_ctx_t, serviceName string) C.sr_error_t {
310 portName, err := getDatastoreLeafValue(ctx, runningSession, fmt.Sprintf("%s/service-profile[name='%s']/ports/port/name", core.ServiceProfilesPath, serviceName))
311 if err != nil {
312 logger.Errorw(ctx, "cannot-get-service-profile-port-leaf", log.Fields{"err": err, "service": serviceName})
313 return C.SR_ERR_OPERATION_FAILED
314 }
315
316 servicePortPath := core.GetServicePortPath(serviceName, portName)
317
318 tpId, err := getDatastoreLeafValue(ctx, runningSession, servicePortPath+"/bbf-nt-service-profile-voltha:technology-profile-id")
319 if err != nil {
320 logger.Errorw(ctx, "cannot-get-service-profile-tp-id-leaf", log.Fields{"err": err, "service": serviceName})
321 return C.SR_ERR_OPERATION_FAILED
322 }
323
324 vlanName, err := getDatastoreLeafValue(ctx, runningSession, servicePortPath+"/port-vlans/port-vlan/name")
325 if err != nil {
326 logger.Errorw(ctx, "cannot-get-service-profile-vlan-leaf", log.Fields{"err": err, "service": serviceName})
327 return C.SR_ERR_OPERATION_FAILED
328 }
329
330 vlansPath := core.GetVlansPath(vlanName)
331
332 sTag, err := getDatastoreLeafValue(ctx, runningSession, vlansPath+"/ingress-rewrite/push-outer-tag/vlan-id")
333 if err != nil {
334 logger.Errorw(ctx, "cannot-get-service-profile-stag-leaf", log.Fields{"err": err, "service": serviceName})
335 return C.SR_ERR_OPERATION_FAILED
336 }
337 if sTag == core.YangVlanIdAny {
338 sTag = strconv.Itoa(core.VolthaVlanIdAny)
339 }
340
341 cTag, err := getDatastoreLeafValue(ctx, runningSession, vlansPath+"/ingress-rewrite/push-second-tag/vlan-id")
342 if err != nil {
343 logger.Errorw(ctx, "cannot-get-service-profile-stag-leaf", log.Fields{"err": err, "service": serviceName})
344 return C.SR_ERR_OPERATION_FAILED
345 }
346 if cTag == core.YangVlanIdAny {
347 cTag = strconv.Itoa(core.VolthaVlanIdAny)
348 }
349
Elia Battistonaa7a0482022-08-17 12:24:02 +0000350 alias := core.ServiceAlias{
351 Key: core.ServiceKey{
352 Port: portName,
353 CTag: cTag,
354 STag: sTag,
355 TpId: tpId,
356 },
357 ServiceName: serviceName,
358 VlansName: vlanName,
359 }
Elia Battistona1333642022-07-27 12:17:24 +0000360 logger.Infow(ctx, "service-profile-deletion-information", log.Fields{
Elia Battistonaa7a0482022-08-17 12:24:02 +0000361 "serviceInfo": alias,
Elia Battistona1333642022-07-27 12:17:24 +0000362 })
363
364 if err := core.AdapterInstance.RemoveService(portName, sTag, cTag, tpId); err != nil {
365 logger.Errorw(ctx, "service-removal-error", log.Fields{
366 "service": serviceName,
367 "err": err,
368 })
369 return C.SR_ERR_OPERATION_FAILED
370 }
371
Elia Battistonaa7a0482022-08-17 12:24:02 +0000372 if err := core.AdapterInstance.DeleteServiceAlias(ctx, alias.Key); err != nil {
373 //Log the error but don't make the callback fail
374 //The service in ONOS has been removed succesfully and the datastore has to stay aligned
375 //The only side effect is a dangling alias left in the KV store
376 logger.Errorw(ctx, "cannot-delete-service-alias-from-kvstore", log.Fields{"err": err, "service": serviceName})
377 }
378
Elia Battistona1333642022-07-27 12:17:24 +0000379 logger.Infow(ctx, "service-profile-removal-request-served", log.Fields{
380 "service": serviceName,
381 })
382
383 return C.SR_ERR_OK
384}
385
386//export edit_vlans_cb
387func edit_vlans_cb(editSession *C.sr_session_ctx_t, event C.sr_event_t) C.sr_error_t {
388 //This function is a callback for changes on VLANs
389 //The "export" comment instructs CGO to create a C function for it
390
391 if event != C.SR_EV_CHANGE {
392 return C.SR_ERR_OK
393 }
394
395 ctx := context.Background()
396 logger.Debug(ctx, "processing-vlans-changes")
397
398 vlanChanges, err := getChangesList(ctx, editSession, core.VlansPath+"//.")
399 if err != nil {
400 logger.Errorw(ctx, "cannot-get-vlans-changes", log.Fields{"err": err})
401 return C.SR_ERR_OPERATION_FAILED
402 }
403
404 for _, n := range vlanChanges {
405 //VLANs must be defined through creation (for service provisioning)
406 //or deletion (for service removal). Changes to the VLAN values
407 //are not supported, because VOLTHA does not support dynamic changes
408 //to the service.
409 switch n.Operation {
410 case C.SR_OP_CREATED:
411 case C.SR_OP_DELETED:
412 //Everything will be handled in the services callback
413 //Just approve the change here
414 return C.SR_ERR_OK
415 default:
416 return C.SR_ERR_UNSUPPORTED
417 }
418 }
419
420 return C.SR_ERR_OK
421}