2 ==================================================================================
3 Copyright (c) 2019 AT&T Intellectual Property.
4 Copyright (c) 2019 Nokia
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
10 http://www.apache.org/licenses/LICENSE-2.0
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17 ==================================================================================
26 "github.com/spf13/viper"
27 "github.com/valyala/fastjson"
33 "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
34 "gerrit.oran-osc.org/r/ric-plt/o1mediator/pkg/sbi"
38 #cgo LDFLAGS: -lsysrepo -lyang
43 #include <sysrepo/values.h>
48 var sbiClient sbi.SBIClientInterface
51 var rnib iRnib = xapp.Rnib
53 func NewNbi(s sbi.SBIClientInterface) *Nbi {
57 schemas: viper.GetStringSlice("nbi.schemas"),
58 cleanupChan: make(chan bool),
63 func (n *Nbi) Start() bool {
64 if ok := n.Setup(n.schemas); !ok {
65 log.Error("NBI: SYSREPO initialization failed, bailing out!")
68 log.Info("NBI: SYSREPO initialization done ... processing O1 requests!")
73 func (n *Nbi) Stop() {
74 C.sr_unsubscribe(n.subscription)
75 C.sr_session_stop(n.session)
76 C.sr_disconnect(n.connection)
78 log.Info("NBI: SYSREPO cleanup done gracefully!")
81 func (n *Nbi) Setup(schemas []string) bool {
82 rc := C.sr_connect(0, &n.connection)
83 if C.SR_ERR_OK != rc {
84 log.Error("NBI: sr_connect failed: %s", C.GoString(C.sr_strerror(rc)))
88 rc = C.sr_session_start(n.connection, C.SR_DS_RUNNING, &n.session)
89 if C.SR_ERR_OK != rc {
90 log.Error("NBI: sr_session_start failed: %s", C.GoString(C.sr_strerror(rc)))
95 if ok := n.DoSubscription(schemas); ok == true {
98 time.Sleep(time.Duration(5 * time.Second))
103 func (n *Nbi) DoSubscription(schemas []string) bool {
104 log.Info("Subscribing YANG modules ... %v", schemas)
106 for _, module := range schemas {
107 modName := C.CString(module)
108 defer C.free(unsafe.Pointer(modName))
110 if done := n.SubscribeModule(modName); !done {
114 return n.SubscribeStatusData()
117 func (n *Nbi) SubscribeModule(module *C.char) bool {
118 rc := C.sr_module_change_subscribe(n.session, module, nil, C.sr_module_change_cb(C.module_change_cb), nil, 0, 0, &n.subscription)
119 if C.SR_ERR_OK != rc {
120 log.Info("NBI: sr_module_change_subscribe failed: %s", C.GoString(C.sr_strerror(rc)))
126 func (n *Nbi) SubscribeStatusData() bool {
127 if ok := n.SubscribeStatus("o-ran-sc-ric-gnb-status-v1", "/o-ran-sc-ric-gnb-status-v1:ric/nodes"); !ok {
131 if ok := n.SubscribeStatus("o-ran-sc-ric-xapp-desc-v1", "/o-ran-sc-ric-xapp-desc-v1:ric/health"); !ok {
135 if ok := n.SubscribeStatus("o-ran-sc-ric-alarm-v1", "/o-ran-sc-ric-alarm-v1:ric/alarms"); !ok {
138 if ok := n.SubscribeStatus("o-ran-sc-ric-xapp-desc-v1", "/o-ran-sc-ric-xapp-desc-v1:ric/configuration"); !ok {
145 func (n *Nbi) SubscribeStatus(module, xpath string) bool {
146 mod := C.CString(module)
147 path := C.CString(xpath)
148 defer C.free(unsafe.Pointer(mod))
149 defer C.free(unsafe.Pointer(path))
151 rc := C.sr_oper_get_items_subscribe(n.session, mod, path, C.sr_oper_get_items_cb(C.gnb_status_cb), nil, 0, &n.subscription)
152 if C.SR_ERR_OK != rc {
153 log.Error("NBI: sr_oper_get_items_subscribe failed: %s", C.GoString(C.sr_strerror(rc)))
159 //export nbiModuleChangeCB
160 func nbiModuleChangeCB(session *C.sr_session_ctx_t, module *C.char, xpath *C.char, event C.sr_event_t, reqId C.int) C.int {
161 changedModule := C.GoString(module)
162 changedXpath := C.GoString(xpath)
164 log.Info("NBI: change event='%d' module=%s xpath=%s reqId=%d", event, changedModule, changedXpath, reqId)
165 if C.SR_EV_CHANGE != event {
166 log.Info("NBI: Changes finalized!")
170 if changedModule == "o-ran-sc-ric-xapp-desc-v1" {
171 configJson := C.yang_data_sr2json(session, module, event, &nbiClient.oper)
172 err := nbiClient.ManageXapps(changedModule, C.GoString(configJson), int(nbiClient.oper))
174 return C.SR_ERR_OPERATION_FAILED
178 if changedModule == "o-ran-sc-ric-ueec-config-v1" {
179 configJson := C.get_data_json(session, module)
180 err := nbiClient.ManageConfigmaps(changedModule, C.GoString(configJson), int(C.SR_OP_MODIFIED))
182 return C.SR_ERR_OPERATION_FAILED
189 func (n *Nbi) ManageXapps(module, configJson string, oper int) error {
190 log.Info("ManageXapps: module=%s configJson=%s", module, configJson)
192 if configJson == "" {
196 root := fmt.Sprintf("%s:ric", module)
197 jsonList, err := n.ParseJsonArray(configJson, root, "xapps", "xapp")
202 for _, m := range jsonList {
203 xappName := string(m.GetStringBytes("name"))
204 namespace := string(m.GetStringBytes("namespace"))
205 relName := string(m.GetStringBytes("release-name"))
206 version := string(m.GetStringBytes("version"))
208 desc := sbiClient.BuildXappDescriptor(xappName, namespace, relName, version)
210 case C.SR_OP_CREATED:
211 return sbiClient.DeployXapp(desc)
212 case C.SR_OP_DELETED:
213 return sbiClient.UndeployXapp(desc)
215 return errors.New(fmt.Sprintf("Operation '%d' not supported!", oper))
221 func (n *Nbi) ManageConfigmaps(module, configJson string, oper int) error {
222 log.Info("ManageConfig: module=%s configJson=%s", module, configJson)
224 if configJson == "" {
228 if oper != C.SR_OP_MODIFIED {
229 return errors.New(fmt.Sprintf("Operation '%d' not supported!", oper))
232 value, err := n.ParseJson(configJson)
234 log.Info("ParseJson failed with error: %v", oper)
238 root := fmt.Sprintf("%s:ric", module)
239 appName := string(value.GetStringBytes(root, "config", "name"))
240 namespace := string(value.GetStringBytes(root, "config", "namespace"))
241 controlVal := value.Get(root, "config", "control")
242 if controlVal == nil {
245 control := controlVal.String()
248 err = json.Unmarshal([]byte(strings.ReplaceAll(control, "\\", "")), &f)
250 log.Info("json.Unmarshal failed: %v", err)
254 xappConfig := sbiClient.BuildXappConfig(appName, namespace, f)
255 return sbiClient.ModifyXappConfig(xappConfig)
258 func (n *Nbi) ParseJson(dsContent string) (*fastjson.Value, error) {
259 var p fastjson.Parser
260 v, err := p.Parse(dsContent)
262 log.Info("fastjson.Parser failed: %v", err)
267 func (n *Nbi) ParseJsonArray(dsContent, model, top, elem string) ([]*fastjson.Value, error) {
268 v, err := n.ParseJson(dsContent)
272 return v.GetArray(model, top, elem), nil
275 //export nbiGnbStateCB
276 func nbiGnbStateCB(session *C.sr_session_ctx_t, module *C.char, xpath *C.char, rpath *C.char, reqid C.uint32_t, parent **C.char) C.int {
277 mod := C.GoString(module)
278 log.Info("nbiGnbStateCB: module='%s' xpath='%s' rpath='%s' [id=%d]", mod, C.GoString(xpath), C.GoString(rpath), reqid)
280 if mod == "o-ran-sc-ric-xapp-desc-v1" {
282 if C.GoString(xpath) == "/o-ran-sc-ric-xapp-desc-v1:ric/configuration" {
283 nbGetAllXappsDefCfg(session, parent)
287 xappnamespace := os.Getenv("XAPP_NAMESPACE")
288 if xappnamespace == "" {
289 xappnamespace = "ricxapp"
291 podList, _ := sbiClient.GetAllPodStatus(xappnamespace)
293 for _, pod := range podList {
294 path := fmt.Sprintf("/o-ran-sc-ric-xapp-desc-v1:ric/health/status[name='%s']", pod.Name)
295 nbiClient.CreateNewElement(session, parent, path, "name", path)
296 nbiClient.CreateNewElement(session, parent, path, "health", pod.Health)
297 nbiClient.CreateNewElement(session, parent, path, "status", pod.Status)
302 if mod == "o-ran-sc-ric-alarm-v1" {
303 if alerts, _ := sbiClient.GetAlerts(); alerts != nil {
304 for _, alert := range alerts.Payload {
305 id := alert.Annotations["alarm_id"]
306 path := fmt.Sprintf("/o-ran-sc-ric-alarm-v1:ric/alarms/alarm[alarm-id='%s']", id)
307 nbiClient.CreateNewElement(session, parent, path, "alarm-id", id)
308 nbiClient.CreateNewElement(session, parent, path, "fault-text", alert.Alert.Labels["alertname"])
309 nbiClient.CreateNewElement(session, parent, path, "severity", alert.Alert.Labels["severity"])
310 nbiClient.CreateNewElement(session, parent, path, "status", alert.Alert.Labels["status"])
311 nbiClient.CreateNewElement(session, parent, path, "additional-info", alert.Annotations["additional_info"])
317 gnbs, err := rnib.GetListGnbIds()
318 log.Info("Rnib.GetListGnbIds() returned elementCount=%d err:%v", len(gnbs), err)
319 if err == nil && len(gnbs) > 0 {
320 for _, gnb := range gnbs {
321 ranName := gnb.GetInventoryName()
322 info, err := rnib.GetNodeb(ranName)
324 log.Error("GetNodeb() failed for ranName=%s: %v", ranName, err)
328 prot := nbiClient.E2APProt2Str(int(info.E2ApplicationProtocol))
329 connStat := nbiClient.ConnStatus2Str(int(info.ConnectionStatus))
330 ntype := nbiClient.NodeType2Str(int(info.NodeType))
332 log.Info("gNB info: %s -> %s %s %s -> %s %s", ranName, prot, connStat, ntype, gnb.GetGlobalNbId().GetPlmnId(), gnb.GetGlobalNbId().GetNbId())
334 path := fmt.Sprintf("/o-ran-sc-ric-gnb-status-v1:ric/nodes/node[ran-name='%s']", ranName)
335 nbiClient.CreateNewElement(session, parent, path, "ran-name", ranName)
336 nbiClient.CreateNewElement(session, parent, path, "ip", info.Ip)
337 nbiClient.CreateNewElement(session, parent, path, "port", fmt.Sprintf("%d", info.Port))
338 nbiClient.CreateNewElement(session, parent, path, "plmn-id", gnb.GetGlobalNbId().GetPlmnId())
339 nbiClient.CreateNewElement(session, parent, path, "nb-id", gnb.GetGlobalNbId().GetNbId())
340 nbiClient.CreateNewElement(session, parent, path, "e2ap-protocol", prot)
341 nbiClient.CreateNewElement(session, parent, path, "connection-status", connStat)
342 nbiClient.CreateNewElement(session, parent, path, "node", ntype)
346 //Check if any Enbs are connected to RIC
347 enbs, err2 := rnib.GetListEnbIds()
348 log.Info("Rnib.GetListEnbIds() returned elementCount=%d err:%v", len(gnbs), err)
349 if err2 == nil || len(enbs) > 0 {
350 log.Info("Getting Enb details from list of Enbs")
351 for _, enb := range enbs {
352 ranName := enb.GetInventoryName()
353 info, err := rnib.GetNodeb(ranName)
355 log.Error("GetNodeb() failed for ranName=%s: %v", ranName, err)
359 prot := nbiClient.E2APProt2Str(int(info.E2ApplicationProtocol))
360 connStat := nbiClient.ConnStatus2Str(int(info.ConnectionStatus))
361 ntype := nbiClient.NodeType2Str(int(info.NodeType))
363 log.Info("eNB info: %s -> %s %s %s -> %s %s", ranName, prot, connStat, ntype, enb.GetGlobalNbId().GetPlmnId(), enb.GetGlobalNbId().GetNbId())
365 path := fmt.Sprintf("/o-ran-sc-ric-gnb-status-v1:ric/nodes/node[ran-name='%s']", ranName)
366 nbiClient.CreateNewElement(session, parent, path, "ran-name", ranName)
367 nbiClient.CreateNewElement(session, parent, path, "ip", info.Ip)
368 nbiClient.CreateNewElement(session, parent, path, "port", fmt.Sprintf("%d", info.Port))
369 nbiClient.CreateNewElement(session, parent, path, "plmn-id", enb.GetGlobalNbId().GetPlmnId())
370 nbiClient.CreateNewElement(session, parent, path, "nb-id", enb.GetGlobalNbId().GetNbId())
371 nbiClient.CreateNewElement(session, parent, path, "e2ap-protocol", prot)
372 nbiClient.CreateNewElement(session, parent, path, "connection-status", connStat)
373 nbiClient.CreateNewElement(session, parent, path, "node", ntype)
380 func (n *Nbi) CreateNewElement(session *C.sr_session_ctx_t, parent **C.char, key, name, value string) {
381 basePath := fmt.Sprintf("%s/%s", key, name)
382 log.Info("%s -> %s", basePath, value)
384 cPath := C.CString(basePath)
385 defer C.free(unsafe.Pointer(cPath))
386 cValue := C.CString(value)
387 defer C.free(unsafe.Pointer(cValue))
389 C.create_new_path(session, parent, cPath, cValue)
392 func (n *Nbi) ConnStatus2Str(connStatus int) string {
395 return "not-specified"
399 return "disconnected"
401 return "setup-failed"
405 return "shutting-down"
409 return "not-specified"
412 func (n *Nbi) E2APProt2Str(prot int) string {
415 return "not-specified"
417 return "x2-setup-request"
419 return "endc-x2-setup-request"
421 return "not-specified"
424 func (n *Nbi) NodeType2Str(ntype int) string {
427 return "not-specified"
433 return "not-specified"
436 func (n *Nbi) testModuleChangeCB(module string) bool {
437 var event C.sr_event_t = C.SR_EV_CHANGE
439 modName := C.CString(module)
440 defer C.free(unsafe.Pointer(modName))
442 if ret := nbiModuleChangeCB(n.session, modName, nil, event, reqID); ret != C.SR_ERR_OK {
448 func (n *Nbi) testModuleChangeCBDone(module string) bool {
449 var event C.sr_event_t = C.SR_EV_DONE
451 modName := C.CString(module)
452 defer C.free(unsafe.Pointer(modName))
454 if ret := nbiModuleChangeCB(n.session, modName, nil, event, reqID); ret != C.SR_ERR_OK {
460 func (n *Nbi) testGnbStateCB(module string) bool {
461 modName := C.CString(module)
462 defer C.free(unsafe.Pointer(modName))
463 reqID := C.uint32_t(100)
464 parent := make([]*C.char, 1)
466 if ret := nbiGnbStateCB(n.session, modName, nil, nil, reqID, &parent[0]); ret != C.SR_ERR_OK {
472 func nbGetAllXappsDefCfg(session *C.sr_session_ctx_t, parent **C.char) {
473 var xappNameList []string
474 var xappCfgList []string
476 //Get the default config of all deployed xapps from appgmr using rest api
477 xappNameList, xappCfgList = sbiClient.GetAllDeployedXappsConfig()
478 if xappCfgList == nil || len(xappCfgList) == 0 {
479 log.Error("GetAllDeployedXappsConfig() Failure")
482 log.Info("GetAllDeployedXappsConfig Success, recvd xapp config")
484 //Loop thru the list of recvd xapps for config
485 for i, xappCfg := range xappCfgList {
486 path := fmt.Sprintf("/o-ran-sc-ric-xapp-desc-v1:ric/configuration/xapps/xapp[name='%s']", xappNameList[i])
487 nbiClient.CreateNewElement(session, parent, path, "name", xappNameList[i])
488 nbiClient.CreateNewElement(session, parent, path, "config", xappCfg)
492 type iRnib interface {
493 GetListGnbIds() ([]*xapp.RNIBNbIdentity, xapp.RNIBIRNibError)
494 GetListEnbIds() ([]*xapp.RNIBNbIdentity, xapp.RNIBIRNibError)
495 GetNodeb(invName string) (*xapp.RNIBNodebInfo, xapp.RNIBIRNibError)