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"
32 "gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
33 "gerrit.oran-osc.org/r/ric-plt/o1mediator/pkg/sbi"
37 #cgo LDFLAGS: -lsysrepo -lyang
42 #include <sysrepo/values.h>
47 var sbiClient sbi.SBIClientInterface
51 func NewNbi(s sbi.SBIClientInterface) *Nbi {
55 schemas: viper.GetStringSlice("nbi.schemas"),
56 cleanupChan: make(chan bool),
61 func (n *Nbi) Start() bool {
62 if ok := n.Setup(n.schemas); !ok {
63 log.Error("NBI: SYSREPO initialization failed, bailing out!")
66 log.Info("NBI: SYSREPO initialization done ... processing O1 requests!")
71 func (n *Nbi) Stop() {
72 C.sr_unsubscribe(n.subscription)
73 C.sr_session_stop(n.session)
74 C.sr_disconnect(n.connection)
76 log.Info("NBI: SYSREPO cleanup done gracefully!")
79 func (n *Nbi) Setup(schemas []string) bool {
80 rc := C.sr_connect(0, &n.connection)
81 if C.SR_ERR_OK != rc {
82 log.Error("NBI: sr_connect failed: %s", C.GoString(C.sr_strerror(rc)))
86 rc = C.sr_session_start(n.connection, C.SR_DS_RUNNING, &n.session)
87 if C.SR_ERR_OK != rc {
88 log.Error("NBI: sr_session_start failed: %s", C.GoString(C.sr_strerror(rc)))
93 if ok := n.DoSubscription(schemas); ok == true {
96 time.Sleep(time.Duration(5 * time.Second))
101 func (n *Nbi) DoSubscription(schemas []string) bool {
102 log.Info("Subscribing YANG modules ... %v", schemas)
104 for _, module := range schemas {
105 modName := C.CString(module)
106 defer C.free(unsafe.Pointer(modName))
108 if done := n.SubscribeModule(modName); !done {
112 return n.SubscribeStatusData()
115 func (n *Nbi) SubscribeModule(module *C.char) bool {
116 rc := C.sr_module_change_subscribe(n.session, module, nil, C.sr_module_change_cb(C.module_change_cb), nil, 0, 0, &n.subscription)
117 if C.SR_ERR_OK != rc {
118 log.Info("NBI: sr_module_change_subscribe failed: %s", C.GoString(C.sr_strerror(rc)))
124 func (n *Nbi) SubscribeStatusData() bool {
125 if ok := n.SubscribeStatus("o-ran-sc-ric-gnb-status-v1", "/o-ran-sc-ric-gnb-status-v1:ric/nodes"); !ok {
129 if ok := n.SubscribeStatus("o-ran-sc-ric-xapp-desc-v1", "/o-ran-sc-ric-xapp-desc-v1:ric/health"); !ok {
133 if ok := n.SubscribeStatus("o-ran-sc-ric-alarm-v1", "/o-ran-sc-ric-alarm-v1:ric/alarms"); !ok {
140 func (n *Nbi) SubscribeStatus(module, xpath string) bool {
141 mod := C.CString(module)
142 path := C.CString(xpath)
143 defer C.free(unsafe.Pointer(mod))
144 defer C.free(unsafe.Pointer(path))
146 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)
147 if C.SR_ERR_OK != rc {
148 log.Error("NBI: sr_oper_get_items_subscribe failed: %s", C.GoString(C.sr_strerror(rc)))
154 //export nbiModuleChangeCB
155 func nbiModuleChangeCB(session *C.sr_session_ctx_t, module *C.char, xpath *C.char, event C.sr_event_t, reqId C.int) C.int {
156 changedModule := C.GoString(module)
157 changedXpath := C.GoString(xpath)
159 log.Info("NBI: change event='%d' module=%s xpath=%s reqId=%d", event, changedModule, changedXpath, reqId)
160 if C.SR_EV_CHANGE != event {
161 log.Info("NBI: Changes finalized!")
165 if changedModule == "o-ran-sc-ric-xapp-desc-v1" {
166 configJson := C.yang_data_sr2json(session, module, event, &nbiClient.oper)
167 err := nbiClient.ManageXapps(changedModule, C.GoString(configJson), int(nbiClient.oper))
169 return C.SR_ERR_OPERATION_FAILED
173 if changedModule == "o-ran-sc-ric-ueec-config-v1" {
174 configJson := C.get_data_json(session, module)
175 err := nbiClient.ManageConfigmaps(changedModule, C.GoString(configJson), int(C.SR_OP_MODIFIED))
177 return C.SR_ERR_OPERATION_FAILED
184 func (n *Nbi) ManageXapps(module, configJson string, oper int) error {
185 log.Info("ManageXapps: module=%s configJson=%s", module, configJson)
187 if configJson == "" {
191 root := fmt.Sprintf("%s:ric", module)
192 jsonList, err := n.ParseJsonArray(configJson, root, "xapps", "xapp")
197 for _, m := range jsonList {
198 xappName := string(m.GetStringBytes("name"))
199 namespace := string(m.GetStringBytes("namespace"))
200 relName := string(m.GetStringBytes("release-name"))
201 version := string(m.GetStringBytes("version"))
203 desc := sbiClient.BuildXappDescriptor(xappName, namespace, relName, version)
205 case C.SR_OP_CREATED:
206 return sbiClient.DeployXapp(desc)
207 case C.SR_OP_DELETED:
208 return sbiClient.UndeployXapp(desc)
210 return errors.New(fmt.Sprintf("Operation '%d' not supported!", oper))
216 func (n *Nbi) ManageConfigmaps(module, configJson string, oper int) error {
217 log.Info("ManageConfig: module=%s configJson=%s", module, configJson)
219 if configJson == "" {
223 if oper != C.SR_OP_MODIFIED {
224 return errors.New(fmt.Sprintf("Operation '%d' not supported!", oper))
227 value, err := n.ParseJson(configJson)
229 log.Info("ParseJson failed with error: %v", oper)
233 root := fmt.Sprintf("%s:ric", module)
234 appName := string(value.GetStringBytes(root, "config", "name"))
235 namespace := string(value.GetStringBytes(root, "config", "namespace"))
236 control := value.Get(root, "config", "control").String()
239 err = json.Unmarshal([]byte(strings.ReplaceAll(control, "\\", "")), &f)
241 log.Info("json.Unmarshal failed: %v", err)
245 xappConfig := sbiClient.BuildXappConfig(appName, namespace, f)
246 return sbiClient.ModifyXappConfig(xappConfig)
249 func (n *Nbi) ParseJson(dsContent string) (*fastjson.Value, error) {
250 var p fastjson.Parser
251 v, err := p.Parse(dsContent)
253 log.Info("fastjson.Parser failed: %v", err)
258 func (n *Nbi) ParseJsonArray(dsContent, model, top, elem string) ([]*fastjson.Value, error) {
259 v, err := n.ParseJson(dsContent)
263 return v.GetArray(model, top, elem), nil
266 //export nbiGnbStateCB
267 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 {
268 mod := C.GoString(module)
269 log.Info("nbiGnbStateCB: module='%s' xpath='%s' rpath='%s' [id=%d]", mod, C.GoString(xpath), C.GoString(rpath), reqid)
271 if mod == "o-ran-sc-ric-xapp-desc-v1" {
272 podList, _ := sbiClient.GetAllPodStatus("ricxapp")
273 for _, pod := range podList {
274 path := fmt.Sprintf("/o-ran-sc-ric-xapp-desc-v1:ric/health/status[name='%s']", pod.Name)
275 nbiClient.CreateNewElement(session, parent, path, "name", path)
276 nbiClient.CreateNewElement(session, parent, path, "health", pod.Health)
277 nbiClient.CreateNewElement(session, parent, path, "status", pod.Status)
282 if mod == "o-ran-sc-ric-alarm-v1" {
283 alerts, _ := sbiClient.GetAlerts()
284 for _, alert := range alerts.Payload {
285 id := alert.Annotations["alarm_id"]
286 path := fmt.Sprintf("/o-ran-sc-ric-alarm-v1:ric/alarms/alarm[alarm-id='%s']", id)
287 nbiClient.CreateNewElement(session, parent, path, "alarm-id", id)
288 nbiClient.CreateNewElement(session, parent, path, "fault-text", alert.Alert.Labels["alertname"])
289 nbiClient.CreateNewElement(session, parent, path, "severity", alert.Alert.Labels["severity"])
290 nbiClient.CreateNewElement(session, parent, path, "status", alert.Alert.Labels["status"])
291 nbiClient.CreateNewElement(session, parent, path, "additional-info", alert.Annotations["additional_info"])
296 gnbs, err := xapp.Rnib.GetListGnbIds()
297 if err != nil || len(gnbs) == 0 {
298 log.Info("Rnib.GetListGnbIds() returned elementCount=%d err:%v", len(gnbs), err)
302 for _, gnb := range gnbs {
303 ranName := gnb.GetInventoryName()
304 info, err := xapp.Rnib.GetNodeb(ranName)
306 log.Error("GetNodeb() failed for ranName=%s: %v", ranName, err)
310 prot := nbiClient.E2APProt2Str(int(info.E2ApplicationProtocol))
311 connStat := nbiClient.ConnStatus2Str(int(info.ConnectionStatus))
312 ntype := nbiClient.NodeType2Str(int(info.NodeType))
314 log.Info("gNB info: %s -> %s %s %s -> %s %s", ranName, prot, connStat, ntype, gnb.GetGlobalNbId().GetPlmnId(), gnb.GetGlobalNbId().GetNbId())
316 path := fmt.Sprintf("/o-ran-sc-ric-gnb-status-v1:ric/nodes/node[ran-name='%s']", ranName)
317 nbiClient.CreateNewElement(session, parent, path, "ran-name", ranName)
318 nbiClient.CreateNewElement(session, parent, path, "ip", info.Ip)
319 nbiClient.CreateNewElement(session, parent, path, "port", fmt.Sprintf("%d", info.Port))
320 nbiClient.CreateNewElement(session, parent, path, "plmn-id", gnb.GetGlobalNbId().GetPlmnId())
321 nbiClient.CreateNewElement(session, parent, path, "nb-id", gnb.GetGlobalNbId().GetNbId())
322 nbiClient.CreateNewElement(session, parent, path, "e2ap-protocol", prot)
323 nbiClient.CreateNewElement(session, parent, path, "connection-status", connStat)
324 nbiClient.CreateNewElement(session, parent, path, "node", ntype)
329 func (n *Nbi) CreateNewElement(session *C.sr_session_ctx_t, parent **C.char, key, name, value string) {
330 basePath := fmt.Sprintf("%s/%s", key, name)
331 log.Info("%s -> %s", basePath, value)
333 cPath := C.CString(basePath)
334 defer C.free(unsafe.Pointer(cPath))
335 cValue := C.CString(value)
336 defer C.free(unsafe.Pointer(cValue))
338 C.create_new_path(session, parent, cPath, cValue)
341 func (n *Nbi) ConnStatus2Str(connStatus int) string {
344 return "not-specified"
348 return "disconnected"
350 return "setup-failed"
354 return "shutting-down"
358 return "not-specified"
361 func (n *Nbi) E2APProt2Str(prot int) string {
364 return "not-specified"
366 return "x2-setup-request"
368 return "endc-x2-setup-request"
370 return "not-specified"
373 func (n *Nbi) NodeType2Str(ntype int) string {
376 return "not-specified"
382 return "not-specified"