"e2mgr/logger"
"e2mgr/managers"
"e2mgr/models"
+ "e2mgr/rmrCgo"
"e2mgr/services"
"e2mgr/services/rmrsender"
+ "encoding/xml"
+ "fmt"
"gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/common"
"gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/entities"
- // "github.com/pkg/errors"
+ "strings"
+ "unsafe"
+)
+
+var(
+ emptyTagsToReplaceToSelfClosingTags = []string{"reject", "ignore", "protocolIEs"}
)
type HealthCheckRequestHandler struct {
ranNameList := h.getRanNameList(request)
isAtleastOneRanConnected := false
+ nodetypeToNbIdentityMapOld := make(map[entities.Node_Type][]*entities.NbIdentity)
+ nodetypeToNbIdentityMapNew := make(map[entities.Node_Type][]*entities.NbIdentity)
+
for _, ranName := range ranNameList {
- nodebInfo, err := h.rNibDataService.GetNodeb(ranName) //This method is needed for getting RAN functions with later commits
+ nodebInfo, err := h.rNibDataService.GetNodeb(ranName)
if err != nil {
_, ok := err.(*common.ResourceNotFoundError)
if !ok {
}
continue
}
+
if nodebInfo.ConnectionStatus == entities.ConnectionStatus_CONNECTED {
isAtleastOneRanConnected = true
+ err := h.sendRICServiceQuery(nodebInfo)
+ if err != nil {
+ return nil,err
+ }
+
+ oldnbIdentity, newnbIdentity := h.ranListManager.UpdateHealthcheckTimeStampSent(ranName)
+ nodetypeToNbIdentityMapOld[nodebInfo.NodeType] = append(nodetypeToNbIdentityMapOld[nodebInfo.NodeType], oldnbIdentity)
+ nodetypeToNbIdentityMapNew[nodebInfo.NodeType] = append(nodetypeToNbIdentityMapNew[nodebInfo.NodeType], newnbIdentity)
}
}
+
+ for k, _ := range nodetypeToNbIdentityMapOld {
+ err := h.ranListManager.UpdateNbIdentities(k, nodetypeToNbIdentityMapOld[k], nodetypeToNbIdentityMapNew[k])
+ if err != nil {
+ return nil,err
+ }
+ }
+
if isAtleastOneRanConnected == false {
return nil, e2managererrors.NewNoConnectedRanError()
}
+ h.logger.Infof("#HealthcheckRequest.Handle - HealthcheckTimeStampSent Update completed to RedisDB")
+
return nil, nil
}
+func (h *HealthCheckRequestHandler) sendRICServiceQuery(nodebInfo *entities.NodebInfo) error {
+
+ serviceQuery := models.NewRicServiceQueryMessage(nodebInfo.GetGnb().RanFunctions)
+ payLoad, err := xml.Marshal(serviceQuery.E2APPDU)
+ if err != nil {
+ h.logger.Errorf("#HealthCHeckRequest.Handle- RAN name: %s - Error marshalling RIC_SERVICE_QUERY. Payload: %s", nodebInfo.RanName, payLoad)
+ //return nil, e2managererrors.NewInternalError()
+ }
+
+ payLoad = replaceEmptyTagsWithSelfClosing(payLoad)
+
+ var xAction []byte
+ var msgSrc unsafe.Pointer
+ msg := models.NewRmrMessage(rmrCgo.RIC_SERVICE_QUERY, nodebInfo.RanName, payLoad, xAction, msgSrc)
+
+ err = h.rmrsender.Send(msg)
+
+ if err != nil {
+ h.logger.Errorf("#HealthCHeckRequest.Handle - failed to send RIC_SERVICE_QUERY message to RMR for %s. Error: %s", nodebInfo.RanName, err)
+ //return nil, e2managererrors.NewRmrError()
+ } else {
+ h.logger.Infof("#HealthCHeckRequest.Handle - RAN name : %s - Successfully built and sent RIC_SERVICE_QUERY. Message: %x", nodebInfo.RanName, msg)
+ }
+
+ return nil
+}
+
func (h *HealthCheckRequestHandler) getRanNameList(request models.Request) []string {
healthCheckRequest := request.(models.HealthCheckRequest)
if request != nil && len(healthCheckRequest.RanList) != 0 {
return healthCheckRequest.RanList
}
- nodeIds := h.ranListManager.GetNbIdentityList()
+ h.logger.Infof("#HealthcheckRequest.getRanNameList - Empty request sent, fetching all connected NbIdentitylist")
+
+ nodeIds := h.ranListManager.GetNbIdentityList()
var ranNameList []string
+
for _, nbIdentity := range nodeIds {
if nbIdentity.ConnectionStatus == entities.ConnectionStatus_CONNECTED {
ranNameList = append(ranNameList, nbIdentity.InventoryName)
}
}
+
return ranNameList
}
+
+func replaceEmptyTagsWithSelfClosing(responsePayload []byte) []byte {
+
+ emptyTagVsSelfClosingTagPairs := make([]string, len(emptyTagsToReplaceToSelfClosingTags)*2)
+
+ j := 0
+
+ for i := 0; i < len(emptyTagsToReplaceToSelfClosingTags); i++ {
+ emptyTagVsSelfClosingTagPairs[j] = fmt.Sprintf("<%[1]s></%[1]s>", emptyTagsToReplaceToSelfClosingTags[i])
+ emptyTagVsSelfClosingTagPairs[j+1] = fmt.Sprintf("<%s/>", emptyTagsToReplaceToSelfClosingTags[i])
+ j += 2
+ }
+
+ responseString := strings.NewReplacer(emptyTagVsSelfClosingTagPairs...).Replace(string(responsePayload))
+ return []byte(responseString)
+}
package httpmsghandlers
import (
+ "bytes"
"e2mgr/configuration"
"e2mgr/e2managererrors"
"e2mgr/mocks"
"e2mgr/models"
+ "e2mgr/rmrCgo"
"e2mgr/services"
+ "encoding/xml"
+ "errors"
"gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/entities"
- "github.com/pkg/errors"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/mock"
+ "io/ioutil"
+ "path/filepath"
+ "strings"
"testing"
+ "unsafe"
)
-func setupHealthCheckHandlerTest(t *testing.T) (*HealthCheckRequestHandler, services.RNibDataService, *mocks.RnibReaderMock, *mocks.RanListManagerMock) {
+const (
+ e2tInstanceFullAddress = "10.0.2.15:9999"
+ e2SetupMsgPrefix = e2tInstanceFullAddress + "|"
+ GnbSetupRequestXmlPath = "../../tests/resources/setupRequest_gnb.xml"
+)
+
+func setupHealthCheckHandlerTest(t *testing.T) (*HealthCheckRequestHandler, services.RNibDataService, *mocks.RnibReaderMock, *mocks.RanListManagerMock, *mocks.RmrMessengerMock) {
logger := initLog(t)
config := &configuration.Configuration{RnibRetryIntervalMs: 10, MaxRnibConnectionAttempts: 3}
rmrSender := getRmrSender(rmrMessengerMock, logger)
handler := NewHealthCheckRequestHandler(logger, rnibDataService, ranListManagerMock, rmrSender)
- return handler, rnibDataService, readerMock, ranListManagerMock
+ return handler, rnibDataService, readerMock, ranListManagerMock, rmrMessengerMock
}
func TestHealthCheckRequestHandlerArguementHasRanNameSuccess(t *testing.T) {
- handler, _, readerMock, _ := setupHealthCheckHandlerTest(t)
-
- nb1 := &entities.NodebInfo{RanName: "RanName_1", ConnectionStatus: entities.ConnectionStatus_CONNECTED}
+ handler, _, readerMock, ranListManagerMock, rmrMessengerMock := setupHealthCheckHandlerTest(t)
ranNames := []string{"RanName_1"}
- readerMock.On("GetNodeb", "RanName_1").Return(nb1, nil)
+ nb1:= createNbIdentity(t,"RanName_1", entities.ConnectionStatus_CONNECTED)
+ oldnbIdentity := &entities.NbIdentity{InventoryName: nb1.RanName, ConnectionStatus: nb1.ConnectionStatus}
+ newnbIdentity := &entities.NbIdentity{InventoryName: nb1.RanName, ConnectionStatus: nb1.ConnectionStatus}
+
+ readerMock.On("GetNodeb", nb1.RanName).Return(nb1, nil)
+
+ mbuf:= createRMRMbuf(t, nb1)
+ rmrMessengerMock.On("SendMsg",mbuf,true).Return(mbuf,nil)
+ ranListManagerMock.On("UpdateHealthcheckTimeStampSent",nb1.RanName).Return(oldnbIdentity, newnbIdentity)
+ ranListManagerMock.On("UpdateNbIdentities",nb1.NodeType, []*entities.NbIdentity{oldnbIdentity}, []*entities.NbIdentity{newnbIdentity}).Return(nil)
_, err := handler.Handle(models.HealthCheckRequest{ranNames})
}
func TestHealthCheckRequestHandlerArguementHasNoRanNameSuccess(t *testing.T) {
- handler, _, readerMock, ranListManagerMock := setupHealthCheckHandlerTest(t)
+ handler, _, readerMock, ranListManagerMock, rmrMessengerMock := setupHealthCheckHandlerTest(t)
nbIdentityList := []*entities.NbIdentity{{InventoryName: "RanName_1", ConnectionStatus: entities.ConnectionStatus_CONNECTED},
{InventoryName: "RanName_2", ConnectionStatus: entities.ConnectionStatus_DISCONNECTED}}
ranListManagerMock.On("GetNbIdentityList").Return(nbIdentityList)
- nb1 := &entities.NodebInfo{RanName: "RanName_1", ConnectionStatus: entities.ConnectionStatus_CONNECTED}
- readerMock.On("GetNodeb", "RanName_1").Return(nb1, nil)
+ nb1:= createNbIdentity(t,"RanName_1", entities.ConnectionStatus_CONNECTED)
+ oldnbIdentity := &entities.NbIdentity{InventoryName: nb1.RanName, ConnectionStatus: nb1.ConnectionStatus}
+ newnbIdentity := &entities.NbIdentity{InventoryName: nb1.RanName, ConnectionStatus: nb1.ConnectionStatus}
+
+ readerMock.On("GetNodeb", nb1.RanName).Return(nb1, nil)
+
+ mbuf:= createRMRMbuf(t, nb1)
+ rmrMessengerMock.On("SendMsg",mbuf,true).Return(mbuf,nil)
+ ranListManagerMock.On("UpdateHealthcheckTimeStampSent",nb1.RanName).Return(oldnbIdentity, newnbIdentity)
+ ranListManagerMock.On("UpdateNbIdentities",nb1.NodeType, []*entities.NbIdentity{oldnbIdentity}, []*entities.NbIdentity{newnbIdentity}).Return(nil)
nb2 := &entities.NodebInfo{RanName: "RanName_2", ConnectionStatus: entities.ConnectionStatus_DISCONNECTED}
readerMock.On("GetNodeb", "RanName_2").Return(nb2, nil)
}
func TestHealthCheckRequestHandlerArguementHasNoRanConnectedFailure(t *testing.T) {
- handler, _, readerMock, ranListManagerMock := setupHealthCheckHandlerTest(t)
+ handler, _, readerMock, ranListManagerMock, rmrMessengerMock := setupHealthCheckHandlerTest(t)
nbIdentityList := []*entities.NbIdentity{{InventoryName: "RanName_1", ConnectionStatus: entities.ConnectionStatus_DISCONNECTED},
{InventoryName: "RanName_2", ConnectionStatus: entities.ConnectionStatus_DISCONNECTED}}
nb1 := &entities.NodebInfo{RanName: "RanName_1", ConnectionStatus: entities.ConnectionStatus_DISCONNECTED}
readerMock.On("GetNodeb", "RanName_1").Return(nb1, nil)
- nb2 := &entities.NodebInfo{RanName: "RanName_2", ConnectionStatus: entities.ConnectionStatus_DISCONNECTED}
+ nb2 := &entities.NodebInfo{RanName: "RanName_2", ConnectionStatus: entities.ConnectionStatus_SHUT_DOWN}
readerMock.On("GetNodeb", "RanName_2").Return(nb2, nil)
_, err := handler.Handle(models.HealthCheckRequest{[]string{}})
+ rmrMessengerMock.AssertNotCalled(t, "SendMsg", mock.Anything, mock.Anything)
+ ranListManagerMock.AssertNotCalled(t,"UpdateHealthcheckTimeStampSent",mock.Anything)
+ ranListManagerMock.AssertNotCalled(t,"UpdateNbIdentities",mock.Anything, mock.Anything, mock.Anything)
assert.IsType(t, &e2managererrors.NoConnectedRanError{}, err)
}
func TestHealthCheckRequestHandlerArguementHasRanNameDBErrorFailure(t *testing.T) {
- handler, _, readerMock, _ := setupHealthCheckHandlerTest(t)
+ handler, _, readerMock, ranListManagerMock, rmrMessengerMock := setupHealthCheckHandlerTest(t)
ranNames := []string{"RanName_1"}
readerMock.On("GetNodeb", "RanName_1").Return(&entities.NodebInfo{}, errors.New("error"))
_, err := handler.Handle(models.HealthCheckRequest{ranNames})
+ rmrMessengerMock.AssertNotCalled(t, "SendMsg", mock.Anything, mock.Anything)
+ ranListManagerMock.AssertNotCalled(t,"UpdateHealthcheckTimeStampSent",mock.Anything)
+ ranListManagerMock.AssertNotCalled(t,"UpdateNbIdentities",mock.Anything, mock.Anything, mock.Anything)
assert.IsType(t, &e2managererrors.RnibDbError{}, err)
readerMock.AssertExpectations(t)
}
+
+func createRMRMbuf(t *testing.T, nodebInfo *entities.NodebInfo) *rmrCgo.MBuf{
+ serviceQuery := models.NewRicServiceQueryMessage(nodebInfo.GetGnb().RanFunctions)
+ payLoad, err := xml.Marshal(&serviceQuery.E2APPDU)
+ payLoad = normalizeXml(payLoad)
+
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var xAction []byte
+ var msgSrc unsafe.Pointer
+
+ rmrMessage := models.NewRmrMessage(rmrCgo.RIC_SERVICE_QUERY, nodebInfo.RanName, payLoad, xAction, msgSrc)
+ return rmrCgo.NewMBuf(rmrMessage.MsgType, len(rmrMessage.Payload), rmrMessage.RanName, &rmrMessage.Payload, &rmrMessage.XAction, rmrMessage.GetMsgSrc())
+}
+
+func createNbIdentity(t *testing.T, RanName string, connectionStatus entities.ConnectionStatus) *entities.NodebInfo {
+ xmlgnb := readXmlFile(t, GnbSetupRequestXmlPath)
+ payload := append([]byte(e2SetupMsgPrefix), xmlgnb...)
+ pipInd := bytes.IndexByte(payload, '|')
+ setupRequest := &models.E2SetupRequestMessage{}
+ err := xml.Unmarshal(normalizeXml(payload[pipInd+1:]), &setupRequest.E2APPDU)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ nodeb := &entities.NodebInfo{
+ AssociatedE2TInstanceAddress: e2tInstanceFullAddress,
+ RanName: RanName,
+ SetupFromNetwork: true,
+ NodeType: entities.Node_GNB,
+ ConnectionStatus: connectionStatus,
+ Configuration: &entities.NodebInfo_Gnb{
+ Gnb: &entities.Gnb{
+ GnbType: entities.GnbType_GNB,
+ RanFunctions: setupRequest.ExtractRanFunctionsList(),
+ },
+ },
+ GlobalNbId: &entities.GlobalNbId{
+ PlmnId: setupRequest.GetPlmnId(),
+ NbId: setupRequest.GetNbId(),
+ },
+ }
+ return nodeb
+}
+
+func normalizeXml(payload []byte) []byte {
+ xmlStr := string(payload)
+ normalized := strings.NewReplacer("<", "<", ">", ">",
+ "<reject></reject>","<reject/>","<ignore></ignore>","<ignore/>",
+ "<protocolIEs></protocolIEs>","<protocolIEs/>").Replace(xmlStr)
+ return []byte(normalized)
+}
+
+func readXmlFile(t *testing.T, xmlPath string) []byte {
+ path, err := filepath.Abs(xmlPath)
+ if err != nil {
+ t.Fatal(err)
+ }
+ xmlAsBytes, err := ioutil.ReadFile(path)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ return xmlAsBytes
+}
--- /dev/null
+//
+// Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This source code is part of the near-RT RIC (RAN Intelligent Controller)
+// platform project (RICP).
+
+package models
+
+import (
+ "encoding/xml"
+ "gerrit.o-ran-sc.org/r/ric-plt/nodeb-rnib.git/entities"
+)
+
+type RanFunctionIdItem struct {
+ Text string `xml:",chardata"`
+ RanFunctionId uint32 `xml:"ranFunctionID"`
+ RanFunctionRevision uint32 `xml:"ranFunctionRevision"`
+}
+
+type RicServiceQueryProtocolIESingleContainer struct {
+ Text string `xml:",chardata"`
+ Id string `xml:"id"`
+ Criticality struct {
+ Text string `xml:",chardata"`
+ Reject string `xml:"reject"`
+ } `xml:"criticality"`
+ Value struct {
+ Text string `xml:",chardata"`
+ RanFunctionIdItem RanFunctionIdItem `xml:"RANfunctionID-Item"`
+ } `xml:"value"`
+}
+
+type RICServiceQueryIEs struct {
+ Text string `xml:",chardata"`
+ Id string `xml:"id"`
+ Criticality struct {
+ Text string `xml:",chardata"`
+ Reject string `xml:"reject"`
+ } `xml:"criticality"`
+ Value struct {
+ Text string `xml:",chardata"`
+ RANFunctionIdList struct {
+ Text string `xml:",chardata"`
+ ProtocolIESingleContainer []RicServiceQueryProtocolIESingleContainer `xml:"ProtocolIE-SingleContainer"`
+ } `xml:"RANfunctionsID-List"`
+ } `xml:"value"`
+}
+
+type RICServiceQuery struct {
+ Text string `xml:",chardata"`
+ ProtocolIEs struct {
+ Text string `xml:",chardata"`
+ RICServiceQueryIEs []RICServiceQueryIEs `xml:"RICserviceQuery-IEs"`
+ } `xml:"protocolIEs"`
+}
+
+type InitiatingMessage struct {
+ Text string `xml:",chardata"`
+ ProcedureCode string `xml:"procedureCode"`
+ Criticality struct {
+ Text string `xml:",chardata"`
+ Ignore string `xml:"ignore"`
+ } `xml:"criticality"`
+ Value struct {
+ Text string `xml:",chardata"`
+ RICServiceQuery RICServiceQuery `xml:"RICserviceQuery"`
+ } `xml:"value"`
+}
+
+type RicServiceQueryE2APPDU struct {
+ XMLName xml.Name `xml:"E2AP-PDU"`
+ Text string `xml:",chardata"`
+ InitiatingMessage InitiatingMessage `xml:"initiatingMessage"`
+}
+
+
+type RICServiceQueryMessage struct{
+ XMLName xml.Name `xml:"RICserviceQueryMessage"`
+ Text string `xml:",chardata"`
+ E2APPDU RicServiceQueryE2APPDU `xml:"E2AP-PDU"`
+}
+
+func NewRicServiceQueryMessage(ranFunctions []*entities.RanFunction) RICServiceQueryMessage {
+ initiatingMessage := InitiatingMessage{}
+ initiatingMessage.ProcedureCode = "6"
+ initiatingMessage.Value.RICServiceQuery.ProtocolIEs.RICServiceQueryIEs = make([]RICServiceQueryIEs,1)
+ initiatingMessage.Value.RICServiceQuery.ProtocolIEs.RICServiceQueryIEs[0].Id = "9"
+ protocolIESingleContainer := make([]RicServiceQueryProtocolIESingleContainer,len(ranFunctions))
+ for i := 0; i < len(ranFunctions); i++ {
+ protocolIESingleContainer[i].Id = "6"
+ protocolIESingleContainer[i].Value.RanFunctionIdItem.RanFunctionId = ranFunctions[i].RanFunctionId
+ protocolIESingleContainer[i].Value.RanFunctionIdItem.RanFunctionRevision = ranFunctions[i].RanFunctionRevision
+ }
+ initiatingMessage.Value.RICServiceQuery.ProtocolIEs.RICServiceQueryIEs[0].Value.RANFunctionIdList.ProtocolIESingleContainer = protocolIESingleContainer
+
+ return RICServiceQueryMessage{E2APPDU:RicServiceQueryE2APPDU{InitiatingMessage:initiatingMessage}}
+}
+
+