Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: config gNBs and UPFs #194

Merged
231 changes: 231 additions & 0 deletions configapi/api_inventory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Canonical Ltd.

package configapi

import (
"errors"
"fmt"
"encoding/json"
"strings"
"net/http"

"github.com/gin-gonic/gin"
"github.com/omec-project/util/httpwrapper"
"github.com/omec-project/webconsole/backend/logger"
"github.com/omec-project/webconsole/configmodels"
"github.com/omec-project/webconsole/dbadapter"
"go.mongodb.org/mongo-driver/bson"
)

const (
gnbDataColl = "webconsoleData.snapshots.gnbData"
upfDataColl = "webconsoleData.snapshots.upfData"
)

func setInventoryCorsHeader(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type")
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, DELETE")
}

func GetGnbs(c *gin.Context) {
setInventoryCorsHeader(c)
logger.WebUILog.Infoln("Get all gNBs")

var gnbs []*configmodels.Gnb
gnbs = make([]*configmodels.Gnb, 0)
rawGnbs, errGetMany := dbadapter.CommonDBClient.RestfulAPIGetMany(gnbDataColl, bson.M{})
if errGetMany != nil {
logger.DbLog.Errorln(errGetMany)
c.JSON(http.StatusInternalServerError, gnbs)
}

for _, rawGnb := range rawGnbs {
var gnbData configmodels.Gnb
err := json.Unmarshal(mapToByte(rawGnb), &gnbData)
if err != nil {
logger.DbLog.Errorf("Could not unmarshall gNB %v", rawGnb)
}
gnbs = append(gnbs, &gnbData)
}
c.JSON(http.StatusOK, gnbs)
}

func GnbPostByName(c *gin.Context) {
patriciareinoso marked this conversation as resolved.
Show resolved Hide resolved
setInventoryCorsHeader(c)
if err := gnbPostHandler(c); err == nil {
c.JSON(http.StatusOK, gin.H{})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
}

func GnbDeleteByName(c *gin.Context) {
setInventoryCorsHeader(c)
if err := gnbDeletetHandler(c); err == nil {
c.JSON(http.StatusOK, gin.H{})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
}

func gnbPostHandler(c *gin.Context) error {
var gnbName string
var exists bool
if gnbName, exists = c.Params.Get("gnb-name"); !exists {
errorMessage := "Post gNB request is missing gnb-name"
configLog.Errorf(errorMessage)
return errors.New(errorMessage)
}
configLog.Infof("Received gNB %v", gnbName)
var err error
var newGnb configmodels.Gnb

allowHeader := strings.Split(c.GetHeader("Content-Type"), ";")
switch allowHeader[0] {
case "application/json":
err = c.ShouldBindJSON(&newGnb)
}
if err != nil {
configLog.Errorf("err %v", err)
return fmt.Errorf("Failed to create gNB %v: %w", gnbName, err)
}
if newGnb.Tac == "" {
errorMessage := "Post gNB request body is missing tac"
configLog.Errorf(errorMessage)
return errors.New(errorMessage)
}
req := httpwrapper.NewRequest(c.Request, newGnb)
procReq := req.Body.(configmodels.Gnb)
procReq.GnbName = gnbName
msg := configmodels.ConfigMessage{
MsgType: configmodels.Inventory,
MsgMethod: configmodels.Post_op,
GnbName: gnbName,
Gnb: &procReq,
}
configChannel <- &msg
patriciareinoso marked this conversation as resolved.
Show resolved Hide resolved
configLog.Infof("Successfully added gNB [%v] to config channel.", gnbName)
return nil
}

func gnbDeletetHandler(c *gin.Context) error {
var gnbName string
var exists bool
if gnbName, exists = c.Params.Get("gnb-name"); !exists {
errorMessage := "Delete gNB request is missing gnb-name"
configLog.Errorf(errorMessage)
return fmt.Errorf(errorMessage)
}
configLog.Infof("Received delete gNB %v request", gnbName)
msg := configmodels.ConfigMessage{
MsgType: configmodels.Inventory,
MsgMethod: configmodels.Delete_op,
GnbName: gnbName,
}
configChannel <- &msg
configLog.Infof("Successfully added gNB [%v] with delete_op to config channel.", gnbName)
return nil
}

func GetUpfs(c *gin.Context) {
setInventoryCorsHeader(c)
logger.WebUILog.Infoln("Get all UPFs")

var upfs []*configmodels.Upf
upfs = make([]*configmodels.Upf, 0)
rawUpfs, errGetMany := dbadapter.CommonDBClient.RestfulAPIGetMany(upfDataColl, bson.M{})
if errGetMany != nil {
logger.DbLog.Errorln(errGetMany)
c.JSON(http.StatusInternalServerError, upfs)
}

for _, rawUpf := range rawUpfs {
var upfData configmodels.Upf
err := json.Unmarshal(mapToByte(rawUpf), &upfData)
if err != nil {
logger.DbLog.Errorf("Could not unmarshall UPF %v", rawUpf)
}
upfs = append(upfs, &upfData)
}
c.JSON(http.StatusOK, upfs)
}

func UpfPostByName(c *gin.Context) {
setInventoryCorsHeader(c)
if err := upfPostHandler(c); err == nil {
c.JSON(http.StatusOK, gin.H{})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
}

func UpfDeleteByName(c *gin.Context) {
setInventoryCorsHeader(c)
if err := upfDeleteHandler(c); err == nil {
c.JSON(http.StatusOK, gin.H{})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
}

func upfPostHandler(c *gin.Context) error {
var upfHostname string
var exists bool
if upfHostname, exists = c.Params.Get("upf-hostname"); !exists {
errorMessage := "Post UPF request is missing upf-hostname"
configLog.Errorf(errorMessage)
return errors.New(errorMessage)
}
configLog.Infof("Received UPF %v", upfHostname)
var err error
var newUpf configmodels.Upf

allowHeader := strings.Split(c.GetHeader("Content-Type"), ";")
switch allowHeader[0] {
case "application/json":
err = c.ShouldBindJSON(&newUpf)
}
if err != nil {
configLog.Errorf("err %v", err)
return fmt.Errorf("Failed to create UPF %v: %w", upfHostname, err)
}
if newUpf.Port == "" {
errorMessage := "Post UPF request body is missing port"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would like to know how UPF port is used and what it should be.

Copy link
Contributor Author

@patriciareinoso patriciareinoso Jul 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UPF port is specified in the parameters when creating a new network slice.

    "upf": {
      "upf-name": "upf-external.testpcf.svc.cluster.local",
      "upf-port": "8805"
    }

We are keeping the UPF parameters consistent with what is asked for network slice creation, even if we use 8805.

configLog.Errorf(errorMessage)
return errors.New(errorMessage)
}
req := httpwrapper.NewRequest(c.Request, newUpf)
procReq := req.Body.(configmodels.Upf)
procReq.Hostname = upfHostname
msg := configmodels.ConfigMessage{
MsgType: configmodels.Inventory,
MsgMethod: configmodels.Post_op,
UpfHostname: upfHostname,
Upf: &procReq,
}
configChannel <- &msg
configLog.Infof("Successfully added UPF [%v] to config channel.", upfHostname)
return nil
}

func upfDeleteHandler(c *gin.Context) error {
var upfHostname string
var exists bool
if upfHostname, exists = c.Params.Get("upf-hostname"); !exists {
errorMessage := "Delete UPF request is missing upf-hostname"
configLog.Errorf(errorMessage)
return fmt.Errorf(errorMessage)
}
configLog.Infof("Received Delete UPF %v", upfHostname)
msg := configmodels.ConfigMessage{
MsgType: configmodels.Inventory,
MsgMethod: configmodels.Delete_op,
UpfHostname: upfHostname,
}
configChannel <- &msg
configLog.Infof("Successfully added UPF [%v] with delete_op to config channel.", upfHostname)
return nil
}
Loading