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: Module change callback - event='%d' module=%s xpath=%s reqId=%d", event, changedModule, changedXpath, reqId)
156 if C.SR_EV_CHANGE == event {
157 configJson := C.yang_data_sr2json(session, module, event, &nbiClient.oper)
158 err := nbiClient.ManageXapps(changedModule, C.GoString(configJson), int(nbiClient.oper))
160 return C.SR_ERR_OPERATION_FAILED
164 if C.SR_EV_DONE == event {
165 configJson := C.get_data_json(session, module)
166 err := nbiClient.ManageConfigmaps(changedModule, C.GoString(configJson), int(nbiClient.oper))
168 return C.SR_ERR_OPERATION_FAILED
175 func (n *Nbi) ManageXapps(module, configJson string, oper int) error {
176 log.Info("ManageXapps: module=%s configJson=%s", module, configJson)
178 if configJson == "" || module != "o-ran-sc-ric-xapp-desc-v1" {
182 root := fmt.Sprintf("%s:ric", module)
183 jsonList, err := n.ParseJsonArray(configJson, root, "xapps", "xapp")
188 for _, m := range jsonList {
189 xappName := string(m.GetStringBytes("name"))
190 namespace := string(m.GetStringBytes("namespace"))
191 relName := string(m.GetStringBytes("release-name"))
192 version := string(m.GetStringBytes("version"))
194 desc := sbiClient.BuildXappDescriptor(xappName, namespace, relName, version)
196 case C.SR_OP_CREATED:
197 return sbiClient.DeployXapp(desc)
198 case C.SR_OP_DELETED:
199 return sbiClient.UndeployXapp(desc)
201 return errors.New(fmt.Sprintf("Operation '%d' not supported!", oper))
207 func (n *Nbi) ManageConfigmaps(module, configJson string, oper int) error {
208 log.Info("ManageConfig: module=%s configJson=%s", module, configJson)
210 if configJson == "" || module != "o-ran-sc-ric-ueec-config-v1" {
214 if oper != C.SR_OP_MODIFIED {
215 return errors.New(fmt.Sprintf("Operation '%d' not supported!", oper))
218 value, err := n.ParseJson(configJson)
223 root := fmt.Sprintf("%s:ric", module)
224 appName := string(value.GetStringBytes(root, "config", "name"))
225 namespace := string(value.GetStringBytes(root, "config", "namespace"))
226 control := value.Get(root, "config", "control").String()
229 err = json.Unmarshal([]byte(strings.ReplaceAll(control, "\\", "")), &f)
231 log.Info("json.Unmarshal failed: %v", err)
235 xappConfig := sbiClient.BuildXappConfig(appName, namespace, f)
236 return sbiClient.ModifyXappConfig(xappConfig)
239 func (n *Nbi) ParseJson(dsContent string) (*fastjson.Value, error) {
240 var p fastjson.Parser
241 v, err := p.Parse(dsContent)
243 log.Info("fastjson.Parser failed: %v", err)
248 func (n *Nbi) ParseJsonArray(dsContent, model, top, elem string) ([]*fastjson.Value, error) {
249 v, err := n.ParseJson(dsContent)
253 return v.GetArray(model, top, elem), nil
256 //export nbiGnbStateCB
257 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 {
258 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)
260 if C.GoString(module) == "o-ran-sc-ric-xapp-desc-v1" {
261 log.Info("xApp health query not implemtented yet!")
265 gnbs, err := xapp.Rnib.GetListGnbIds()
266 if err != nil || len(gnbs) == 0 {
267 log.Info("Rnib.GetListGnbIds() returned elementCount=%d err:%v", len(gnbs), err)
271 for _, gnb := range gnbs {
272 ranName := gnb.GetInventoryName()
273 info, err := xapp.Rnib.GetNodeb(ranName)
275 log.Error("GetNodeb() failed for ranName=%s: %v", ranName, err)
279 prot := nbiClient.E2APProt2Str(int(info.E2ApplicationProtocol))
280 connStat := nbiClient.ConnStatus2Str(int(info.ConnectionStatus))
281 ntype := nbiClient.NodeType2Str(int(info.NodeType))
283 log.Info("gNB info: %s -> %s %s %s -> %s %s", ranName, prot, connStat, ntype, gnb.GetGlobalNbId().GetPlmnId(), gnb.GetGlobalNbId().GetNbId())
285 nbiClient.CreateNewElement(session, parent, ranName, "ran-name", ranName)
286 nbiClient.CreateNewElement(session, parent, ranName, "ip", info.Ip)
287 nbiClient.CreateNewElement(session, parent, ranName, "port", fmt.Sprintf("%d", info.Port))
288 nbiClient.CreateNewElement(session, parent, ranName, "plmn-id", gnb.GetGlobalNbId().GetPlmnId())
289 nbiClient.CreateNewElement(session, parent, ranName, "nb-id", gnb.GetGlobalNbId().GetNbId())
290 nbiClient.CreateNewElement(session, parent, ranName, "e2ap-protocol", prot)
291 nbiClient.CreateNewElement(session, parent, ranName, "connection-status", connStat)
292 nbiClient.CreateNewElement(session, parent, ranName, "node", ntype)
297 func (n *Nbi) CreateNewElement(session *C.sr_session_ctx_t, parent **C.char, key, name, value string) {
298 basePath := fmt.Sprintf("/o-ran-sc-ric-gnb-status-v1:ric/nodes/node[ran-name='%s']/%s", key, name)
299 log.Info("%s -> %s", basePath, value)
301 cPath := C.CString(basePath)
302 defer C.free(unsafe.Pointer(cPath))
303 cValue := C.CString(value)
304 defer C.free(unsafe.Pointer(cValue))
306 C.create_new_path(session, parent, cPath, cValue)
309 func (n *Nbi) ConnStatus2Str(connStatus int) string {
312 return "not-specified"
316 return "disconnected"
318 return "setup-failed"
322 return "shutting-down"
326 return "not-specified"
329 func (n *Nbi) E2APProt2Str(prot int) string {
332 return "not-specified"
334 return "x2-setup-request"
336 return "endc-x2-setup-request"
338 return "not-specified"
341 func (n *Nbi) NodeType2Str(ntype int) string {
344 return "not-specified"
350 return "not-specified"