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 {
135 func (n *Nbi) SubscribeStatus(module, xpath string) bool {
136 mod := C.CString(module)
137 path := C.CString(xpath)
138 defer C.free(unsafe.Pointer(mod))
139 defer C.free(unsafe.Pointer(path))
141 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)
142 if C.SR_ERR_OK != rc {
143 log.Error("NBI: sr_oper_get_items_subscribe failed: %s", C.GoString(C.sr_strerror(rc)))
149 //export nbiModuleChangeCB
150 func nbiModuleChangeCB(session *C.sr_session_ctx_t, module *C.char, xpath *C.char, event C.sr_event_t, reqId C.int) C.int {
151 changedModule := C.GoString(module)
152 changedXpath := C.GoString(xpath)
154 log.Info("NBI: change event='%d' module=%s xpath=%s reqId=%d", event, changedModule, changedXpath, reqId)
155 if C.SR_EV_CHANGE != event {
156 log.Info("NBI: Changes finalized!")
160 if changedModule == "o-ran-sc-ric-xapp-desc-v1" {
161 configJson := C.yang_data_sr2json(session, module, event, &nbiClient.oper)
162 err := nbiClient.ManageXapps(changedModule, C.GoString(configJson), int(nbiClient.oper))
164 return C.SR_ERR_OPERATION_FAILED
168 if changedModule == "o-ran-sc-ric-ueec-config-v1" {
169 configJson := C.get_data_json(session, module)
170 err := nbiClient.ManageConfigmaps(changedModule, C.GoString(configJson), int(C.SR_OP_MODIFIED))
172 return C.SR_ERR_OPERATION_FAILED
179 func (n *Nbi) ManageXapps(module, configJson string, oper int) error {
180 log.Info("ManageXapps: module=%s configJson=%s", module, configJson)
182 if configJson == "" {
186 root := fmt.Sprintf("%s:ric", module)
187 jsonList, err := n.ParseJsonArray(configJson, root, "xapps", "xapp")
192 for _, m := range jsonList {
193 xappName := string(m.GetStringBytes("name"))
194 namespace := string(m.GetStringBytes("namespace"))
195 relName := string(m.GetStringBytes("release-name"))
196 version := string(m.GetStringBytes("version"))
198 desc := sbiClient.BuildXappDescriptor(xappName, namespace, relName, version)
200 case C.SR_OP_CREATED:
201 return sbiClient.DeployXapp(desc)
202 case C.SR_OP_DELETED:
203 return sbiClient.UndeployXapp(desc)
205 return errors.New(fmt.Sprintf("Operation '%d' not supported!", oper))
211 func (n *Nbi) ManageConfigmaps(module, configJson string, oper int) error {
212 log.Info("ManageConfig: module=%s configJson=%s", module, configJson)
214 if configJson == "" {
218 if oper != C.SR_OP_MODIFIED {
219 return errors.New(fmt.Sprintf("Operation '%d' not supported!", oper))
222 value, err := n.ParseJson(configJson)
224 log.Info("ParseJson failed with error: %v", oper)
228 root := fmt.Sprintf("%s:ric", module)
229 appName := string(value.GetStringBytes(root, "config", "name"))
230 namespace := string(value.GetStringBytes(root, "config", "namespace"))
231 control := value.Get(root, "config", "control").String()
234 err = json.Unmarshal([]byte(strings.ReplaceAll(control, "\\", "")), &f)
236 log.Info("json.Unmarshal failed: %v", err)
240 xappConfig := sbiClient.BuildXappConfig(appName, namespace, f)
241 return sbiClient.ModifyXappConfig(xappConfig)
244 func (n *Nbi) ParseJson(dsContent string) (*fastjson.Value, error) {
245 var p fastjson.Parser
246 v, err := p.Parse(dsContent)
248 log.Info("fastjson.Parser failed: %v", err)
253 func (n *Nbi) ParseJsonArray(dsContent, model, top, elem string) ([]*fastjson.Value, error) {
254 v, err := n.ParseJson(dsContent)
258 return v.GetArray(model, top, elem), nil
261 //export nbiGnbStateCB
262 func nbiGnbStateCB(session *C.sr_session_ctx_t, module *C.char, xpath *C.char, req_xpath *C.char, reqid C.uint32_t, parent **C.char) C.int {
263 log.Info("NBI: Module state data for module='%s' path='%s' rpath='%s' requested [id=%d]", C.GoString(module), C.GoString(xpath), C.GoString(req_xpath), reqid)
265 if C.GoString(module) == "o-ran-sc-ric-xapp-desc-v1" {
266 podList, _ := sbiClient.GetAllPodStatus("ricxapp")
267 for _, pod := range podList {
268 path := fmt.Sprintf("/o-ran-sc-ric-xapp-desc-v1:ric/health/status[name='%s']", pod.Name)
269 nbiClient.CreateNewElement(session, parent, path, "name", path)
270 nbiClient.CreateNewElement(session, parent, path, "health", pod.Health)
271 nbiClient.CreateNewElement(session, parent, path, "status", pod.Status)
276 gnbs, err := xapp.Rnib.GetListGnbIds()
277 if err != nil || len(gnbs) == 0 {
278 log.Info("Rnib.GetListGnbIds() returned elementCount=%d err:%v", len(gnbs), err)
282 for _, gnb := range gnbs {
283 ranName := gnb.GetInventoryName()
284 info, err := xapp.Rnib.GetNodeb(ranName)
286 log.Error("GetNodeb() failed for ranName=%s: %v", ranName, err)
290 prot := nbiClient.E2APProt2Str(int(info.E2ApplicationProtocol))
291 connStat := nbiClient.ConnStatus2Str(int(info.ConnectionStatus))
292 ntype := nbiClient.NodeType2Str(int(info.NodeType))
294 log.Info("gNB info: %s -> %s %s %s -> %s %s", ranName, prot, connStat, ntype, gnb.GetGlobalNbId().GetPlmnId(), gnb.GetGlobalNbId().GetNbId())
296 path := fmt.Sprintf("/o-ran-sc-ric-gnb-status-v1:ric/nodes/node[ran-name='%s']", ranName)
297 nbiClient.CreateNewElement(session, parent, path, "ran-name", ranName)
298 nbiClient.CreateNewElement(session, parent, path, "ip", info.Ip)
299 nbiClient.CreateNewElement(session, parent, path, "port", fmt.Sprintf("%d", info.Port))
300 nbiClient.CreateNewElement(session, parent, path, "plmn-id", gnb.GetGlobalNbId().GetPlmnId())
301 nbiClient.CreateNewElement(session, parent, path, "nb-id", gnb.GetGlobalNbId().GetNbId())
302 nbiClient.CreateNewElement(session, parent, path, "e2ap-protocol", prot)
303 nbiClient.CreateNewElement(session, parent, path, "connection-status", connStat)
304 nbiClient.CreateNewElement(session, parent, path, "node", ntype)
309 func (n *Nbi) CreateNewElement(session *C.sr_session_ctx_t, parent **C.char, key, name, value string) {
310 basePath := fmt.Sprintf("%s/%s", key, name)
311 log.Info("%s -> %s", basePath, value)
313 cPath := C.CString(basePath)
314 defer C.free(unsafe.Pointer(cPath))
315 cValue := C.CString(value)
316 defer C.free(unsafe.Pointer(cValue))
318 C.create_new_path(session, parent, cPath, cValue)
321 func (n *Nbi) ConnStatus2Str(connStatus int) string {
324 return "not-specified"
328 return "disconnected"
330 return "setup-failed"
334 return "shutting-down"
338 return "not-specified"
341 func (n *Nbi) E2APProt2Str(prot int) string {
344 return "not-specified"
346 return "x2-setup-request"
348 return "endc-x2-setup-request"
350 return "not-specified"
353 func (n *Nbi) NodeType2Str(ntype int) string {
356 return "not-specified"
362 return "not-specified"