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
52 func NewNbi(s sbi.SBIClientInterface) *Nbi {
56 schemas: viper.GetStringSlice("nbi.schemas"),
57 cleanupChan: make(chan bool),
62 func (n *Nbi) Start() bool {
63 if ok := n.Setup(n.schemas); !ok {
64 log.Error("NBI: SYSREPO initialization failed, bailing out!")
67 log.Info("NBI: SYSREPO initialization done ... processing O1 requests!")
72 func (n *Nbi) Stop() {
73 C.sr_unsubscribe(n.subscription)
74 C.sr_session_stop(n.session)
75 C.sr_disconnect(n.connection)
77 log.Info("NBI: SYSREPO cleanup done gracefully!")
80 func (n *Nbi) Setup(schemas []string) bool {
81 rc := C.sr_connect(0, &n.connection)
82 if C.SR_ERR_OK != rc {
83 log.Error("NBI: sr_connect failed: %s", C.GoString(C.sr_strerror(rc)))
87 rc = C.sr_session_start(n.connection, C.SR_DS_RUNNING, &n.session)
88 if C.SR_ERR_OK != rc {
89 log.Error("NBI: sr_session_start failed: %s", C.GoString(C.sr_strerror(rc)))
94 if ok := n.DoSubscription(schemas); ok == true {
97 time.Sleep(time.Duration(5 * time.Second))
102 func (n *Nbi) DoSubscription(schemas []string) bool {
103 log.Info("Subscribing YANG modules ... %v", schemas)
105 for _, module := range schemas {
106 modName := C.CString(module)
107 defer C.free(unsafe.Pointer(modName))
109 if done := n.SubscribeModule(modName); !done {
113 return n.SubscribeStatusData()
116 func (n *Nbi) SubscribeModule(module *C.char) bool {
117 rc := C.sr_module_change_subscribe(n.session, module, nil, C.sr_module_change_cb(C.module_change_cb), nil, 0, 0, &n.subscription)
118 if C.SR_ERR_OK != rc {
119 log.Info("NBI: sr_module_change_subscribe failed: %s", C.GoString(C.sr_strerror(rc)))
125 func (n *Nbi) SubscribeStatusData() bool {
126 if ok := n.SubscribeStatus("o-ran-sc-ric-gnb-status-v1", "/o-ran-sc-ric-gnb-status-v1:ric/nodes"); !ok {
130 if ok := n.SubscribeStatus("o-ran-sc-ric-xapp-desc-v1", "/o-ran-sc-ric-xapp-desc-v1:ric/health"); !ok {
134 if ok := n.SubscribeStatus("o-ran-sc-ric-alarm-v1", "/o-ran-sc-ric-alarm-v1:ric/alarms"); !ok {
141 func (n *Nbi) SubscribeStatus(module, xpath string) bool {
142 mod := C.CString(module)
143 path := C.CString(xpath)
144 defer C.free(unsafe.Pointer(mod))
145 defer C.free(unsafe.Pointer(path))
147 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)
148 if C.SR_ERR_OK != rc {
149 log.Error("NBI: sr_oper_get_items_subscribe failed: %s", C.GoString(C.sr_strerror(rc)))
155 //export nbiModuleChangeCB
156 func nbiModuleChangeCB(session *C.sr_session_ctx_t, module *C.char, xpath *C.char, event C.sr_event_t, reqId C.int) C.int {
157 changedModule := C.GoString(module)
158 changedXpath := C.GoString(xpath)
160 log.Info("NBI: change event='%d' module=%s xpath=%s reqId=%d", event, changedModule, changedXpath, reqId)
161 if C.SR_EV_CHANGE != event {
162 log.Info("NBI: Changes finalized!")
166 if changedModule == "o-ran-sc-ric-xapp-desc-v1" {
167 configJson := C.yang_data_sr2json(session, module, event, &nbiClient.oper)
168 err := nbiClient.ManageXapps(changedModule, C.GoString(configJson), int(nbiClient.oper))
170 return C.SR_ERR_OPERATION_FAILED
174 if changedModule == "o-ran-sc-ric-ueec-config-v1" {
175 configJson := C.get_data_json(session, module)
176 err := nbiClient.ManageConfigmaps(changedModule, C.GoString(configJson), int(C.SR_OP_MODIFIED))
178 return C.SR_ERR_OPERATION_FAILED
185 func (n *Nbi) ManageXapps(module, configJson string, oper int) error {
186 log.Info("ManageXapps: module=%s configJson=%s", module, configJson)
188 if configJson == "" {
192 root := fmt.Sprintf("%s:ric", module)
193 jsonList, err := n.ParseJsonArray(configJson, root, "xapps", "xapp")
198 for _, m := range jsonList {
199 xappName := string(m.GetStringBytes("name"))
200 namespace := string(m.GetStringBytes("namespace"))
201 relName := string(m.GetStringBytes("release-name"))
202 version := string(m.GetStringBytes("version"))
204 desc := sbiClient.BuildXappDescriptor(xappName, namespace, relName, version)
206 case C.SR_OP_CREATED:
207 return sbiClient.DeployXapp(desc)
208 case C.SR_OP_DELETED:
209 return sbiClient.UndeployXapp(desc)
211 return errors.New(fmt.Sprintf("Operation '%d' not supported!", oper))
217 func (n *Nbi) ManageConfigmaps(module, configJson string, oper int) error {
218 log.Info("ManageConfig: module=%s configJson=%s", module, configJson)
220 if configJson == "" {
224 if oper != C.SR_OP_MODIFIED {
225 return errors.New(fmt.Sprintf("Operation '%d' not supported!", oper))
228 value, err := n.ParseJson(configJson)
230 log.Info("ParseJson failed with error: %v", oper)
234 root := fmt.Sprintf("%s:ric", module)
235 appName := string(value.GetStringBytes(root, "config", "name"))
236 namespace := string(value.GetStringBytes(root, "config", "namespace"))
237 control := value.Get(root, "config", "control").String()
240 err = json.Unmarshal([]byte(strings.ReplaceAll(control, "\\", "")), &f)
242 log.Info("json.Unmarshal failed: %v", err)
246 xappConfig := sbiClient.BuildXappConfig(appName, namespace, f)
247 return sbiClient.ModifyXappConfig(xappConfig)
250 func (n *Nbi) ParseJson(dsContent string) (*fastjson.Value, error) {
251 var p fastjson.Parser
252 v, err := p.Parse(dsContent)
254 log.Info("fastjson.Parser failed: %v", err)
259 func (n *Nbi) ParseJsonArray(dsContent, model, top, elem string) ([]*fastjson.Value, error) {
260 v, err := n.ParseJson(dsContent)
264 return v.GetArray(model, top, elem), nil
267 //export nbiGnbStateCB
268 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 {
269 mod := C.GoString(module)
270 log.Info("nbiGnbStateCB: module='%s' xpath='%s' rpath='%s' [id=%d]", mod, C.GoString(xpath), C.GoString(rpath), reqid)
272 if mod == "o-ran-sc-ric-xapp-desc-v1" {
273 xappnamespace := os.Getenv("XAPP_NAMESPACE")
274 if xappnamespace == "" {
275 xappnamespace = "ricxapp"
277 podList, _ := sbiClient.GetAllPodStatus(xappnamespace)
279 for _, pod := range podList {
280 path := fmt.Sprintf("/o-ran-sc-ric-xapp-desc-v1:ric/health/status[name='%s']", pod.Name)
281 nbiClient.CreateNewElement(session, parent, path, "name", path)
282 nbiClient.CreateNewElement(session, parent, path, "health", pod.Health)
283 nbiClient.CreateNewElement(session, parent, path, "status", pod.Status)
288 if mod == "o-ran-sc-ric-alarm-v1" {
289 alerts, _ := sbiClient.GetAlerts()
290 for _, alert := range alerts.Payload {
291 id := alert.Annotations["alarm_id"]
292 path := fmt.Sprintf("/o-ran-sc-ric-alarm-v1:ric/alarms/alarm[alarm-id='%s']", id)
293 nbiClient.CreateNewElement(session, parent, path, "alarm-id", id)
294 nbiClient.CreateNewElement(session, parent, path, "fault-text", alert.Alert.Labels["alertname"])
295 nbiClient.CreateNewElement(session, parent, path, "severity", alert.Alert.Labels["severity"])
296 nbiClient.CreateNewElement(session, parent, path, "status", alert.Alert.Labels["status"])
297 nbiClient.CreateNewElement(session, parent, path, "additional-info", alert.Annotations["additional_info"])
302 gnbs, err := xapp.Rnib.GetListGnbIds()
303 if err != nil || len(gnbs) == 0 {
304 log.Info("Rnib.GetListGnbIds() returned elementCount=%d err:%v", len(gnbs), err)
308 for _, gnb := range gnbs {
309 ranName := gnb.GetInventoryName()
310 info, err := xapp.Rnib.GetNodeb(ranName)
312 log.Error("GetNodeb() failed for ranName=%s: %v", ranName, err)
316 prot := nbiClient.E2APProt2Str(int(info.E2ApplicationProtocol))
317 connStat := nbiClient.ConnStatus2Str(int(info.ConnectionStatus))
318 ntype := nbiClient.NodeType2Str(int(info.NodeType))
320 log.Info("gNB info: %s -> %s %s %s -> %s %s", ranName, prot, connStat, ntype, gnb.GetGlobalNbId().GetPlmnId(), gnb.GetGlobalNbId().GetNbId())
322 path := fmt.Sprintf("/o-ran-sc-ric-gnb-status-v1:ric/nodes/node[ran-name='%s']", ranName)
323 nbiClient.CreateNewElement(session, parent, path, "ran-name", ranName)
324 nbiClient.CreateNewElement(session, parent, path, "ip", info.Ip)
325 nbiClient.CreateNewElement(session, parent, path, "port", fmt.Sprintf("%d", info.Port))
326 nbiClient.CreateNewElement(session, parent, path, "plmn-id", gnb.GetGlobalNbId().GetPlmnId())
327 nbiClient.CreateNewElement(session, parent, path, "nb-id", gnb.GetGlobalNbId().GetNbId())
328 nbiClient.CreateNewElement(session, parent, path, "e2ap-protocol", prot)
329 nbiClient.CreateNewElement(session, parent, path, "connection-status", connStat)
330 nbiClient.CreateNewElement(session, parent, path, "node", ntype)
335 func (n *Nbi) CreateNewElement(session *C.sr_session_ctx_t, parent **C.char, key, name, value string) {
336 basePath := fmt.Sprintf("%s/%s", key, name)
337 log.Info("%s -> %s", basePath, value)
339 cPath := C.CString(basePath)
340 defer C.free(unsafe.Pointer(cPath))
341 cValue := C.CString(value)
342 defer C.free(unsafe.Pointer(cValue))
344 C.create_new_path(session, parent, cPath, cValue)
347 func (n *Nbi) ConnStatus2Str(connStatus int) string {
350 return "not-specified"
354 return "disconnected"
356 return "setup-failed"
360 return "shutting-down"
364 return "not-specified"
367 func (n *Nbi) E2APProt2Str(prot int) string {
370 return "not-specified"
372 return "x2-setup-request"
374 return "endc-x2-setup-request"
376 return "not-specified"
379 func (n *Nbi) NodeType2Str(ntype int) string {
382 return "not-specified"
388 return "not-specified"