Skip to content

Commit

Permalink
feat: enable NRF response caching (#90)
Browse files Browse the repository at this point in the history
* feat: enable NRF response caching

Signed-off-by: Dario Faccin <dario.faccin@canonical.com>

* address linting failures

Signed-off-by: Dario Faccin <dario.faccin@canonical.com>

* feat: add NF notify

Signed-off-by: Dario Faccin <dario.faccin@canonical.com>

* add missing license and set nrf dependency as direct

Signed-off-by: Dario Faccin <dario.faccin@canonical.com>

* address review comments

Signed-off-by: Dario Faccin <dario.faccin@canonical.com>

* add first unit test

Signed-off-by: Dario Faccin <dario.faccin@canonical.com>

* add more unit tests

Signed-off-by: Dario Faccin <dario.faccin@canonical.com>

* add NF deregister and more unit tests

Signed-off-by: Dario Faccin <dario.faccin@canonical.com>

* migrate to nrfcache from openapi

Signed-off-by: Dario Faccin <dario.faccin@canonical.com>

* register to NRF after setting NRF cache

Signed-off-by: Dario Faccin <dario.faccin@canonical.com>

* update funcs signatures

Signed-off-by: Dario Faccin <dario.faccin@canonical.com>

* update copyright

Signed-off-by: Dario Faccin <dario.faccin@canonical.com>

* fixes for linting

Signed-off-by: Dario Faccin <dario.faccin@canonical.com>

* address review comments

Signed-off-by: Dario Faccin <dario.faccin@canonical.com>

* Address review comments

Signed-off-by: Arrobo, Gabriel <gabriel.arrobo@intel.com>

---------

Signed-off-by: Dario Faccin <dario.faccin@canonical.com>
Signed-off-by: Arrobo, Gabriel <gabriel.arrobo@intel.com>
Co-authored-by: Arrobo, Gabriel <gabriel.arrobo@intel.com>
  • Loading branch information
dariofaccin and gab-arrobo authored Aug 19, 2024
1 parent 5510350 commit 4162596
Show file tree
Hide file tree
Showing 15 changed files with 1,036 additions and 65 deletions.
615 changes: 615 additions & 0 deletions ausf_test.go

Large diffs are not rendered by default.

66 changes: 66 additions & 0 deletions callback/api_nf_subscribe_notify.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-FileCopyrightText: 2022 Infosys Limited
// SPDX-FileCopyrightText: 2024 Canonical Ltd.
// Copyright 2019 free5GC.org
//
// SPDX-License-Identifier: Apache-2.0
//

package callback

import (
"net/http"

"github.com/gin-gonic/gin"
"github.com/omec-project/ausf/logger"
"github.com/omec-project/ausf/producer"
"github.com/omec-project/openapi"
"github.com/omec-project/openapi/models"
"github.com/omec-project/util/httpwrapper"
)

func HTTPNfSubscriptionStatusNotify(c *gin.Context) {
var nfSubscriptionStatusNotification models.NotificationData

requestBody, err := c.GetRawData()
if err != nil {
logger.CallbackLog.Errorf("get Request Body error: %+v", err)
problemDetail := models.ProblemDetails{
Title: "System failure",
Status: http.StatusInternalServerError,
Detail: err.Error(),
Cause: "SYSTEM_FAILURE",
}
c.JSON(http.StatusInternalServerError, problemDetail)
return
}

err = openapi.Deserialize(&nfSubscriptionStatusNotification, requestBody, "application/json")
if err != nil {
problemDetail := "[Request Body] " + err.Error()
rsp := models.ProblemDetails{
Title: "Malformed request syntax",
Status: http.StatusBadRequest,
Detail: problemDetail,
}
logger.CallbackLog.Errorln(problemDetail)
c.JSON(http.StatusBadRequest, rsp)
return
}

req := httpwrapper.NewRequest(c.Request, nfSubscriptionStatusNotification)

rsp := producer.HandleNfSubscriptionStatusNotify(req)

responseBody, err := openapi.Serialize(rsp.Body, "application/json")
if err != nil {
logger.CallbackLog.Errorln(err)
problemDetails := models.ProblemDetails{
Status: http.StatusInternalServerError,
Cause: "SYSTEM_FAILURE",
Detail: err.Error(),
}
c.JSON(http.StatusInternalServerError, problemDetails)
} else if rsp.Body != nil {
c.Data(rsp.Status, "application/json", responseBody)
}
}
71 changes: 71 additions & 0 deletions callback/router.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2019 free5GC.org
//
// SPDX-License-Identifier: Apache-2.0
//

package callback

import (
"net/http"
"strings"

"github.com/gin-gonic/gin"
"github.com/omec-project/ausf/logger"
loggerUtil "github.com/omec-project/util/logger"
)

// Route is the information for every URI.
type Route struct {
// HandlerFunc is the handler function of this route.
HandlerFunc gin.HandlerFunc
// Name is the name of this Route.
Name string
// Method is the string for the HTTP method, ex: GET, POST etc..
Method string
// Pattern is the pattern of the URI.
Pattern string
}

// Routes is the list of the generated Route.
type Routes []Route

// NewRouter returns a new router.
func NewRouter() *gin.Engine {
router := loggerUtil.NewGinWithLogrus(logger.GinLog)
AddService(router)
return router
}

func AddService(engine *gin.Engine) *gin.RouterGroup {
group := engine.Group("/nausf-callback/v1")

for _, route := range routes {
switch route.Method {
case "GET":
group.GET(route.Pattern, route.HandlerFunc)
case "POST":
group.POST(route.Pattern, route.HandlerFunc)
case "PUT":
group.PUT(route.Pattern, route.HandlerFunc)
case "PATCH":
group.PATCH(route.Pattern, route.HandlerFunc)
case "DELETE":
group.DELETE(route.Pattern, route.HandlerFunc)
}
}
return group
}

// Index is the index handler.
func Index(c *gin.Context) {
c.String(http.StatusOK, "Hello World!")
}

var routes = Routes{
{
HTTPNfSubscriptionStatusNotify,
"NfStatusNotify",
strings.ToUpper("Post"),
"/nf-status-notify",
},
}
62 changes: 50 additions & 12 deletions consumer/nf_discovery.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2019 free5GC.org
//
// SPDX-FileCopyrightText: 2024 Canonical Ltd.
// SPDX-License-Identifier: Apache-2.0
//

Expand All @@ -10,29 +10,67 @@ import (
"fmt"
"net/http"

ausfContext "github.com/omec-project/ausf/context"
"github.com/omec-project/ausf/logger"
"github.com/omec-project/openapi/Nnrf_NFDiscovery"
"github.com/omec-project/openapi/models"
nrfCache "github.com/omec-project/openapi/nrfcache"
)

var (
CreateSubscription = SendCreateSubscription
NRFCacheSearchNFInstances = nrfCache.SearchNFInstances
StoreApiClient = &Nnrf_NFDiscovery.APIClient{}
StoreApiSearchNFInstances = (*Nnrf_NFDiscovery.NFInstancesStoreApiService).SearchNFInstances
)

func SendSearchNFInstances(nrfUri string, targetNfType, requestNfType models.NfType,
param Nnrf_NFDiscovery.SearchNFInstancesParamOpts) (*models.SearchResult, error) {
var SendSearchNFInstances = func(nrfUri string, targetNfType, requestNfType models.NfType,
param *Nnrf_NFDiscovery.SearchNFInstancesParamOpts) (models.SearchResult, error) {
if ausfContext.GetSelf().EnableNrfCaching {
return NRFCacheSearchNFInstances(nrfUri, targetNfType, requestNfType, param)
} else {
return SendNfDiscoveryToNrf(nrfUri, targetNfType, requestNfType, param)
}
}

var SendNfDiscoveryToNrf = func(nrfUri string, targetNfType, requestNfType models.NfType,
param *Nnrf_NFDiscovery.SearchNFInstancesParamOpts) (models.SearchResult, error) {
// Set client and set url
configuration := Nnrf_NFDiscovery.NewConfiguration()
configuration.SetBasePath(nrfUri)
client := Nnrf_NFDiscovery.NewAPIClient(configuration)

result, rsp, rspErr := client.NFInstancesStoreApi.SearchNFInstances(context.TODO(),
targetNfType, requestNfType, &param)
if rspErr != nil {
return nil, fmt.Errorf("NFInstancesStoreApi Response error: %+w", rspErr)
result, res, err := StoreApiSearchNFInstances(client.NFInstancesStoreApi, context.TODO(), targetNfType, requestNfType, param)
if res != nil && res.StatusCode == http.StatusTemporaryRedirect {
err = fmt.Errorf("temporary redirect for non NRF consumer")
}
defer func() {
if rspCloseErr := rsp.Body.Close(); rspCloseErr != nil {
logger.ConsumerLog.Errorf("NFInstancesStoreApi Response cannot close: %v", rspCloseErr)
if bodyCloseErr := res.Body.Close(); bodyCloseErr != nil {
err = fmt.Errorf("SearchNFInstances' response body cannot close: %+w", bodyCloseErr)
}
}()
if rsp != nil && rsp.StatusCode == http.StatusTemporaryRedirect {
return nil, fmt.Errorf("temporary Redirect For Non NRF Consumer")

ausfSelf := ausfContext.GetSelf()

var nrfSubData models.NrfSubscriptionData
var problemDetails *models.ProblemDetails
for _, nfProfile := range result.NfInstances {
// checking whether the AUSF subscribed to this target nfinstanceid or not
if _, ok := ausfSelf.NfStatusSubscriptions.Load(nfProfile.NfInstanceId); !ok {
nrfSubscriptionData := models.NrfSubscriptionData{
NfStatusNotificationUri: fmt.Sprintf("%s/nausf-callback/v1/nf-status-notify", ausfSelf.GetIPv4Uri()),
SubscrCond: &models.NfInstanceIdCond{NfInstanceId: nfProfile.NfInstanceId},
ReqNfType: requestNfType,
}
nrfSubData, problemDetails, err = CreateSubscription(nrfUri, nrfSubscriptionData)
if problemDetails != nil {
logger.ConsumerLog.Errorf("SendCreateSubscription to NRF, Problem[%+v]", problemDetails)
} else if err != nil {
logger.ConsumerLog.Errorf("SendCreateSubscription Error[%+v]", err)
}
ausfSelf.NfStatusSubscriptions.Store(nfProfile.NfInstanceId, nrfSubData.SubscriptionId)
}
}
return &result, nil

return result, err
}
70 changes: 65 additions & 5 deletions consumer/nf_management.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2019 free5GC.org
//
// SPDX-FileCopyrightText: 2024 Canonical Ltd.
// SPDX-License-Identifier: Apache-2.0
//

Expand All @@ -12,14 +12,14 @@ import (
"strings"
"time"

ausf_context "github.com/omec-project/ausf/context"
ausfContext "github.com/omec-project/ausf/context"
"github.com/omec-project/ausf/logger"
"github.com/omec-project/openapi"
"github.com/omec-project/openapi/Nnrf_NFManagement"
"github.com/omec-project/openapi/models"
)

func BuildNFInstance(ausfContext *ausf_context.AUSFContext) (profile models.NfProfile, err error) {
func BuildNFInstance(ausfContext *ausfContext.AUSFContext) (profile models.NfProfile, err error) {
profile.NfInstanceId = ausfContext.NfId
profile.NfType = models.NfType_AUSF
profile.NfStatus = models.NfStatus_REGISTERED
Expand Down Expand Up @@ -83,7 +83,7 @@ var SendRegisterNFInstance = func(nrfUri, nfInstanceId string, profile models.Nf
func SendDeregisterNFInstance() (*models.ProblemDetails, error) {
logger.AppLog.Infof("Send Deregister NFInstance")

ausfSelf := ausf_context.GetSelf()
ausfSelf := ausfContext.GetSelf()
// Set client and set url
configuration := Nnrf_NFManagement.NewConfiguration()
configuration.SetBasePath(ausfSelf.NrfUri)
Expand Down Expand Up @@ -111,7 +111,7 @@ func SendDeregisterNFInstance() (*models.ProblemDetails, error) {
var SendUpdateNFInstance = func(patchItem []models.PatchItem) (nfProfile models.NfProfile, problemDetails *models.ProblemDetails, err error) {
logger.ConsumerLog.Debugf("Send Update NFInstance")

ausfSelf := ausf_context.GetSelf()
ausfSelf := ausfContext.GetSelf()
configuration := Nnrf_NFManagement.NewConfiguration()
configuration.SetBasePath(ausfSelf.NrfUri)
client := Nnrf_NFManagement.NewAPIClient(configuration)
Expand All @@ -137,3 +137,63 @@ var SendUpdateNFInstance = func(patchItem []models.PatchItem) (nfProfile models.
}
return
}

var SendCreateSubscription = func(nrfUri string, nrfSubscriptionData models.NrfSubscriptionData) (nrfSubData models.NrfSubscriptionData, problemDetails *models.ProblemDetails, err error) {
logger.ConsumerLog.Debugf("send Create Subscription")

// Set client and set url
configuration := Nnrf_NFManagement.NewConfiguration()
configuration.SetBasePath(nrfUri)
client := Nnrf_NFManagement.NewAPIClient(configuration)

var res *http.Response
nrfSubData, res, err = client.SubscriptionsCollectionApi.CreateSubscription(context.TODO(), nrfSubscriptionData)
if err == nil {
return
} else if res != nil {
defer func() {
if resCloseErr := res.Body.Close(); resCloseErr != nil {
logger.ConsumerLog.Errorf("SendCreateSubscription response cannot close: %+v", resCloseErr)
}
}()
if res.Status != err.Error() {
logger.ConsumerLog.Errorf("SendCreateSubscription received error response: %v", res.Status)
return
}
problem := err.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails)
problemDetails = &problem
} else {
err = openapi.ReportError("server no response")
}
return
}

var SendRemoveSubscription = func(subscriptionId string) (problemDetails *models.ProblemDetails, err error) {
logger.ConsumerLog.Infoln("send Remove Subscription")

ausfSelf := ausfContext.GetSelf()
// Set client and set url
configuration := Nnrf_NFManagement.NewConfiguration()
configuration.SetBasePath(ausfSelf.NrfUri)
client := Nnrf_NFManagement.NewAPIClient(configuration)
var res *http.Response

res, err = client.SubscriptionIDDocumentApi.RemoveSubscription(context.Background(), subscriptionId)
if err == nil {
return
} else if res != nil {
defer func() {
if bodyCloseErr := res.Body.Close(); bodyCloseErr != nil {
err = fmt.Errorf("RemoveSubscription's response body cannot close: %w", bodyCloseErr)
}
}()
if res.Status != err.Error() {
return
}
problem := err.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails)
problemDetails = &problem
} else {
err = openapi.ReportError("server no response")
}
return
}
10 changes: 9 additions & 1 deletion context/ausf_context_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import (
"fmt"
"os"
"strconv"
"time"

"github.com/google/uuid"

"github.com/omec-project/ausf/factory"
"github.com/omec-project/ausf/logger"
"github.com/omec-project/ausf/util"
Expand Down Expand Up @@ -83,6 +83,14 @@ func InitAusfContext(context *AUSFContext) {
if roc != "true" {
context.PlmnList = append(context.PlmnList, configuration.PlmnSupportList...)
}
context.EnableNrfCaching = configuration.EnableNrfCaching
if configuration.EnableNrfCaching {
if configuration.NrfCacheEvictionInterval == 0 {
context.NrfCacheEvictionInterval = time.Duration(900) // 15 mins
} else {
context.NrfCacheEvictionInterval = time.Duration(configuration.NrfCacheEvictionInterval)
}
}

// context.NfService
context.NfService = make(map[models.ServiceName]models.NfService)
Expand Down
Loading

0 comments on commit 4162596

Please sign in to comment.