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 {
142 func (n *Nbi) SubscribeStatus(module, xpath string) bool {
143 mod := C.CString(module)
144 path := C.CString(xpath)
145 defer C.free(unsafe.Pointer(mod))
146 defer C.free(unsafe.Pointer(path))
148 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)
149 if C.SR_ERR_OK != rc {
150 log.Error("NBI: sr_oper_get_items_subscribe failed: %s", C.GoString(C.sr_strerror(rc)))
156 //export nbiModuleChangeCB
157 func nbiModuleChangeCB(session *C.sr_session_ctx_t, module *C.char, xpath *C.char, event C.sr_event_t, reqId C.int) C.int {
158 changedModule := C.GoString(module)
159 changedXpath := C.GoString(xpath)
161 log.Info("NBI: change event='%d' module=%s xpath=%s reqId=%d", event, changedModule, changedXpath, reqId)
162 if C.SR_EV_CHANGE != event {
163 log.Info("NBI: Changes finalized!")
167 if changedModule == "o-ran-sc-ric-xapp-desc-v1" {
168 configJson := C.yang_data_sr2json(session, module, event, &nbiClient.oper)
169 err := nbiClient.ManageXapps(changedModule, C.GoString(configJson), int(nbiClient.oper))
171 return C.SR_ERR_OPERATION_FAILED
175 if changedModule == "o-ran-sc-ric-ueec-config-v1" {
176 configJson := C.get_data_json(session, module)
177 err := nbiClient.ManageConfigmaps(changedModule, C.GoString(configJson), int(C.SR_OP_MODIFIED))
179 return C.SR_ERR_OPERATION_FAILED
186 func (n *Nbi) ManageXapps(module, configJson string, oper int) error {
187 log.Info("ManageXapps: module=%s configJson=%s", module, configJson)
189 if configJson == "" {
193 root := fmt.Sprintf("%s:ric", module)
194 jsonList, err := n.ParseJsonArray(configJson, root, "xapps", "xapp")
199 for _, m := range jsonList {
200 xappName := string(m.GetStringBytes("name"))
201 namespace := string(m.GetStringBytes("namespace"))
202 relName := string(m.GetStringBytes("release-name"))
203 version := string(m.GetStringBytes("version"))
205 desc := sbiClient.BuildXappDescriptor(xappName, namespace, relName, version)
207 case C.SR_OP_CREATED:
208 return sbiClient.DeployXapp(desc)
209 case C.SR_OP_DELETED:
210 return sbiClient.UndeployXapp(desc)
212 return errors.New(fmt.Sprintf("Operation '%d' not supported!", oper))
218 func (n *Nbi) ManageConfigmaps(module, configJson string, oper int) error {
219 log.Info("ManageConfig: module=%s configJson=%s", module, configJson)
221 if configJson == "" {
225 if oper != C.SR_OP_MODIFIED {
226 return errors.New(fmt.Sprintf("Operation '%d' not supported!", oper))
229 value, err := n.ParseJson(configJson)
231 log.Info("ParseJson failed with error: %v", oper)
235 root := fmt.Sprintf("%s:ric", module)
236 appName := string(value.GetStringBytes(root, "config", "name"))
237 namespace := string(value.GetStringBytes(root, "config", "namespace"))
238 controlVal := value.Get(root, "config", "control")
239 if controlVal == nil {
242 control := controlVal.String()
245 err = json.Unmarshal([]byte(strings.ReplaceAll(control, "\\", "")), &f)
247 log.Info("json.Unmarshal failed: %v", err)
251 xappConfig := sbiClient.BuildXappConfig(appName, namespace, f)
252 return sbiClient.ModifyXappConfig(xappConfig)
255 func (n *Nbi) ParseJson(dsContent string) (*fastjson.Value, error) {
256 var p fastjson.Parser
257 v, err := p.Parse(dsContent)
259 log.Info("fastjson.Parser failed: %v", err)
264 func (n *Nbi) ParseJsonArray(dsContent, model, top, elem string) ([]*fastjson.Value, error) {
265 v, err := n.ParseJson(dsContent)
269 return v.GetArray(model, top, elem), nil
272 //export nbiGnbStateCB
273 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 {
274 mod := C.GoString(module)
275 log.Info("nbiGnbStateCB: module='%s' xpath='%s' rpath='%s' [id=%d]", mod, C.GoString(xpath), C.GoString(rpath), reqid)
277 if mod == "o-ran-sc-ric-xapp-desc-v1" {
278 xappnamespace := os.Getenv("XAPP_NAMESPACE")
279 if xappnamespace == "" {
280 xappnamespace = "ricxapp"
282 podList, _ := sbiClient.GetAllPodStatus(xappnamespace)
284 for _, pod := range podList {
285 path := fmt.Sprintf("/o-ran-sc-ric-xapp-desc-v1:ric/health/status[name='%s']", pod.Name)
286 nbiClient.CreateNewElement(session, parent, path, "name", path)
287 nbiClient.CreateNewElement(session, parent, path, "health", pod.Health)
288 nbiClient.CreateNewElement(session, parent, path, "status", pod.Status)
293 if mod == "o-ran-sc-ric-alarm-v1" {
294 if alerts, _ := sbiClient.GetAlerts(); alerts != nil {
295 for _, alert := range alerts.Payload {
296 id := alert.Annotations["alarm_id"]
297 path := fmt.Sprintf("/o-ran-sc-ric-alarm-v1:ric/alarms/alarm[alarm-id='%s']", id)
298 nbiClient.CreateNewElement(session, parent, path, "alarm-id", id)
299 nbiClient.CreateNewElement(session, parent, path, "fault-text", alert.Alert.Labels["alertname"])
300 nbiClient.CreateNewElement(session, parent, path, "severity", alert.Alert.Labels["severity"])
301 nbiClient.CreateNewElement(session, parent, path, "status", alert.Alert.Labels["status"])
302 nbiClient.CreateNewElement(session, parent, path, "additional-info", alert.Annotations["additional_info"])
308 gnbs, err := rnib.GetListGnbIds()
309 log.Info("Rnib.GetListGnbIds() returned elementCount=%d err:%v", len(gnbs), err)
310 if err == nil && len(gnbs) > 0 {
311 for _, gnb := range gnbs {
312 ranName := gnb.GetInventoryName()
313 info, err := rnib.GetNodeb(ranName)
315 log.Error("GetNodeb() failed for ranName=%s: %v", ranName, err)
319 prot := nbiClient.E2APProt2Str(int(info.E2ApplicationProtocol))
320 connStat := nbiClient.ConnStatus2Str(int(info.ConnectionStatus))
321 ntype := nbiClient.NodeType2Str(int(info.NodeType))
323 log.Info("gNB info: %s -> %s %s %s -> %s %s", ranName, prot, connStat, ntype, gnb.GetGlobalNbId().GetPlmnId(), gnb.GetGlobalNbId().GetNbId())
325 path := fmt.Sprintf("/o-ran-sc-ric-gnb-status-v1:ric/nodes/node[ran-name='%s']", ranName)
326 nbiClient.CreateNewElement(session, parent, path, "ran-name", ranName)
327 nbiClient.CreateNewElement(session, parent, path, "ip", info.Ip)
328 nbiClient.CreateNewElement(session, parent, path, "port", fmt.Sprintf("%d", info.Port))
329 nbiClient.CreateNewElement(session, parent, path, "plmn-id", gnb.GetGlobalNbId().GetPlmnId())
330 nbiClient.CreateNewElement(session, parent, path, "nb-id", gnb.GetGlobalNbId().GetNbId())
331 nbiClient.CreateNewElement(session, parent, path, "e2ap-protocol", prot)
332 nbiClient.CreateNewElement(session, parent, path, "connection-status", connStat)
333 nbiClient.CreateNewElement(session, parent, path, "node", ntype)
337 //Check if any Enbs are connected to RIC
338 enbs, err2 := rnib.GetListEnbIds()
339 log.Info("Rnib.GetListEnbIds() returned elementCount=%d err:%v", len(gnbs), err)
340 if err2 == nil || len(enbs) > 0 {
341 log.Info("Getting Enb details from list of Enbs")
342 for _, enb := range enbs {
343 ranName := enb.GetInventoryName()
344 info, err := rnib.GetNodeb(ranName)
346 log.Error("GetNodeb() failed for ranName=%s: %v", ranName, err)
350 prot := nbiClient.E2APProt2Str(int(info.E2ApplicationProtocol))
351 connStat := nbiClient.ConnStatus2Str(int(info.ConnectionStatus))
352 ntype := nbiClient.NodeType2Str(int(info.NodeType))
354 log.Info("eNB info: %s -> %s %s %s -> %s %s", ranName, prot, connStat, ntype, enb.GetGlobalNbId().GetPlmnId(), enb.GetGlobalNbId().GetNbId())
356 path := fmt.Sprintf("/o-ran-sc-ric-gnb-status-v1:ric/nodes/node[ran-name='%s']", ranName)
357 nbiClient.CreateNewElement(session, parent, path, "ran-name", ranName)
358 nbiClient.CreateNewElement(session, parent, path, "ip", info.Ip)
359 nbiClient.CreateNewElement(session, parent, path, "port", fmt.Sprintf("%d", info.Port))
360 nbiClient.CreateNewElement(session, parent, path, "plmn-id", enb.GetGlobalNbId().GetPlmnId())
361 nbiClient.CreateNewElement(session, parent, path, "nb-id", enb.GetGlobalNbId().GetNbId())
362 nbiClient.CreateNewElement(session, parent, path, "e2ap-protocol", prot)
363 nbiClient.CreateNewElement(session, parent, path, "connection-status", connStat)
364 nbiClient.CreateNewElement(session, parent, path, "node", ntype)
371 func (n *Nbi) CreateNewElement(session *C.sr_session_ctx_t, parent **C.char, key, name, value string) {
372 basePath := fmt.Sprintf("%s/%s", key, name)
373 log.Info("%s -> %s", basePath, value)
375 cPath := C.CString(basePath)
376 defer C.free(unsafe.Pointer(cPath))
377 cValue := C.CString(value)
378 defer C.free(unsafe.Pointer(cValue))
380 C.create_new_path(session, parent, cPath, cValue)
383 func (n *Nbi) ConnStatus2Str(connStatus int) string {
386 return "not-specified"
390 return "disconnected"
392 return "setup-failed"
396 return "shutting-down"
400 return "not-specified"
403 func (n *Nbi) E2APProt2Str(prot int) string {
406 return "not-specified"
408 return "x2-setup-request"
410 return "endc-x2-setup-request"
412 return "not-specified"
415 func (n *Nbi) NodeType2Str(ntype int) string {
418 return "not-specified"
424 return "not-specified"
427 func (n *Nbi) testModuleChangeCB(module string) bool {
428 var event C.sr_event_t = C.SR_EV_CHANGE
430 modName := C.CString(module)
431 defer C.free(unsafe.Pointer(modName))
433 if ret := nbiModuleChangeCB(n.session, modName, nil, event, reqID); ret != C.SR_ERR_OK {
439 func (n *Nbi) testModuleChangeCBDone(module string) bool {
440 var event C.sr_event_t = C.SR_EV_DONE
442 modName := C.CString(module)
443 defer C.free(unsafe.Pointer(modName))
445 if ret := nbiModuleChangeCB(n.session, modName, nil, event, reqID); ret != C.SR_ERR_OK {
451 func (n *Nbi) testGnbStateCB(module string) bool {
452 modName := C.CString(module)
453 defer C.free(unsafe.Pointer(modName))
454 reqID := C.uint32_t(100)
455 parent := make([]*C.char, 1)
457 if ret := nbiGnbStateCB(n.session, modName, nil, nil, reqID, &parent[0]); ret != C.SR_ERR_OK {
463 type iRnib interface {
464 GetListGnbIds() ([]*xapp.RNIBNbIdentity, xapp.RNIBIRNibError)
465 GetListEnbIds() ([]*xapp.RNIBNbIdentity, xapp.RNIBIRNibError)
466 GetNodeb(invName string) (*xapp.RNIBNodebInfo, xapp.RNIBIRNibError)