RUN \
cd /opt/dev && \
git clone https://github.com/sysrepo/sysrepo.git && \
- cd sysrepo && mkdir build && cd build && \
+ cd sysrepo && sed -i -e 's/2000/30000/g' src/common.h.in && \
+ mkdir build && cd build && \
cmake -DCMAKE_BUILD_TYPE:String="Release" -DENABLE_TESTS=OFF -DREPOSITORY_LOC:PATH=/etc/sysrepo .. && \
make -j2 && \
make install && make sr_clean && \
package main
import (
- "log"
- "strings"
- "time"
- "os"
"bytes"
+ "encoding/json"
"flag"
"fmt"
- "encoding/json"
- "io/ioutil"
"github.com/Juniper/go-netconf/netconf"
xj "github.com/basgys/goxml2json"
"golang.org/x/crypto/ssh"
+ "io/ioutil"
+ "log"
+ "os"
+ "strings"
+ "time"
)
var (
- host = flag.String("host", "localhost", "Hostname")
- username = flag.String("username", "netconf", "Username")
- passwd = flag.String("password", "netconf", "Password")
- source = flag.String("source", "running", "Source datastore")
- target = flag.String("target", "running", "Target datastore")
- subtree = flag.String("subtree", "netconf-server", "Subtree or module to select")
- namespace = flag.String("namespace", "urn:o-ran:ric:gnb-status:1.0", "XML namespace")
- file = flag.String("file", "", "Configuration file")
- action = flag.String("action", "get", "Netconf command: get or edit")
- timeout = flag.Int("timeout", 30, "Timeout")
+ host = flag.String("host", "localhost", "Hostname")
+ username = flag.String("username", "netconf", "Username")
+ passwd = flag.String("password", "netconf", "Password")
+ source = flag.String("source", "running", "Source datastore")
+ target = flag.String("target", "running", "Target datastore")
+ subtree = flag.String("subtree", "ric", "Subtree or module to select")
+ namespace = flag.String("namespace", "urn:o-ran:ric:xapp-desc:1.0", "XML namespace")
+ file = flag.String("file", "", "Configuration file")
+ action = flag.String("action", "get", "Netconf command: get or edit")
+ timeout = flag.Int("timeout", 30, "Timeout")
getStateXml = "<get><filter type=\"subtree\"><ric xmlns=\"%s\"></ric></filter></get>"
getConfigXml = "<get-config><source><%s/></source><filter type=\"subtree\"><%s/></filter></get-config>"
func main() {
defer func() { // catch or finally
- if err := recover(); err != nil { //catch
- fmt.Fprintf(os.Stderr, "Something went wrong: %v\n", err)
- os.Exit(1)
- }
+ if err := recover(); err != nil { //catch
+ fmt.Fprintf(os.Stderr, "Something went wrong: %v\n", err)
+ os.Exit(1)
+ }
}()
if flag.Parse(); flag.Parsed() == false {
package main
import (
+ "fmt"
"os"
"os/signal"
"syscall"
- "fmt"
"gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
"gerrit.oran-osc.org/r/ric-plt/o1mediator/pkg/nbi"
changedModule := C.GoString(module)
changedXpath := C.GoString(xpath)
- log.Info("NBI: Module change callback - event='%d' module=%s xpath=%s reqId=%d", event, changedModule, changedXpath, reqId)
+ log.Info("NBI: change event='%d' module=%s xpath=%s reqId=%d", event, changedModule, changedXpath, reqId)
+ if C.SR_EV_CHANGE != event {
+ log.Info("NBI: Changes finalized!")
+ return C.SR_ERR_OK
+ }
- if C.SR_EV_CHANGE == event {
+ if changedModule == "o-ran-sc-ric-xapp-desc-v1" {
configJson := C.yang_data_sr2json(session, module, event, &nbiClient.oper)
err := nbiClient.ManageXapps(changedModule, C.GoString(configJson), int(nbiClient.oper))
if err != nil {
}
}
- if C.SR_EV_DONE == event {
+ if changedModule == "o-ran-sc-ric-ueec-config-v1" {
configJson := C.get_data_json(session, module)
- err := nbiClient.ManageConfigmaps(changedModule, C.GoString(configJson), int(nbiClient.oper))
+ err := nbiClient.ManageConfigmaps(changedModule, C.GoString(configJson), int(C.SR_OP_MODIFIED))
if err != nil {
return C.SR_ERR_OPERATION_FAILED
}
func (n *Nbi) ManageXapps(module, configJson string, oper int) error {
log.Info("ManageXapps: module=%s configJson=%s", module, configJson)
- if configJson == "" || module != "o-ran-sc-ric-xapp-desc-v1" {
+ if configJson == "" {
return nil
}
func (n *Nbi) ManageConfigmaps(module, configJson string, oper int) error {
log.Info("ManageConfig: module=%s configJson=%s", module, configJson)
- if configJson == "" || module != "o-ran-sc-ric-ueec-config-v1" {
+ if configJson == "" {
return nil
}
value, err := n.ParseJson(configJson)
if err != nil {
+ log.Info("ParseJson failed with error: %v", oper)
return err
}
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)
if C.GoString(module) == "o-ran-sc-ric-xapp-desc-v1" {
- log.Info("xApp health query not implemtented yet!")
+ podList, _ := sbiClient.GetAllPodStatus("ricxapp")
+ for _, pod := range podList {
+ path := fmt.Sprintf("/o-ran-sc-ric-xapp-desc-v1:ric/health/status[name='%s']", pod.Name)
+ nbiClient.CreateNewElement(session, parent, path, "name", path)
+ nbiClient.CreateNewElement(session, parent, path, "health", pod.Health)
+ nbiClient.CreateNewElement(session, parent, path, "status", pod.Status)
+ }
return C.SR_ERR_OK
}
log.Info("gNB info: %s -> %s %s %s -> %s %s", ranName, prot, connStat, ntype, gnb.GetGlobalNbId().GetPlmnId(), gnb.GetGlobalNbId().GetNbId())
- nbiClient.CreateNewElement(session, parent, ranName, "ran-name", ranName)
- nbiClient.CreateNewElement(session, parent, ranName, "ip", info.Ip)
- nbiClient.CreateNewElement(session, parent, ranName, "port", fmt.Sprintf("%d", info.Port))
- nbiClient.CreateNewElement(session, parent, ranName, "plmn-id", gnb.GetGlobalNbId().GetPlmnId())
- nbiClient.CreateNewElement(session, parent, ranName, "nb-id", gnb.GetGlobalNbId().GetNbId())
- nbiClient.CreateNewElement(session, parent, ranName, "e2ap-protocol", prot)
- nbiClient.CreateNewElement(session, parent, ranName, "connection-status", connStat)
- nbiClient.CreateNewElement(session, parent, ranName, "node", ntype)
+ path := fmt.Sprintf("/o-ran-sc-ric-gnb-status-v1:ric/nodes/node[ran-name='%s']", ranName)
+ nbiClient.CreateNewElement(session, parent, path, "ran-name", ranName)
+ nbiClient.CreateNewElement(session, parent, path, "ip", info.Ip)
+ nbiClient.CreateNewElement(session, parent, path, "port", fmt.Sprintf("%d", info.Port))
+ nbiClient.CreateNewElement(session, parent, path, "plmn-id", gnb.GetGlobalNbId().GetPlmnId())
+ nbiClient.CreateNewElement(session, parent, path, "nb-id", gnb.GetGlobalNbId().GetNbId())
+ nbiClient.CreateNewElement(session, parent, path, "e2ap-protocol", prot)
+ nbiClient.CreateNewElement(session, parent, path, "connection-status", connStat)
+ nbiClient.CreateNewElement(session, parent, path, "node", ntype)
}
return C.SR_ERR_OK
}
func (n *Nbi) CreateNewElement(session *C.sr_session_ctx_t, parent **C.char, key, name, value string) {
- basePath := fmt.Sprintf("/o-ran-sc-ric-gnb-status-v1:ric/nodes/node[ran-name='%s']/%s", key, name)
+ basePath := fmt.Sprintf("%s/%s", key, name)
log.Info("%s -> %s", basePath, value)
cPath := C.CString(basePath)
package nbi
import (
- "os"
- "time"
"encoding/json"
- "testing"
- "net"
- "net/http"
- "net/http/httptest"
"github.com/stretchr/testify/assert"
+ "net"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "testing"
+ "time"
apimodel "gerrit.oran-osc.org/r/ric-plt/o1mediator/pkg/appmgrmodel"
"gerrit.oran-osc.org/r/ric-plt/o1mediator/pkg/sbi"
-
)
var XappConfig = `{
}
}`
+var kpodOutput = `
+NAME READY STATUS RESTARTS AGE
+ricxapp-ueec-7bfdd587db-2jl9j 1/1 Running 53 29d
+ricxapp-anr-6748846478-8hmtz 1-1 Running 1 29d
+ricxapp-dualco-7f76f65c99-5p6c6 0/1 Running 1 29d
+`
+
var n *Nbi
// Test cases
assert.Equal(t, true, err == nil)
}
+func TestGetAllPodStatus(t *testing.T) {
+ sbi.CommandExec = func(args string) (out string, err error) {
+ assert.Equal(t, "/usr/local/bin/kubectl get pod -n ricxapp", args)
+ return kpodOutput, nil
+ }
+
+ expectedPodList := []sbi.PodStatus{
+ sbi.PodStatus{
+ Name: "ueec",
+ Health: "healthy",
+ Status: "Running",
+ },
+ sbi.PodStatus{
+ Name: "anr",
+ Health: "unavailable",
+ Status: "Running",
+ },
+ sbi.PodStatus{
+ Name: "dualco",
+ Health: "unhealthy",
+ Status: "Running",
+ },
+ }
+
+ podList, err := sbiClient.GetAllPodStatus("ricxapp")
+ assert.Equal(t, true, err == nil)
+ assert.Equal(t, podList, expectedPodList)
+}
+
func TestErrorCases(t *testing.T) {
// Invalid config
err := n.ManageXapps("o-ran-sc-ric-xapp-desc-v1", "", 2)
err = n.ManageConfigmaps("o-ran-sc-ric-ueec-config-v1", "", 1)
assert.Equal(t, true, err == nil)
- // Invalid module
- err = n.ManageConfigmaps("", "{}", 1)
- assert.Equal(t, true, err == nil)
-
- // Unexpected module
- err = n.ManageConfigmaps("o-ran-sc-ric-xapp-desc-v1", "{}", 0)
- assert.Equal(t, true, err == nil)
-
// Invalid operation
err = n.ManageConfigmaps("o-ran-sc-ric-ueec-config-v1", "{}", 0)
assert.Equal(t, true, err != nil)
func CreateHTTPServer(t *testing.T, method, url string, status int, respData interface{}) *httptest.Server {
l, err := net.Listen("tcp", "localhost:8080")
if err != nil {
- t.Error("Failed to create listener: " + err.Error())
+ t.Error("Failed to create listener: " + err.Error())
}
ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, r.Method, method)
func ConfigMatcher(result, expected *apimodel.XAppConfig) bool {
if *result.Metadata.XappName == *expected.Metadata.XappName &&
- *result.Metadata.Namespace == *expected.Metadata.Namespace {
+ *result.Metadata.Namespace == *expected.Metadata.Namespace {
return true
}
return false
package sbi
import (
+ "bytes"
+ "fmt"
httptransport "github.com/go-openapi/runtime/client"
"github.com/go-openapi/strfmt"
+ "os/exec"
+ "regexp"
+ "strings"
"time"
"gerrit.o-ran-sc.org/r/ric-plt/xapp-frame/pkg/xapp"
apimodel "gerrit.oran-osc.org/r/ric-plt/o1mediator/pkg/appmgrmodel"
)
+type PodStatus struct {
+ Name string
+ Health string
+ Status string
+}
+
var log = xapp.Logger
func NewSBIClient(host, baseUrl string, prot []string, timo int) *SBIClient {
}
return err
}
+
+func (s *SBIClient) GetAllPodStatus(namespace string) ([]PodStatus, error) {
+ output, err := s.RunCommand(fmt.Sprintf("/usr/local/bin/kubectl get pod -n %s", namespace))
+ if err != nil {
+ return []PodStatus{}, err
+ }
+
+ podStatusList := []PodStatus{}
+ var readyStr string
+ re := regexp.MustCompile(fmt.Sprintf(`%s-.*`, namespace))
+ podList := re.FindAllStringSubmatch(string(output), -1)
+ if podList != nil {
+ for _, pod := range podList {
+ p := PodStatus{}
+ fmt.Sscanf(pod[0], "%s %s %s", &p.Name, &readyStr, &p.Status)
+ p.Name = strings.Split(p.Name, "-")[1]
+ p.Health = s.GetHealthState(readyStr)
+
+ podStatusList = append(podStatusList, p)
+ }
+ }
+ return podStatusList, nil
+}
+
+func (s *SBIClient) GetHealthState(ready string) (state string) {
+ result := strings.Split(ready, "/")
+ if len(result) < 2 {
+ return "unavailable"
+ }
+
+ if result[0] == result[1] {
+ state = "healthy"
+ } else {
+ state = "unhealthy"
+ }
+ return
+}
+
+func (s *SBIClient) RunCommand(args string) (string, error) {
+ return CommandExec(args)
+}
+
+var CommandExec = func(args string) (string, error) {
+ cmd := exec.Command("/bin/sh", "-c", args)
+ var stdout bytes.Buffer
+ var stderr bytes.Buffer
+ cmd.Stdout = &stdout
+ cmd.Stderr = &stderr
+
+ xapp.Logger.Debug("Running command: '%s'", cmd)
+ if err := cmd.Run(); err != nil {
+ xapp.Logger.Error("Command failed (%s): %v - %s", cmd, err.Error(), stderr.String())
+ return "", err
+ }
+ xapp.Logger.Debug("Command executed successfully!")
+ return stdout.String(), nil
+}
BuildXappConfig(name, namespace string, configData interface{}) *apimodel.XAppConfig
ModifyXappConfig(xappConfig *apimodel.XAppConfig) error
+
+ GetAllPodStatus(namespace string) ([]PodStatus, error)
}
"xApp descriptor";
}
+ typedef health-status {
+ type enumeration {
+ enum unavailable {
+ description
+ "The health status not available";
+ }
+ enum healthy {
+ description
+ "The xApp is healthy";
+ }
+ enum unhealthy {
+ description
+ "The xApp is not healthy";
+ }
+ }
+ description
+ "xApp health status";
+ }
+
grouping xapp-status {
leaf name {
type string;
description
- "Name of the xApp in helm chart";
+ "Name of the xApp visible in Kubernetes";
}
leaf status {
- type boolean;
+ type string;
+ description
+ "The status of the xApp pod: running, restarted, etc";
+ }
+ leaf health {
+ type health-status;
description
- "The status of xApp: true=healthy false=not-healthy";
+ "The health status of xApp: healthy, not-healthy, unavailable";
}
description
"xApp health status";
# By default this file is in the docker build directory,
# but the location can configured in the JJB template.
---
-tag: 0.3.2
+tag: 0.4.0