Skip to content

Commit

Permalink
feat: Adds Pebble notifications (#250)
Browse files Browse the repository at this point in the history
* Adds Pebble notifications

Signed-off-by: Bartlomiej Gmerek <[email protected]>

* Adds error handling for sending Pebble notifications

Signed-off-by: Bartlomiej Gmerek <[email protected]>

* Fixes typos; changes Pebble notice base URL to aetherproject.org

Signed-off-by: Bartlomiej Gmerek <[email protected]>

* Removes duplicated writing to channel

Signed-off-by: Bartlomiej Gmerek <[email protected]>

* Restores LTS config option

Signed-off-by: Bartlomiej Gmerek <[email protected]>

* Moves writing to channel inside the DeviceGroup and NetworkSlice delete functions; addresses minor style comments

Signed-off-by: Bartlomiej Gmerek <[email protected]>

* Removes dots from logs

Signed-off-by: Bartlomiej Gmerek <[email protected]>

---------

Signed-off-by: Bartlomiej Gmerek <[email protected]>
  • Loading branch information
Gmerold authored Nov 20, 2024
1 parent 455ac42 commit 7d03f6d
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 34 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.8.1-dev
1.8.1
17 changes: 9 additions & 8 deletions backend/factory/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,15 @@ type Info struct {
}

type Configuration struct {
Mongodb *Mongodb `yaml:"mongodb"`
RocEnd *RocEndpt `yaml:"managedByConfigPod,omitempty"` // fetch config during bootup
LteEnd []*LteEndpt `yaml:"endpoints,omitempty"` // LTE endpoints are configured and not auto-detected
TLS *TLS `yaml:"tls"`
Mode5G bool `yaml:"mode5G,omitempty"`
SdfComp bool `yaml:"spec-compliant-sdf"`
EnableAuthentication bool `yaml:"enableAuthentication,omitempty"`
CfgPort int `yaml:"cfgport,omitempty"`
Mongodb *Mongodb `yaml:"mongodb"`
RocEnd *RocEndpt `yaml:"managedByConfigPod,omitempty"` // fetch config during bootup
LteEnd []*LteEndpt `yaml:"endpoints,omitempty"` // LTE endpoints are configured and not auto-detected
TLS *TLS `yaml:"tls"`
Mode5G bool `yaml:"mode5G,omitempty"`
SdfComp bool `yaml:"spec-compliant-sdf"`
EnableAuthentication bool `yaml:"enableAuthentication,omitempty"`
SendPebbleNotifications bool `yaml:"send_pebble_notifications,omitempty"`
CfgPort int `yaml:"cfgport,omitempty"`
}

type TLS struct {
Expand Down
87 changes: 63 additions & 24 deletions proto/server/configEvtHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package server
import (
"encoding/json"
"fmt"
"os/exec"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -39,9 +40,11 @@ type Update5GSubscriberMsg struct {
PrevSlice *configmodels.Slice
}

var rwLock sync.RWMutex

var imsiData map[string]*models.AuthenticationSubscription
var (
execCommand = exec.Command
imsiData map[string]*models.AuthenticationSubscription
rwLock sync.RWMutex
)

func init() {
imsiData = make(map[string]*models.AuthenticationSubscription)
Expand Down Expand Up @@ -113,7 +116,6 @@ func configHandler(configMsgChan chan *configmodels.ConfigMessage, configReceive
client.outStandingPushConfig <- configMsg
}
} else {
var config5gMsg Update5GSubscriberMsg
if configMsg.MsgType == configmodels.Inventory {
if configMsg.GnbName != "" {
logger.ConfigLog.Infof("received delete gNB [%v] from config channel", configMsg.GnbName)
Expand All @@ -124,35 +126,19 @@ func configHandler(configMsgChan chan *configmodels.ConfigMessage, configReceive
handleUpfDelete(configMsg.UpfHostname)
}
} else if configMsg.MsgType != configmodels.Sub_data {
rwLock.Lock()
// update config snapshot
if configMsg.DevGroup == nil {
if configMsg.DevGroup == nil && configMsg.DevGroupName != "" {
logger.ConfigLog.Infof("received delete Device Group [%v] from config channel", configMsg.DevGroupName)
config5gMsg.PrevDevGroup = getDeviceGroupByName(configMsg.DevGroupName)
filter := bson.M{"group-name": configMsg.DevGroupName}
errDelOne := dbadapter.CommonDBClient.RestfulAPIDeleteOne(devGroupDataColl, filter)
if errDelOne != nil {
logger.DbLog.Warnln(errDelOne)
}
handleDeviceGroupDelete(configMsg, subsUpdateChan)
}

if configMsg.Slice == nil {
if configMsg.Slice == nil && configMsg.SliceName != "" {
logger.ConfigLog.Infof("received delete Slice [%v] from config channel", configMsg.SliceName)
config5gMsg.PrevSlice = getSliceByName(configMsg.SliceName)
filter := bson.M{"slice-name": configMsg.SliceName}
errDelOne := dbadapter.CommonDBClient.RestfulAPIDeleteOne(sliceDataColl, filter)
if errDelOne != nil {
logger.DbLog.Warnln(errDelOne)
}
handleNetworkSliceDelete(configMsg, subsUpdateChan)
}
rwLock.Unlock()
} else {
logger.ConfigLog.Infof("received delete Subscriber [%v] from config channel", configMsg.Imsi)
}
if factory.WebUIConfig.Configuration.Mode5G {
config5gMsg.Msg = configMsg
subsUpdateChan <- &config5gMsg
}
// loop through all clients and send this message to all clients
if len(clientNFPool) == 0 {
logger.ConfigLog.Infoln("no client available. No need to send config")
Expand Down Expand Up @@ -196,6 +182,22 @@ func handleDeviceGroupPost(configMsg *configmodels.ConfigMessage, subsUpdateChan
rwLock.Unlock()
}

func handleDeviceGroupDelete(configMsg *configmodels.ConfigMessage, subsUpdateChan chan *Update5GSubscriberMsg) {
rwLock.Lock()
if factory.WebUIConfig.Configuration.Mode5G {
var config5gMsg Update5GSubscriberMsg
config5gMsg.Msg = configMsg
config5gMsg.PrevDevGroup = getDeviceGroupByName(configMsg.DevGroupName)
subsUpdateChan <- &config5gMsg
}
filter := bson.M{"group-name": configMsg.DevGroupName}
err := dbadapter.CommonDBClient.RestfulAPIDeleteOne(devGroupDataColl, filter)
if err != nil {
logger.DbLog.Warnln(err)
}
rwLock.Unlock()
}

func handleNetworkSlicePost(configMsg *configmodels.ConfigMessage, subsUpdateChan chan *Update5GSubscriberMsg) {
rwLock.Lock()
if factory.WebUIConfig.Configuration.Mode5G {
Expand All @@ -210,6 +212,34 @@ func handleNetworkSlicePost(configMsg *configmodels.ConfigMessage, subsUpdateCha
if errPost != nil {
logger.DbLog.Warnln(errPost)
}
if factory.WebUIConfig.Configuration.SendPebbleNotifications {
err := sendPebbleNotification("aetherproject.org/webconsole/networkslice/create")
if err != nil {
logger.ConfigLog.Warnf("sending Pebble notification failed: %s. continuing silently", err.Error())
}
}
rwLock.Unlock()
}

func handleNetworkSliceDelete(configMsg *configmodels.ConfigMessage, subsUpdateChan chan *Update5GSubscriberMsg) {
rwLock.Lock()
if factory.WebUIConfig.Configuration.Mode5G {
var config5gMsg Update5GSubscriberMsg
config5gMsg.Msg = configMsg
config5gMsg.PrevSlice = getSliceByName(configMsg.SliceName)
subsUpdateChan <- &config5gMsg
}
filter := bson.M{"slice-name": configMsg.SliceName}
err := dbadapter.CommonDBClient.RestfulAPIDeleteOne(sliceDataColl, filter)
if err != nil {
logger.DbLog.Warnln(err)
}
if factory.WebUIConfig.Configuration.SendPebbleNotifications {
err := sendPebbleNotification("aetherproject.org/webconsole/networkslice/delete")
if err != nil {
logger.ConfigLog.Warnf("sending Pebble notification failed: %s. continuing silently", err.Error())
}
}
rwLock.Unlock()
}

Expand Down Expand Up @@ -720,3 +750,12 @@ func SnssaiModelsToHex(snssai models.Snssai) string {
sst := fmt.Sprintf("%02x", snssai.Sst)
return sst + snssai.Sd
}

func sendPebbleNotification(key string) error {
cmd := execCommand("pebble", "notify", key)
if err := cmd.Run(); err != nil {
return fmt.Errorf("couldn't execute a pebble notify: %w", err)
}
logger.ConfigLog.Infoln("custom Pebble notification sent")
return nil
}
59 changes: 58 additions & 1 deletion proto/server/configEvtHandler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ package server

import (
"encoding/json"
"os"
"os/exec"
"reflect"
"testing"

Expand All @@ -16,7 +18,10 @@ import (
"go.mongodb.org/mongo-driver/bson/primitive"
)

var postData []map[string]interface{}
var (
execCommandTimesCalled = 0
postData []map[string]interface{}
)

func deviceGroup(name string) configmodels.DeviceGroups {
traffic_class := configmodels.TrafficClassInfo{
Expand Down Expand Up @@ -121,6 +126,58 @@ func (m *MockMongoSliceGetOne) RestfulAPIGetOne(collName string, filter bson.M)
return previousSliceBson, nil
}

func mockExecCommand(command string, args ...string) *exec.Cmd {
cs := []string{"-test.run=TestExecCommandHelper", "--", "ENTER YOUR COMMAND HERE"}
cs = append(cs, args...)
cmd := exec.Command(os.Args[0], cs...)
execCommandTimesCalled += 1
return cmd
}

func Test_sendPebbleNotification_on_when_handleNetworkSlicePost(t *testing.T) {
execCommand = mockExecCommand
defer func() { execCommand = exec.Command }()
numPebbleNotificationsSent := execCommandTimesCalled
networkSlice := []configmodels.Slice{networkSlice("slice1")}
factory.WebUIConfig.Configuration.SendPebbleNotifications = true

configMsg := configmodels.ConfigMessage{
SliceName: networkSlice[0].SliceName,
Slice: &networkSlice[0],
}
subsUpdateChan := make(chan *Update5GSubscriberMsg, 10)
dbadapter.CommonDBClient = &MockMongoPost{dbadapter.CommonDBClient}
dbadapter.CommonDBClient = &MockMongoGetOneNil{dbadapter.CommonDBClient}
handleNetworkSlicePost(&configMsg, subsUpdateChan)

if execCommandTimesCalled != numPebbleNotificationsSent+1 {
t.Errorf("Unexpected number of Pebble notifications: %v. Should be: %v", execCommandTimesCalled, numPebbleNotificationsSent+1)
}
}

func Test_sendPebbleNotification_off_when_handleNetworkSlicePost(t *testing.T) {
execCommand = mockExecCommand
defer func() { execCommand = exec.Command }()
numPebbleNotificationsSent := execCommandTimesCalled
networkSlices := []configmodels.Slice{networkSlice("slice1")}
factory.WebUIConfig.Configuration.SendPebbleNotifications = false

for _, testSlice := range networkSlices {
configMsg := configmodels.ConfigMessage{
SliceName: testSlice.SliceName,
Slice: &testSlice,
}
subsUpdateChan := make(chan *Update5GSubscriberMsg, 10)
dbadapter.CommonDBClient = &MockMongoPost{dbadapter.CommonDBClient}
dbadapter.CommonDBClient = &MockMongoGetOneNil{dbadapter.CommonDBClient}
handleNetworkSlicePost(&configMsg, subsUpdateChan)
}

if execCommandTimesCalled != numPebbleNotificationsSent {
t.Errorf("Unexpected number of Pebble notifications: %v. Should be: %v", execCommandTimesCalled, numPebbleNotificationsSent)
}
}

func Test_handleDeviceGroupPost(t *testing.T) {
deviceGroups := []configmodels.DeviceGroups{deviceGroup("group1"), deviceGroup("group2"), deviceGroup("group_no_imsis"), deviceGroup("group_no_traf_class"), deviceGroup("group_no_qos")}
deviceGroups[2].Imsis = []string{}
Expand Down

0 comments on commit 7d03f6d

Please sign in to comment.