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 if err != nil || len(gnbs) == 0 {
310 log.Info("Rnib.GetListGnbIds() returned elementCount=%d err:%v", len(gnbs), err)
314 for _, gnb := range gnbs {
315 ranName := gnb.GetInventoryName()
316 info, err := rnib.GetNodeb(ranName)
318 log.Error("GetNodeb() failed for ranName=%s: %v", ranName, err)
322 prot := nbiClient.E2APProt2Str(int(info.E2ApplicationProtocol))
323 connStat := nbiClient.ConnStatus2Str(int(info.ConnectionStatus))
324 ntype := nbiClient.NodeType2Str(int(info.NodeType))
326 log.Info("gNB info: %s -> %s %s %s -> %s %s", ranName, prot, connStat, ntype, gnb.GetGlobalNbId().GetPlmnId(), gnb.GetGlobalNbId().GetNbId())
328 path := fmt.Sprintf("/o-ran-sc-ric-gnb-status-v1:ric/nodes/node[ran-name='%s']", ranName)
329 nbiClient.CreateNewElement(session, parent, path, "ran-name", ranName)
330 nbiClient.CreateNewElement(session, parent, path, "ip", info.Ip)
331 nbiClient.CreateNewElement(session, parent, path, "port", fmt.Sprintf("%d", info.Port))
332 nbiClient.CreateNewElement(session, parent, path, "plmn-id", gnb.GetGlobalNbId().GetPlmnId())
333 nbiClient.CreateNewElement(session, parent, path, "nb-id", gnb.GetGlobalNbId().GetNbId())
334 nbiClient.CreateNewElement(session, parent, path, "e2ap-protocol", prot)
335 nbiClient.CreateNewElement(session, parent, path, "connection-status", connStat)
336 nbiClient.CreateNewElement(session, parent, path, "node", ntype)
341 func (n *Nbi) CreateNewElement(session *C.sr_session_ctx_t, parent **C.char, key, name, value string) {
342 basePath := fmt.Sprintf("%s/%s", key, name)
343 log.Info("%s -> %s", basePath, value)
345 cPath := C.CString(basePath)
346 defer C.free(unsafe.Pointer(cPath))
347 cValue := C.CString(value)
348 defer C.free(unsafe.Pointer(cValue))
350 C.create_new_path(session, parent, cPath, cValue)
353 func (n *Nbi) ConnStatus2Str(connStatus int) string {
356 return "not-specified"
360 return "disconnected"
362 return "setup-failed"
366 return "shutting-down"
370 return "not-specified"
373 func (n *Nbi) E2APProt2Str(prot int) string {
376 return "not-specified"
378 return "x2-setup-request"
380 return "endc-x2-setup-request"
382 return "not-specified"
385 func (n *Nbi) NodeType2Str(ntype int) string {
388 return "not-specified"
394 return "not-specified"
397 func (n *Nbi) testModuleChangeCB(module string) bool {
398 var event C.sr_event_t = C.SR_EV_CHANGE
400 modName := C.CString(module)
401 defer C.free(unsafe.Pointer(modName))
403 if ret := nbiModuleChangeCB(n.session, modName, nil, event, reqID); ret != C.SR_ERR_OK {
409 func (n *Nbi) testModuleChangeCBDone(module string) bool {
410 var event C.sr_event_t = C.SR_EV_DONE
412 modName := C.CString(module)
413 defer C.free(unsafe.Pointer(modName))
415 if ret := nbiModuleChangeCB(n.session, modName, nil, event, reqID); ret != C.SR_ERR_OK {
421 func (n *Nbi) testGnbStateCB(module string) bool {
422 modName := C.CString(module)
423 defer C.free(unsafe.Pointer(modName))
424 reqID := C.uint32_t(100)
425 parent := make([]*C.char, 1)
427 if ret := nbiGnbStateCB(n.session, modName, nil, nil, reqID, &parent[0]); ret != C.SR_ERR_OK {
433 type iRnib interface {
434 GetListGnbIds() ([]*xapp.RNIBNbIdentity, xapp.RNIBIRNibError)
435 GetNodeb(invName string) (*xapp.RNIBNodebInfo, xapp.RNIBIRNibError)