diff --git a/cmd/main.go b/cmd/main.go index 1c03345..86bdc4a 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -12,10 +12,13 @@ package main import ( + "context" "fmt" "os" + "os/signal" "path/filepath" "runtime/debug" + "syscall" "github.com/urfave/cli" @@ -64,19 +67,32 @@ func action(cliCtx *cli.Context) error { logger.MainLog.Infoln(cliCtx.App.Name) logger.MainLog.Infoln("PCF version: ", version.GetVersion()) + ctx, cancel := context.WithCancel(context.Background()) + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) + + go func() { + <-sigCh // Wait for interrupt signal to gracefully shutdown UPF + cancel() // Notify each goroutine and wait them stopped + }() + cfg, err := factory.ReadConfig(cliCtx.String("config")) if err != nil { + sigCh <- nil return err } factory.PcfConfig = cfg - pcf, err := service.NewApp(cfg) + pcf, err := service.NewApp(ctx, cfg, tlsKeyLogPath) if err != nil { + sigCh <- nil return err } PCF = pcf - - pcf.Start(tlsKeyLogPath) + if pcf == nil { + logger.MainLog.Infoln("pcf is nil") + } + pcf.Start() return nil } diff --git a/internal/context/context.go b/internal/context/context.go index 7fd925b..6c8380d 100644 --- a/internal/context/context.go +++ b/internal/context/context.go @@ -82,7 +82,7 @@ type NFContext interface { var _ NFContext = &PCFContext{} -func InitpcfContext(context *PCFContext) { +func InitPcfContext(context *PCFContext) { config := factory.PcfConfig logger.UtilLog.Infof("pcfconfig Info: Version[%s] Description[%s]", config.Info.Version, config.Info.Description) configuration := config.Configuration @@ -157,7 +157,7 @@ func Init() { pcfContext.PcfSuppFeats = make(map[models.ServiceName]openapi.SupportedFeature) pcfContext.BdtPolicyIDGenerator = idgenerator.NewGenerator(1, math.MaxInt64) pcfContext.RatingGroupIdGenerator = idgenerator.NewGenerator(1, math.MaxInt64) - InitpcfContext(&pcfContext) + InitPcfContext(&pcfContext) } // Create new PCF context @@ -458,11 +458,6 @@ func (c *PCFContext) AuthorizationCheck(token string, serviceName models.Service logger.UtilLog.Debugf("PCFContext::AuthorizationCheck: OAuth2 not required\n") return nil } - // TODO: free5gc webconsole uses npcf-oam but it can't get token since it's not an NF. - if serviceName == models.ServiceName_NPCF_OAM { - logger.UtilLog.Warnf("OAuth2 is enable but namf-oam didn't check token now.") - return nil - } logger.UtilLog.Debugf("PCFContext::AuthorizationCheck: token[%s] serviceName[%s]\n", token, serviceName) return oauth.VerifyOAuth(token, string(serviceName), c.NrfCertPem) diff --git a/internal/logger/logger.go b/internal/logger/logger.go index 6ffbf16..d628c7d 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -14,6 +14,7 @@ var ( CfgLog *logrus.Entry CtxLog *logrus.Entry GinLog *logrus.Entry + SBILog *logrus.Entry AmPolicyLog *logrus.Entry BdtPolicyLog *logrus.Entry ConsumerLog *logrus.Entry @@ -38,6 +39,7 @@ func init() { CfgLog = NfLog.WithField(logger_util.FieldCategory, "CFG") CtxLog = NfLog.WithField(logger_util.FieldCategory, "CTX") GinLog = NfLog.WithField(logger_util.FieldCategory, "GIN") + SBILog = NfLog.WithField(logger_util.FieldCategory, "SBI") AmPolicyLog = NfLog.WithField(logger_util.FieldCategory, "AmPol") BdtPolicyLog = NfLog.WithField(logger_util.FieldCategory, "BdtPol") ConsumerLog = NfLog.WithField(logger_util.FieldCategory, "Consumer") diff --git a/internal/sbi/ampolicy/api_default.go b/internal/sbi/ampolicy/api_default.go deleted file mode 100644 index e980b0d..0000000 --- a/internal/sbi/ampolicy/api_default.go +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Npcf_AMPolicyControl - * - * Access and Mobility Policy Control Service API - * - * API version: 1.0.0 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package ampolicy - -import ( - "net/http" - - "github.com/gin-gonic/gin" - - "github.com/free5gc/openapi" - "github.com/free5gc/openapi/models" - "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/internal/sbi/producer" - "github.com/free5gc/pcf/internal/util" - "github.com/free5gc/util/httpwrapper" -) - -func HTTPPoliciesPolAssoIdDelete(c *gin.Context) { - req := httpwrapper.NewRequest(c.Request, nil) - req.Params["polAssoId"], _ = c.Params.Get("polAssoId") - - rsp := producer.HandleDeletePoliciesPolAssoId(req) - - responseBody, err := openapi.Serialize(rsp.Body, "application/json") - if err != nil { - logger.AmPolicyLog.Errorln(err) - problemDetails := models.ProblemDetails{ - Status: http.StatusInternalServerError, - Cause: "SYSTEM_FAILURE", - Detail: err.Error(), - } - c.JSON(http.StatusInternalServerError, problemDetails) - } else { - c.Data(rsp.Status, "application/json", responseBody) - } -} - -// HTTPPoliciesPolAssoIdGet - -func HTTPPoliciesPolAssoIdGet(c *gin.Context) { - req := httpwrapper.NewRequest(c.Request, nil) - req.Params["polAssoId"], _ = c.Params.Get("polAssoId") - - rsp := producer.HandleGetPoliciesPolAssoId(req) - - responseBody, err := openapi.Serialize(rsp.Body, "application/json") - if err != nil { - logger.AmPolicyLog.Errorln(err) - problemDetails := models.ProblemDetails{ - Status: http.StatusInternalServerError, - Cause: "SYSTEM_FAILURE", - Detail: err.Error(), - } - c.JSON(http.StatusInternalServerError, problemDetails) - } else { - c.Data(rsp.Status, "application/json", responseBody) - } -} - -// HTTPPoliciesPolAssoIdUpdatePost - -func HTTPPoliciesPolAssoIdUpdatePost(c *gin.Context) { - var policyAssociationUpdateRequest models.PolicyAssociationUpdateRequest - - requestBody, err := c.GetRawData() - if err != nil { - problemDetail := models.ProblemDetails{ - Title: "System failure", - Status: http.StatusInternalServerError, - Detail: err.Error(), - Cause: "SYSTEM_FAILURE", - } - logger.AmPolicyLog.Errorf("Get Request Body error: %+v", err) - c.JSON(http.StatusInternalServerError, problemDetail) - return - } - - err = openapi.Deserialize(&policyAssociationUpdateRequest, requestBody, "application/json") - if err != nil { - problemDetail := "[Request Body] " + err.Error() - rsp := models.ProblemDetails{ - Title: "Malformed request syntax", - Status: http.StatusBadRequest, - Detail: problemDetail, - } - logger.AmPolicyLog.Errorln(problemDetail) - c.JSON(http.StatusBadRequest, rsp) - return - } - - req := httpwrapper.NewRequest(c.Request, policyAssociationUpdateRequest) - req.Params["polAssoId"], _ = c.Params.Get("polAssoId") - - rsp := producer.HandleUpdatePostPoliciesPolAssoId(req) - - responseBody, err := openapi.Serialize(rsp.Body, "application/json") - if err != nil { - logger.AmPolicyLog.Errorln(err) - problemDetails := models.ProblemDetails{ - Status: http.StatusInternalServerError, - Cause: "SYSTEM_FAILURE", - Detail: err.Error(), - } - c.JSON(http.StatusInternalServerError, problemDetails) - } else { - c.Data(rsp.Status, "application/json", responseBody) - } -} - -// HTTPPoliciesPost - -func HTTPPoliciesPost(c *gin.Context) { - var policyAssociationRequest models.PolicyAssociationRequest - - requestBody, err := c.GetRawData() - if err != nil { - problemDetail := models.ProblemDetails{ - Title: "System failure", - Status: http.StatusInternalServerError, - Detail: err.Error(), - Cause: "SYSTEM_FAILURE", - } - logger.AmPolicyLog.Errorf("Get Request Body error: %+v", err) - c.JSON(http.StatusInternalServerError, problemDetail) - return - } - - err = openapi.Deserialize(&policyAssociationRequest, requestBody, "application/json") - if err != nil { - problemDetail := "[Request Body] " + err.Error() - rsp := models.ProblemDetails{ - Title: "Malformed request syntax", - Status: http.StatusBadRequest, - Detail: problemDetail, - } - logger.AmPolicyLog.Errorln(problemDetail) - c.JSON(http.StatusBadRequest, rsp) - return - } - - if policyAssociationRequest.Supi == "" || policyAssociationRequest.NotificationUri == "" { - rsp := util.GetProblemDetail("Miss Mandotory IE", util.ERROR_REQUEST_PARAMETERS) - logger.AmPolicyLog.Errorln(rsp.Detail) - c.JSON(int(rsp.Status), rsp) - return - } - - req := httpwrapper.NewRequest(c.Request, policyAssociationRequest) - req.Params["polAssoId"], _ = c.Params.Get("polAssoId") - - rsp := producer.HandlePostPolicies(req) - - for key, val := range rsp.Header { - c.Header(key, val[0]) - } - - responseBody, err := openapi.Serialize(rsp.Body, "application/json") - if err != nil { - logger.AmPolicyLog.Errorln(err) - problemDetails := models.ProblemDetails{ - Status: http.StatusInternalServerError, - Cause: "SYSTEM_FAILURE", - Detail: err.Error(), - } - c.JSON(http.StatusInternalServerError, problemDetails) - } else { - c.Data(rsp.Status, "application/json", responseBody) - } -} diff --git a/internal/sbi/ampolicy/routers.go b/internal/sbi/ampolicy/routers.go deleted file mode 100644 index f32ed29..0000000 --- a/internal/sbi/ampolicy/routers.go +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Npcf_AMPolicyControl - * - * Access and Mobility Policy Control Service API - * - * API version: 1.0.0 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package ampolicy - -import ( - "net/http" - "strings" - - "github.com/gin-gonic/gin" - - "github.com/free5gc/openapi/models" - pcf_context "github.com/free5gc/pcf/internal/context" - "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/internal/util" - "github.com/free5gc/pcf/pkg/factory" - logger_util "github.com/free5gc/util/logger" -) - -// Route is the information for every URI. -type Route struct { - // 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 - // HandlerFunc is the handler function of this route. - HandlerFunc gin.HandlerFunc -} - -// Routes is the list of the generated Route. -type Routes []Route - -// NewRouter returns a new router. -func NewRouter() *gin.Engine { - router := logger_util.NewGinWithLogrus(logger.GinLog) - AddService(router) - return router -} - -func AddService(engine *gin.Engine) *gin.RouterGroup { - group := engine.Group(factory.PcfAMpolicyCtlResUriPrefix) - - routerAuthorizationCheck := util.NewRouterAuthorizationCheck(models.ServiceName_NPCF_AM_POLICY_CONTROL) - group.Use(func(c *gin.Context) { - routerAuthorizationCheck.Check(c, pcf_context.GetSelf()) - }) - - 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 "DELETE": - group.DELETE(route.Pattern, route.HandlerFunc) - case "PATCH": - group.PATCH(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{ - { - "Index", - "GET", - "/", - Index, - }, - - { - "HTTPPoliciesPolAssoIdDelete", - strings.ToUpper("Delete"), - "/policies/:polAssoId", - HTTPPoliciesPolAssoIdDelete, - }, - - { - "HTTPPoliciesPolAssoIdGet", - strings.ToUpper("Get"), - "/policies/:polAssoId", - HTTPPoliciesPolAssoIdGet, - }, - - { - "HTTPPoliciesPolAssoIdUpdatePost", - strings.ToUpper("Post"), - "/policies/:polAssoId/update", - HTTPPoliciesPolAssoIdUpdatePost, - }, - - { - "HTTPPoliciesPost", - strings.ToUpper("Post"), - "/policies", - HTTPPoliciesPost, - }, -} diff --git a/internal/sbi/api_ampolicy.go b/internal/sbi/api_ampolicy.go new file mode 100644 index 0000000..692cd07 --- /dev/null +++ b/internal/sbi/api_ampolicy.go @@ -0,0 +1,152 @@ +package sbi + +import ( + "net/http" + + "github.com/gin-gonic/gin" + + "github.com/free5gc/openapi" + "github.com/free5gc/openapi/models" + "github.com/free5gc/pcf/internal/logger" + "github.com/free5gc/pcf/internal/util" +) + +func (s *Server) HTTPPoliciesPolAssoIdDelete(c *gin.Context) { + polAssoId, _ := c.Params.Get("polAssoId") + + if polAssoId == "" { + problemDetails := &models.ProblemDetails{ + Title: util.ERROR_INITIAL_PARAMETERS, + Status: http.StatusBadRequest, + } + c.JSON(http.StatusBadRequest, problemDetails) + return + } + + s.Processor().HandleDeletePoliciesPolAssoId(c, polAssoId) +} + +// HTTPPoliciesPolAssoIdGet - +func (s *Server) HTTPPoliciesPolAssoIdGet(c *gin.Context) { + polAssoId, _ := c.Params.Get("polAssoId") + + if polAssoId == "" { + problemDetails := &models.ProblemDetails{ + Title: util.ERROR_INITIAL_PARAMETERS, + Status: http.StatusBadRequest, + } + c.JSON(http.StatusBadRequest, problemDetails) + return + } + + s.Processor().HandleGetPoliciesPolAssoId(c, polAssoId) +} + +// HTTPPoliciesPolAssoIdUpdatePost - +func (s *Server) HTTPPoliciesPolAssoIdUpdatePost(c *gin.Context) { + var policyAssociationUpdateRequest models.PolicyAssociationUpdateRequest + + requestBody, err := c.GetRawData() + if err != nil { + problemDetail := models.ProblemDetails{ + Title: "System failure", + Status: http.StatusInternalServerError, + Detail: err.Error(), + Cause: "SYSTEM_FAILURE", + } + logger.AmPolicyLog.Errorf("Get Request Body error: %+v", err) + c.JSON(http.StatusInternalServerError, problemDetail) + return + } + + err = openapi.Deserialize(&policyAssociationUpdateRequest, requestBody, "application/json") + if err != nil { + problemDetail := "[Request Body] " + err.Error() + rsp := models.ProblemDetails{ + Title: "Malformed request syntax", + Status: http.StatusBadRequest, + Detail: problemDetail, + } + logger.AmPolicyLog.Errorln(problemDetail) + c.JSON(http.StatusBadRequest, rsp) + return + } + + polAssoId, _ := c.Params.Get("polAssoId") + + if polAssoId == "" { + problemDetails := &models.ProblemDetails{ + Title: util.ERROR_INITIAL_PARAMETERS, + Status: http.StatusBadRequest, + } + c.JSON(http.StatusBadRequest, problemDetails) + return + } + + s.Processor().HandleUpdatePostPoliciesPolAssoId(c, polAssoId, policyAssociationUpdateRequest) +} + +func (s *Server) HTTPPoliciesPost(c *gin.Context) { + var policyAssociationRequest models.PolicyAssociationRequest + requestBody, err := c.GetRawData() + if err != nil { + problemDetail := models.ProblemDetails{ + Title: "System failure", + Status: http.StatusInternalServerError, + Detail: err.Error(), + Cause: "SYSTEM_FAILURE", + } + logger.AmPolicyLog.Errorf("Get Request Body error: %+v", err) + c.JSON(http.StatusInternalServerError, problemDetail) + return + } + + err = openapi.Deserialize(&policyAssociationRequest, requestBody, "application/json") + if err != nil { + problemDetail := "[Request Body] " + err.Error() + rsp := models.ProblemDetails{ + Title: "Malformed request syntax", + Status: http.StatusBadRequest, + Detail: problemDetail, + } + logger.AmPolicyLog.Errorln(problemDetail) + c.JSON(http.StatusBadRequest, rsp) + return + } + + if policyAssociationRequest.Supi == "" || policyAssociationRequest.NotificationUri == "" { + rsp := util.GetProblemDetail("Miss Mandotory IE", util.ERROR_REQUEST_PARAMETERS) + logger.AmPolicyLog.Errorln(rsp.Detail) + c.JSON(int(rsp.Status), rsp) + return + } + + polAssoId, _ := c.Params.Get("polAssoId") + + s.Processor().HandlePostPolicies(c, polAssoId, policyAssociationRequest) +} + +func (s *Server) getAmPolicyRoutes() []Route { + return []Route{ + { + Method: http.MethodGet, + Pattern: "/policies/:polAssoId", + APIFunc: s.HTTPPoliciesPolAssoIdGet, + }, + { + Method: http.MethodDelete, + Pattern: "/policies/:polAssoId", + APIFunc: s.HTTPPoliciesPolAssoIdDelete, + }, + { + Method: http.MethodPost, + Pattern: "/policies/:polAssoId/update", + APIFunc: s.HTTPPoliciesPolAssoIdUpdatePost, + }, + { + Method: http.MethodPost, + Pattern: "/policies", + APIFunc: s.HTTPPoliciesPost, + }, + } +} diff --git a/internal/sbi/api_bdtpolicy.go b/internal/sbi/api_bdtpolicy.go new file mode 100644 index 0000000..4eff462 --- /dev/null +++ b/internal/sbi/api_bdtpolicy.go @@ -0,0 +1,133 @@ +/* + * Npcf_BDTPolicyControl Service API + * + * The Npcf_BDTPolicyControl Service is used by an NF service consumer to + * retrieve background data transfer policies from the PCF and to update + * the PCF with the background data transfer policy selected by the NF + * service consumer. + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package sbi + +import ( + "net/http" + + "github.com/gin-gonic/gin" + + "github.com/free5gc/openapi" + "github.com/free5gc/openapi/models" + "github.com/free5gc/pcf/internal/logger" + "github.com/free5gc/pcf/internal/util" +) + +func (s *Server) getBdtPolicyRoutes() []Route { + return []Route{ + { + Method: http.MethodPost, + Pattern: "/bdtpolicies", + APIFunc: s.HTTPCreateBDTPolicy, + }, + { + Method: http.MethodGet, + Pattern: "/bdtpolicies/:bdtPolicyId", + APIFunc: s.HTTPGetBDTPolicy, + }, + { + Method: http.MethodPatch, + Pattern: "/bdtpolicies/:bdtPolicyId", + APIFunc: s.HTTPUpdateBDTPolicy, + }, + } +} + +func (s *Server) HTTPCreateBDTPolicy(c *gin.Context) { + var bdtReqData models.BdtReqData + // step 1: retrieve http request body + requestBody, err := c.GetRawData() + if err != nil { + problemDetail := models.ProblemDetails{ + Title: "System failure", + Status: http.StatusInternalServerError, + Detail: err.Error(), + Cause: "SYSTEM_FAILURE", + } + logger.BdtPolicyLog.Errorf("Get Request Body error: %+v", err) + c.JSON(http.StatusInternalServerError, problemDetail) + return + } + + // step 2: convert requestBody to openapi models + err = openapi.Deserialize(&bdtReqData, requestBody, "application/json") + if err != nil { + problemDetail := "[Request Body] " + err.Error() + rsp := models.ProblemDetails{ + Title: "Malformed request syntax", + Status: http.StatusBadRequest, + Detail: problemDetail, + } + logger.BdtPolicyLog.Errorln(problemDetail) + c.JSON(http.StatusBadRequest, rsp) + return + } + + s.Processor().HandleCreateBDTPolicyContextRequest(c, bdtReqData) +} + +func (s *Server) HTTPGetBDTPolicy(c *gin.Context) { + bdtPolicyId := c.Params.ByName("bdtPolicyId") + if bdtPolicyId == "" { + problemDetails := &models.ProblemDetails{ + Title: util.ERROR_INITIAL_PARAMETERS, + Status: http.StatusBadRequest, + } + c.JSON(http.StatusBadRequest, problemDetails) + return + } + s.Processor().HandleGetBDTPolicyContextRequest(c, bdtPolicyId) +} + +// UpdateBDTPolicy - Update an Individual BDT policy +func (s *Server) HTTPUpdateBDTPolicy(c *gin.Context) { + var bdtPolicyDataPatch models.BdtPolicyDataPatch + // step 1: retrieve http request body + requestBody, err := c.GetRawData() + if err != nil { + problemDetail := models.ProblemDetails{ + Title: "System failure", + Status: http.StatusInternalServerError, + Detail: err.Error(), + Cause: "SYSTEM_FAILURE", + } + logger.BdtPolicyLog.Errorf("Get Request Body error: %+v", err) + c.JSON(http.StatusInternalServerError, problemDetail) + return + } + + // step 2: convert requestBody to openapi models + err = openapi.Deserialize(&bdtPolicyDataPatch, requestBody, "application/json") + if err != nil { + problemDetail := "[Request Body] " + err.Error() + rsp := models.ProblemDetails{ + Title: "Malformed request syntax", + Status: http.StatusBadRequest, + Detail: problemDetail, + } + logger.BdtPolicyLog.Errorln(problemDetail) + c.JSON(http.StatusBadRequest, rsp) + return + } + + bdtPolicyId := c.Params.ByName("bdtPolicyId") + if bdtPolicyId == "" { + problemDetails := &models.ProblemDetails{ + Title: util.ERROR_INITIAL_PARAMETERS, + Status: http.StatusBadRequest, + } + c.JSON(http.StatusBadRequest, problemDetails) + return + } + s.Processor().HandleUpdateBDTPolicyContextProcedure(c, bdtPolicyId, bdtPolicyDataPatch) +} diff --git a/internal/sbi/api_httpcallback.go b/internal/sbi/api_httpcallback.go new file mode 100644 index 0000000..20bc5f5 --- /dev/null +++ b/internal/sbi/api_httpcallback.go @@ -0,0 +1,151 @@ +package sbi + +import ( + "net/http" + + "github.com/gin-gonic/gin" + + "github.com/free5gc/openapi" + "github.com/free5gc/openapi/models" + "github.com/free5gc/pcf/internal/logger" + "github.com/free5gc/pcf/internal/util" +) + +func (s *Server) getHttpCallBackRoutes() []Route { + return []Route{ + { + Method: http.MethodPost, + Pattern: "/nudr-notify/policy-data/:supi", + APIFunc: s.HTTPUdrPolicyDataChangeNotify, + }, + { + Method: http.MethodPost, + Pattern: "/nudr-notify/influence-data/:supi/:pduSessionId", + APIFunc: s.HTTPUdrInfluenceDataUpdateNotify, + }, + { + Method: http.MethodPost, + Pattern: "/amfstatus", + APIFunc: s.HTTPAmfStatusChangeNotify, + }, + } +} + +// amf_status_change +func (s *Server) HTTPAmfStatusChangeNotify(c *gin.Context) { + var amfStatusChangeNotification models.AmfStatusChangeNotification + + requestBody, err := c.GetRawData() + if err != nil { + problemDetail := models.ProblemDetails{ + Title: "System failure", + Status: http.StatusInternalServerError, + Detail: err.Error(), + Cause: "SYSTEM_FAILURE", + } + logger.CallbackLog.Errorf("Get Request Body error: %+v", err) + c.JSON(http.StatusInternalServerError, problemDetail) + return + } + + err = openapi.Deserialize(&amfStatusChangeNotification, 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 + } + + s.Processor().HandleAmfStatusChangeNotify(c, amfStatusChangeNotification) +} + +// sm_policy_notify +// Nudr-Notify-smpolicy +func (s *Server) HTTPUdrPolicyDataChangeNotify(c *gin.Context) { + var policyDataChangeNotification models.PolicyDataChangeNotification + + requestBody, err := c.GetRawData() + if err != nil { + problemDetail := models.ProblemDetails{ + Title: "System failure", + Status: http.StatusInternalServerError, + Detail: err.Error(), + Cause: "SYSTEM_FAILURE", + } + logger.CallbackLog.Errorf("Get Request Body error: %+v", err) + c.JSON(http.StatusInternalServerError, problemDetail) + return + } + + err = openapi.Deserialize(&policyDataChangeNotification, 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 + } + + supi := c.Params.ByName("supi") + if supi == "" { + problemDetails := &models.ProblemDetails{ + Title: util.ERROR_INITIAL_PARAMETERS, + Status: http.StatusBadRequest, + } + c.JSON(http.StatusBadRequest, problemDetails) + return + } + s.Processor().HandlePolicyDataChangeNotify(c, supi, policyDataChangeNotification) +} + +// Influence Data Update Notification +func (s *Server) HTTPUdrInfluenceDataUpdateNotify(c *gin.Context) { + var trafficInfluDataNotif []models.TrafficInfluDataNotif + + requestBody, err := c.GetRawData() + if err != nil { + problemDetail := models.ProblemDetails{ + Title: "System failure", + Status: http.StatusInternalServerError, + Detail: err.Error(), + Cause: "SYSTEM_FAILURE", + } + logger.CallbackLog.Errorf("Get Request Body error: %+v", err) + c.JSON(http.StatusInternalServerError, problemDetail) + return + } + + err = openapi.Deserialize(&trafficInfluDataNotif, 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 + } + + supi := c.Params.ByName("supi") + pduSessionId := c.Params.ByName("pduSessionId") + if supi == "" || pduSessionId == "" { + problemDetails := &models.ProblemDetails{ + Title: util.ERROR_INITIAL_PARAMETERS, + Status: http.StatusBadRequest, + } + c.JSON(http.StatusBadRequest, problemDetails) + return + } + s.Processor().HandleInfluenceDataUpdateNotify(c, supi, pduSessionId, trafficInfluDataNotif) +} diff --git a/internal/sbi/api_oam.go b/internal/sbi/api_oam.go new file mode 100644 index 0000000..d6e4199 --- /dev/null +++ b/internal/sbi/api_oam.go @@ -0,0 +1,63 @@ +package sbi + +import ( + "net/http" + + "github.com/gin-contrib/cors" + "github.com/gin-gonic/gin" + + "github.com/free5gc/openapi/models" + "github.com/free5gc/pcf/internal/util" +) + +const ( + CorsConfigMaxAge = 86400 +) + +func (s *Server) setCorsHeader(c *gin.Context) { + // TODO: 1. turn these values into configurable variables + // TODO: 2. use the official cors middleware + s.router.Use(cors.New(cors.Config{ + AllowMethods: []string{"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE"}, + AllowHeaders: []string{ + "Origin", "Content-Length", "Content-Type", "User-Agent", + "Referrer", "Host", "Token", "X-Requested-With", + }, + ExposeHeaders: []string{"Content-Length"}, + AllowCredentials: true, + AllowAllOrigins: true, + MaxAge: CorsConfigMaxAge, + })) + + 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, Content-Length, Accept-Encoding, "+ + "X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With") + c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, PATCH, DELETE") +} + +func (s *Server) HTTPOAMGetAmPolicy(c *gin.Context) { + s.setCorsHeader(c) + + supi := c.Params.ByName("supi") + if supi == "" { + problemDetails := &models.ProblemDetails{ + Title: util.ERROR_INITIAL_PARAMETERS, + Status: http.StatusBadRequest, + } + c.JSON(http.StatusBadRequest, problemDetails) + return + } + s.Processor().HandleOAMGetAmPolicyRequest(c, supi) +} + +func (s *Server) getOamRoutes() []Route { + return []Route{ + { + Method: http.MethodGet, + Pattern: "/am-policy/:supi", + APIFunc: s.HTTPOAMGetAmPolicy, + }, + } +} diff --git a/internal/sbi/api_policyauthorization.go b/internal/sbi/api_policyauthorization.go new file mode 100644 index 0000000..4f2f6e9 --- /dev/null +++ b/internal/sbi/api_policyauthorization.go @@ -0,0 +1,268 @@ +/* + * Npcf_PolicyAuthorization Service API + * + * This is the Policy Authorization Service + * + * API version: 1.0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package sbi + +import ( + "net/http" + + "github.com/gin-gonic/gin" + + "github.com/free5gc/openapi" + "github.com/free5gc/openapi/models" + "github.com/free5gc/pcf/internal/logger" + "github.com/free5gc/pcf/internal/util" +) + +func (s *Server) getPolicyAuthorizationRoutes() []Route { + return []Route{ + { + Method: http.MethodPost, + Pattern: "/app-sessions", + APIFunc: s.HTTPPostAppSessions, + }, + { + Method: http.MethodDelete, + Pattern: "/app-sessions/:appSessionId/events-subscription", + APIFunc: s.HTTPDeleteEventsSubsc, + }, + { + Method: http.MethodPut, + Pattern: "/app-sessions/:appSessionId/events-subscription", + APIFunc: s.HTTPUpdateEventsSubsc, + }, + { + Method: http.MethodPost, + Pattern: "/app-sessions/:appSessionId/delete", + APIFunc: s.HTTPDeleteAppSession, + }, + { + Method: http.MethodGet, + Pattern: "/app-sessions/:appSessionId", + APIFunc: s.HTTPGetAppSession, + }, + { + Method: http.MethodPatch, + Pattern: "/app-sessions/:appSessionId", + APIFunc: s.HTTPModAppSession, + }, + } +} + +// api_application_session +// HTTPPostAppSessions - Creates a new Individual Application Session Context resource +func (s *Server) HTTPPostAppSessions(c *gin.Context) { + var appSessionContext models.AppSessionContext + + requestBody, err := c.GetRawData() + if err != nil { + problemDetail := models.ProblemDetails{ + Title: "System failure", + Status: http.StatusInternalServerError, + Detail: err.Error(), + Cause: "SYSTEM_FAILURE", + } + logger.PolicyAuthLog.Errorf("Get Request Body error: %+v", err) + c.JSON(http.StatusInternalServerError, problemDetail) + return + } + + err = openapi.Deserialize(&appSessionContext, requestBody, "application/json") + if err != nil { + problemDetail := "[Request Body] " + err.Error() + rsp := models.ProblemDetails{ + Title: "Malformed request syntax", + Status: http.StatusBadRequest, + Detail: problemDetail, + } + logger.PolicyAuthLog.Errorln(problemDetail) + c.JSON(http.StatusBadRequest, rsp) + return + } + + ascReqData := appSessionContext.AscReqData + if ascReqData == nil || ascReqData.SuppFeat == "" || ascReqData.NotifUri == "" { + // Check Mandatory IEs + rsp := util.GetProblemDetail("Errorneous/Missing Mandotory IE", util.ERROR_INITIAL_PARAMETERS) + logger.PolicyAuthLog.Errorln(rsp.Detail) + c.JSON(int(rsp.Status), rsp) + return + } + + s.Processor().HandlePostAppSessionsContext(c, appSessionContext) +} + +// api_events_subscription +// HTTPDeleteEventsSubsc - deletes the Events Subscription subresource +func (s *Server) HTTPDeleteEventsSubsc(c *gin.Context) { + appSessionId := c.Params.ByName("appSessionId") + if appSessionId == "" { + problemDetails := &models.ProblemDetails{ + Title: util.ERROR_INITIAL_PARAMETERS, + Status: http.StatusBadRequest, + } + c.JSON(http.StatusBadRequest, problemDetails) + return + } + + s.Processor().HandleDeleteEventsSubscContext(c, appSessionId) +} + +// HTTPUpdateEventsSubsc - creates or modifies an Events Subscription subresource +func (s *Server) HTTPUpdateEventsSubsc(c *gin.Context) { + var eventsSubscReqData models.EventsSubscReqData + + requestBody, err := c.GetRawData() + if err != nil { + problemDetail := models.ProblemDetails{ + Title: "System failure", + Status: http.StatusInternalServerError, + Detail: err.Error(), + Cause: "SYSTEM_FAILURE", + } + logger.PolicyAuthLog.Errorf("Get Request Body error: %+v", err) + c.JSON(http.StatusInternalServerError, problemDetail) + return + } + + err = openapi.Deserialize(&eventsSubscReqData, requestBody, "application/json") + if err != nil { + problemDetail := "[Request Body] " + err.Error() + rsp := models.ProblemDetails{ + Title: "Malformed request syntax", + Status: http.StatusBadRequest, + Detail: problemDetail, + } + logger.PolicyAuthLog.Errorln(problemDetail) + c.JSON(http.StatusBadRequest, rsp) + return + } + + if eventsSubscReqData.Events == nil || eventsSubscReqData.NotifUri == "" { + problemDetail := util.GetProblemDetail("Errorneous/Missing Mandotory IE", util.ERROR_REQUEST_PARAMETERS) + logger.PolicyAuthLog.Errorln(problemDetail.Detail) + c.JSON(int(problemDetail.Status), problemDetail) + return + } + + appSessionId := c.Params.ByName("appSessionId") + if appSessionId == "" { + problemDetails := &models.ProblemDetails{ + Title: util.ERROR_INITIAL_PARAMETERS, + Status: http.StatusBadRequest, + } + c.JSON(http.StatusBadRequest, problemDetails) + return + } + + s.Processor().HandleUpdateEventsSubscContext(c, appSessionId, eventsSubscReqData) +} + +func (s *Server) HTTPDeleteAppSession(c *gin.Context) { + var eventsSubscReqData *models.EventsSubscReqData + + requestBody, err := c.GetRawData() + if err != nil { + problemDetail := models.ProblemDetails{ + Title: "System failure", + Status: http.StatusInternalServerError, + Detail: err.Error(), + Cause: "SYSTEM_FAILURE", + } + logger.PolicyAuthLog.Errorf("Get Request Body error: %+v", err) + c.JSON(http.StatusInternalServerError, problemDetail) + return + } + + // EventsSubscReqData is Optional + if len(requestBody) > 0 { + err = openapi.Deserialize(&eventsSubscReqData, requestBody, "application/json") + if err != nil { + problemDetail := "[Request Body] " + err.Error() + rsp := models.ProblemDetails{ + Title: "Malformed request syntax", + Status: http.StatusBadRequest, + Detail: problemDetail, + } + logger.PolicyAuthLog.Errorln(problemDetail) + c.JSON(http.StatusBadRequest, rsp) + return + } + } + + appSessionId := c.Params.ByName("appSessionId") + if appSessionId == "" { + problemDetails := &models.ProblemDetails{ + Title: util.ERROR_INITIAL_PARAMETERS, + Status: http.StatusBadRequest, + } + c.JSON(http.StatusBadRequest, problemDetails) + return + } + + s.Processor().HandleDeleteAppSessionContext(c, appSessionId, eventsSubscReqData) +} + +// HTTPGetAppSession - Reads an existing Individual Application Session Context +func (s *Server) HTTPGetAppSession(c *gin.Context) { + appSessionId := c.Params.ByName("appSessionId") + if appSessionId == "" { + problemDetails := &models.ProblemDetails{ + Title: util.ERROR_INITIAL_PARAMETERS, + Status: http.StatusBadRequest, + } + c.JSON(http.StatusBadRequest, problemDetails) + return + } + + s.Processor().HandleGetAppSessionContext(c, appSessionId) +} + +// HTTPModAppSession - Modifies an existing Individual Application Session Context +func (s *Server) HTTPModAppSession(c *gin.Context) { + var appSessionContextUpdateData models.AppSessionContextUpdateData + + requestBody, err := c.GetRawData() + if err != nil { + problemDetail := models.ProblemDetails{ + Title: "System failure", + Status: http.StatusInternalServerError, + Detail: err.Error(), + Cause: "SYSTEM_FAILURE", + } + logger.PolicyAuthLog.Errorf("Get Request Body error: %+v", err) + c.JSON(http.StatusInternalServerError, problemDetail) + return + } + + err = openapi.Deserialize(&appSessionContextUpdateData, requestBody, "application/json") + if err != nil { + problemDetail := "[Request Body] " + err.Error() + rsp := models.ProblemDetails{ + Title: "Malformed request syntax", + Status: http.StatusBadRequest, + Detail: problemDetail, + } + logger.PolicyAuthLog.Errorln(problemDetail) + c.JSON(http.StatusBadRequest, rsp) + return + } + + appSessionId := c.Params.ByName("appSessionId") + if appSessionId == "" { + problemDetails := &models.ProblemDetails{ + Title: util.ERROR_INITIAL_PARAMETERS, + Status: http.StatusBadRequest, + } + c.JSON(http.StatusBadRequest, problemDetails) + return + } + + s.Processor().HandleModAppSessionContext(c, appSessionId, appSessionContextUpdateData) +} diff --git a/internal/sbi/api_smpolicy.go b/internal/sbi/api_smpolicy.go new file mode 100644 index 0000000..9e770fd --- /dev/null +++ b/internal/sbi/api_smpolicy.go @@ -0,0 +1,150 @@ +/* + * Npcf_SMPolicyControl + * + * Session Management Policy Control Service + * + * API version: 1.0.1 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package sbi + +import ( + "net/http" + + "github.com/gin-gonic/gin" + + "github.com/free5gc/openapi" + "github.com/free5gc/openapi/models" + "github.com/free5gc/pcf/internal/logger" + "github.com/free5gc/pcf/internal/util" +) + +func (s *Server) getSmPolicyRoutes() []Route { + return []Route{ + { + Method: http.MethodPost, + Pattern: "/sm-policies", + APIFunc: s.HTTPSmPoliciesPost, + }, + { + Method: http.MethodPost, + Pattern: "/sm-policies/:smPolicyId/delete", + APIFunc: s.HTTPSmPoliciesSmPolicyIdDeletePost, + }, + { + Method: http.MethodGet, + Pattern: "/sm-policies/:smPolicyId", + APIFunc: s.HTTPSmPoliciesSmPolicyIDGet, + }, + { + Method: http.MethodPost, + Pattern: "/sm-policies/:smPolicyId/update", + APIFunc: s.HTTPSmPoliciesSmPolicyIdUpdatePost, + }, + } +} + +// SmPoliciesPost - +func (s *Server) HTTPSmPoliciesPost(c *gin.Context) { + var smPolicyContextData models.SmPolicyContextData + // step 1: retrieve http request body + requestBody, err := c.GetRawData() + if err != nil { + problemDetail := models.ProblemDetails{ + Title: "System failure", + Status: http.StatusInternalServerError, + Detail: err.Error(), + Cause: "SYSTEM_FAILURE", + } + logger.SmPolicyLog.Errorf("Get Request Body error: %+v", err) + c.JSON(http.StatusInternalServerError, problemDetail) + return + } + + // step 2: convert requestBody to openapi models + err = openapi.Deserialize(&smPolicyContextData, requestBody, "application/json") + if err != nil { + problemDetail := "[Request Body] " + err.Error() + rsp := models.ProblemDetails{ + Title: "Malformed request syntax", + Status: http.StatusBadRequest, + Detail: problemDetail, + } + logger.SmPolicyLog.Errorln(problemDetail) + c.JSON(http.StatusBadRequest, rsp) + return + } + + s.Processor().HandleCreateSmPolicyRequest(c, smPolicyContextData) +} + +// SmPoliciesSmPolicyIdDeletePost - +func (s *Server) HTTPSmPoliciesSmPolicyIdDeletePost(c *gin.Context) { + smPolicyId := c.Params.ByName("smPolicyId") + if smPolicyId == "" { + problemDetails := &models.ProblemDetails{ + Title: util.ERROR_INITIAL_PARAMETERS, + Status: http.StatusBadRequest, + } + c.JSON(http.StatusBadRequest, problemDetails) + return + } + s.Processor().HandleDeleteSmPolicyContextRequest(c, smPolicyId) +} + +// SmPoliciesSmPolicyIdGet - +func (s *Server) HTTPSmPoliciesSmPolicyIDGet(c *gin.Context) { + smPolicyId := c.Params.ByName("smPolicyId") + if smPolicyId == "" { + problemDetails := &models.ProblemDetails{ + Title: util.ERROR_INITIAL_PARAMETERS, + Status: http.StatusBadRequest, + } + c.JSON(http.StatusBadRequest, problemDetails) + return + } + s.Processor().HandleGetSmPolicyContextRequest(c, smPolicyId) +} + +func (s *Server) HTTPSmPoliciesSmPolicyIdUpdatePost(c *gin.Context) { + var smPolicyUpdateContextData models.SmPolicyUpdateContextData + // step 1: retrieve http request body + requestBody, err := c.GetRawData() + if err != nil { + problemDetail := models.ProblemDetails{ + Title: "System failure", + Status: http.StatusInternalServerError, + Detail: err.Error(), + Cause: "SYSTEM_FAILURE", + } + logger.SmPolicyLog.Errorf("Get Request Body error: %+v", err) + c.JSON(http.StatusInternalServerError, problemDetail) + return + } + + // step 2: convert requestBody to openapi models + err = openapi.Deserialize(&smPolicyUpdateContextData, requestBody, "application/json") + if err != nil { + problemDetail := "[Request Body] " + err.Error() + rsp := models.ProblemDetails{ + Title: "Malformed request syntax", + Status: http.StatusBadRequest, + Detail: problemDetail, + } + logger.SmPolicyLog.Errorln(problemDetail) + c.JSON(http.StatusBadRequest, rsp) + return + } + + smPolicyId := c.Params.ByName("smPolicyId") + if smPolicyId == "" { + problemDetails := &models.ProblemDetails{ + Title: util.ERROR_INITIAL_PARAMETERS, + Status: http.StatusBadRequest, + } + c.JSON(http.StatusBadRequest, problemDetails) + return + } + s.Processor().HandleUpdateSmPolicyContextRequest(c, smPolicyId, smPolicyUpdateContextData) +} diff --git a/internal/sbi/api_uepolicy.go b/internal/sbi/api_uepolicy.go new file mode 100644 index 0000000..9eb778d --- /dev/null +++ b/internal/sbi/api_uepolicy.go @@ -0,0 +1,61 @@ +/* + * Npcf_UEPolicyControl + * + * UE Policy Control Service API + * + * API version: 1.0.0 + * Generated by: OpenAPI Generator (https://openapi-generator.tech) + */ + +package sbi + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +func (s *Server) getUePolicyRoutes() []Route { + return []Route{ + { + Method: http.MethodDelete, + Pattern: "/policies/{polAssoId}", + APIFunc: s.PoliciesPolAssoIdDelete, + }, + { + Method: http.MethodGet, + Pattern: "/policies/{polAssoId}", + APIFunc: s.PoliciesPolAssoIdGet, + }, + { + Method: http.MethodPost, + Pattern: "/policies/{polAssoId}/update", + APIFunc: s.PoliciesPolAssoIdUpdatePost, + }, + { + Method: http.MethodPost, + Pattern: "/policies", + APIFunc: s.PoliciesPost, + }, + } +} + +// PoliciesPolAssoIdDelete - +func (s *Server) PoliciesPolAssoIdDelete(c *gin.Context) { + c.JSON(http.StatusNotImplemented, nil) +} + +// PoliciesPolAssoIdGet - +func (s *Server) PoliciesPolAssoIdGet(c *gin.Context) { + c.JSON(http.StatusNotImplemented, nil) +} + +// PoliciesPolAssoIdUpdatePost - +func (s *Server) PoliciesPolAssoIdUpdatePost(c *gin.Context) { + c.JSON(http.StatusNotImplemented, nil) +} + +// PoliciesPost - +func (s *Server) PoliciesPost(c *gin.Context) { + c.JSON(http.StatusNotImplemented, nil) +} diff --git a/internal/sbi/bdtpolicy/api_bdt_policies_collection_routers.go b/internal/sbi/bdtpolicy/api_bdt_policies_collection_routers.go deleted file mode 100644 index 7f636d6..0000000 --- a/internal/sbi/bdtpolicy/api_bdt_policies_collection_routers.go +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Npcf_BDTPolicyControl Service API - * - * The Npcf_BDTPolicyControl Service is used by an NF service consumer to - * retrieve background data transfer policies from the PCF and to update - * the PCF with the background data transfer policy selected by the NF - * service consumer. - * - * API version: 1.0.0 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package bdtpolicy - -import ( - "net/http" - - "github.com/gin-gonic/gin" - - "github.com/free5gc/openapi" - "github.com/free5gc/openapi/models" - "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/internal/sbi/producer" - "github.com/free5gc/util/httpwrapper" -) - -// CreateBDTPolicy - Create a new Individual BDT policy -func HTTPCreateBDTPolicy(c *gin.Context) { - var bdtReqData models.BdtReqData - // step 1: retrieve http request body - requestBody, err := c.GetRawData() - if err != nil { - problemDetail := models.ProblemDetails{ - Title: "System failure", - Status: http.StatusInternalServerError, - Detail: err.Error(), - Cause: "SYSTEM_FAILURE", - } - logger.BdtPolicyLog.Errorf("Get Request Body error: %+v", err) - c.JSON(http.StatusInternalServerError, problemDetail) - return - } - - // step 2: convert requestBody to openapi models - err = openapi.Deserialize(&bdtReqData, requestBody, "application/json") - if err != nil { - problemDetail := "[Request Body] " + err.Error() - rsp := models.ProblemDetails{ - Title: "Malformed request syntax", - Status: http.StatusBadRequest, - Detail: problemDetail, - } - logger.BdtPolicyLog.Errorln(problemDetail) - c.JSON(http.StatusBadRequest, rsp) - return - } - - req := httpwrapper.NewRequest(c.Request, bdtReqData) - rsp := producer.HandleCreateBDTPolicyContextRequest(req) - // step 5: response - for key, val := range rsp.Header { // header response is optional - c.Header(key, val[0]) - } - responseBody, err := openapi.Serialize(rsp.Body, "application/json") - if err != nil { - logger.BdtPolicyLog.Errorln(err) - problemDetails := models.ProblemDetails{ - Status: http.StatusInternalServerError, - Cause: "SYSTEM_FAILURE", - Detail: err.Error(), - } - c.JSON(http.StatusInternalServerError, problemDetails) - } else { - c.Data(rsp.Status, "application/json", responseBody) - } -} diff --git a/internal/sbi/bdtpolicy/api_individual_bdt_policy_document_routers.go b/internal/sbi/bdtpolicy/api_individual_bdt_policy_document_routers.go deleted file mode 100644 index 3f1f061..0000000 --- a/internal/sbi/bdtpolicy/api_individual_bdt_policy_document_routers.go +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Npcf_BDTPolicyControl Service API - * - * The Npcf_BDTPolicyControl Service is used by an NF service consumer to - * retrieve background data transfer policies from the PCF and to update - * the PCF with the background data transfer policy selected by the NF - * service consumer. - * - * API version: 1.0.0 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package bdtpolicy - -import ( - "net/http" - - "github.com/gin-gonic/gin" - - "github.com/free5gc/openapi" - "github.com/free5gc/openapi/models" - "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/internal/sbi/producer" - "github.com/free5gc/util/httpwrapper" -) - -// GetBDTPolicy - Read an Individual BDT policy -func HTTPGetBDTPolicy(c *gin.Context) { - req := httpwrapper.NewRequest(c.Request, nil) - req.Params["bdtPolicyId"] = c.Params.ByName("bdtPolicyId") - - rsp := producer.HandleGetBDTPolicyContextRequest(req) - - responseBody, err := openapi.Serialize(rsp.Body, "application/json") - if err != nil { - logger.BdtPolicyLog.Errorln(err) - problemDetails := models.ProblemDetails{ - Status: http.StatusInternalServerError, - Cause: "SYSTEM_FAILURE", - Detail: err.Error(), - } - c.JSON(http.StatusInternalServerError, problemDetails) - } else { - c.Data(rsp.Status, "application/json", responseBody) - } -} - -// UpdateBDTPolicy - Update an Individual BDT policy -func HTTPUpdateBDTPolicy(c *gin.Context) { - var bdtPolicyDataPatch models.BdtPolicyDataPatch - // step 1: retrieve http request body - requestBody, err := c.GetRawData() - if err != nil { - problemDetail := models.ProblemDetails{ - Title: "System failure", - Status: http.StatusInternalServerError, - Detail: err.Error(), - Cause: "SYSTEM_FAILURE", - } - logger.BdtPolicyLog.Errorf("Get Request Body error: %+v", err) - c.JSON(http.StatusInternalServerError, problemDetail) - return - } - - // step 2: convert requestBody to openapi models - err = openapi.Deserialize(&bdtPolicyDataPatch, requestBody, "application/json") - if err != nil { - problemDetail := "[Request Body] " + err.Error() - rsp := models.ProblemDetails{ - Title: "Malformed request syntax", - Status: http.StatusBadRequest, - Detail: problemDetail, - } - logger.BdtPolicyLog.Errorln(problemDetail) - c.JSON(http.StatusBadRequest, rsp) - return - } - - req := httpwrapper.NewRequest(c.Request, bdtPolicyDataPatch) - req.Params["bdtPolicyId"] = c.Params.ByName("bdtPolicyId") - - rsp := producer.HandleUpdateBDTPolicyContextProcedure(req) - - responseBody, err := openapi.Serialize(rsp.Body, "application/json") - if err != nil { - logger.BdtPolicyLog.Errorln(err) - problemDetails := models.ProblemDetails{ - Status: http.StatusInternalServerError, - Cause: "SYSTEM_FAILURE", - Detail: err.Error(), - } - c.JSON(http.StatusInternalServerError, problemDetails) - } else { - c.Data(rsp.Status, "application/json", responseBody) - } -} diff --git a/internal/sbi/bdtpolicy/routers.go b/internal/sbi/bdtpolicy/routers.go deleted file mode 100644 index 5c1b719..0000000 --- a/internal/sbi/bdtpolicy/routers.go +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Npcf_BDTPolicyControl Service API - * - * The Npcf_BDTPolicyControl Service is used by an NF service consumer to - * retrieve background data transfer policies from the PCF and to update - * the PCF with the background data transfer policy selected by the NF - * service consumer. - * - * API version: 1.0.0 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package bdtpolicy - -import ( - "net/http" - "strings" - - "github.com/gin-gonic/gin" - - "github.com/free5gc/openapi/models" - pcf_context "github.com/free5gc/pcf/internal/context" - "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/internal/util" - "github.com/free5gc/pcf/pkg/factory" - logger_util "github.com/free5gc/util/logger" -) - -// Route is the information for every URI. -type Route struct { - // 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 - // HandlerFunc is the handler function of this route. - HandlerFunc gin.HandlerFunc -} - -// Routes is the list of the generated Route. -type Routes []Route - -// NewRouter returns a new router. -func NewRouter() *gin.Engine { - router := logger_util.NewGinWithLogrus(logger.GinLog) - AddService(router) - return router -} - -func AddService(engine *gin.Engine) *gin.RouterGroup { - group := engine.Group(factory.PcfBdtPolicyCtlResUriPrefix) - - routerAuthorizationCheck := util.NewRouterAuthorizationCheck(models.ServiceName_NPCF_BDTPOLICYCONTROL) - group.Use(func(c *gin.Context) { - routerAuthorizationCheck.Check(c, pcf_context.GetSelf()) - }) - - 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 "DELETE": - group.DELETE(route.Pattern, route.HandlerFunc) - case "PATCH": - group.PATCH(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{ - { - "Index", - "GET", - "/", - Index, - }, - - { - "CreateBDTPolicy", - strings.ToUpper("Post"), - "/bdtpolicies", - HTTPCreateBDTPolicy, - }, - - { - "GetBDTPolicy", - strings.ToUpper("Get"), - "/bdtpolicies/:bdtPolicyId", - HTTPGetBDTPolicy, - }, - - { - "UpdateBDTPolicy", - strings.ToUpper("Patch"), - "/bdtpolicies/:bdtPolicyId", - HTTPUpdateBDTPolicy, - }, -} diff --git a/internal/sbi/consumer/communication.go b/internal/sbi/consumer/amf_service.go similarity index 59% rename from internal/sbi/consumer/communication.go rename to internal/sbi/consumer/amf_service.go index f7060b8..daea3d1 100644 --- a/internal/sbi/consumer/communication.go +++ b/internal/sbi/consumer/amf_service.go @@ -3,27 +3,60 @@ package consumer import ( "fmt" "strings" + "sync" "github.com/free5gc/openapi" + "github.com/free5gc/openapi/Namf_Communication" "github.com/free5gc/openapi/models" pcf_context "github.com/free5gc/pcf/internal/context" "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/internal/util" "github.com/free5gc/pcf/pkg/factory" ) -func AmfStatusChangeSubscribe(amfUri string, guamiList []models.Guami) ( +type namfService struct { + consumer *Consumer + + nfComMu sync.RWMutex + + nfComClients map[string]*Namf_Communication.APIClient +} + +func (s *namfService) getNFCommunicationClient(uri string) *Namf_Communication.APIClient { + if uri == "" { + return nil + } + s.nfComMu.RLock() + client, ok := s.nfComClients[uri] + if ok { + defer s.nfComMu.RUnlock() + return client + } + + configuration := Namf_Communication.NewConfiguration() + configuration.SetBasePath(uri) + client = Namf_Communication.NewAPIClient(configuration) + + s.nfComMu.RUnlock() + s.nfComMu.Lock() + defer s.nfComMu.Unlock() + s.nfComClients[uri] = client + return client +} + +func (s *namfService) AmfStatusChangeSubscribe(amfUri string, guamiList []models.Guami) ( problemDetails *models.ProblemDetails, err error, ) { logger.ConsumerLog.Debugf("PCF Subscribe to AMF status[%+v]", amfUri) - pcfSelf := pcf_context.GetSelf() - client := util.GetNamfClient(amfUri) + pcfContext := s.consumer.pcf.Context() + + // Set client and set url + client := s.getNFCommunicationClient(amfUri) subscriptionData := models.SubscriptionData{ - AmfStatusUri: fmt.Sprintf("%s"+factory.PcfCallbackResUriPrefix+"/amfstatus", pcfSelf.GetIPv4Uri()), + AmfStatusUri: fmt.Sprintf("%s"+factory.PcfCallbackResUriPrefix+"/amfstatus", pcfContext.GetIPv4Uri()), GuamiList: guamiList, } - ctx, pd, err := pcf_context.GetSelf().GetTokenCtx(models.ServiceName_NAMF_COMM, models.NfType_AMF) + ctx, pd, err := pcfContext.GetTokenCtx(models.ServiceName_NAMF_COMM, models.NfType_AMF) if err != nil { return pd, err } @@ -45,7 +78,7 @@ func AmfStatusChangeSubscribe(amfUri string, guamiList []models.Guami) ( AmfStatusUri: res.AmfStatusUri, GuamiList: res.GuamiList, } - pcfSelf.NewAmfStatusSubscription(subscriptionID, amfStatusSubsData) + pcfContext.NewAmfStatusSubscription(subscriptionID, amfStatusSubsData) } else if httpResp != nil { if httpResp.Status != localErr.Error() { err = localErr diff --git a/internal/sbi/consumer/consumer.go b/internal/sbi/consumer/consumer.go new file mode 100644 index 0000000..b5843f1 --- /dev/null +++ b/internal/sbi/consumer/consumer.go @@ -0,0 +1,51 @@ +package consumer + +import ( + "context" + + "github.com/free5gc/openapi/Namf_Communication" + "github.com/free5gc/openapi/Nnrf_NFDiscovery" + "github.com/free5gc/openapi/Nnrf_NFManagement" + "github.com/free5gc/openapi/Nudr_DataRepository" + pcf_context "github.com/free5gc/pcf/internal/context" + "github.com/free5gc/pcf/pkg/factory" +) + +type pcf interface { + Config() *factory.Config + Context() *pcf_context.PCFContext + CancelContext() context.Context +} + +type Consumer struct { + pcf + + // consumer services + *nnrfService + *namfService + *nudrService +} + +func NewConsumer(pcf pcf) (*Consumer, error) { + c := &Consumer{ + pcf: pcf, + } + + c.nnrfService = &nnrfService{ + consumer: c, + nfMngmntClients: make(map[string]*Nnrf_NFManagement.APIClient), + nfDiscClients: make(map[string]*Nnrf_NFDiscovery.APIClient), + } + + c.namfService = &namfService{ + consumer: c, + nfComClients: make(map[string]*Namf_Communication.APIClient), + } + + c.nudrService = &nudrService{ + consumer: c, + nfDataSubClients: make(map[string]*Nudr_DataRepository.APIClient), + } + + return c, nil +} diff --git a/internal/sbi/consumer/nf_discovery.go b/internal/sbi/consumer/nf_discovery.go deleted file mode 100644 index 224c589..0000000 --- a/internal/sbi/consumer/nf_discovery.go +++ /dev/null @@ -1,118 +0,0 @@ -package consumer - -import ( - "fmt" - "net/http" - - "github.com/antihax/optional" - - "github.com/free5gc/openapi/Nnrf_NFDiscovery" - "github.com/free5gc/openapi/models" - pcf_context "github.com/free5gc/pcf/internal/context" - "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/internal/util" -) - -func SendSearchNFInstances( - 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) - - ctx, _, err := pcf_context.GetSelf().GetTokenCtx(models.ServiceName_NNRF_DISC, models.NfType_NRF) - if err != nil { - return nil, err - } - - result, res, err := client.NFInstancesStoreApi.SearchNFInstances(ctx, targetNfType, requestNfType, ¶m) - if err != nil { - logger.ConsumerLog.Errorf("SearchNFInstances failed: %+v", err) - } - defer func() { - if resCloseErr := res.Body.Close(); resCloseErr != nil { - logger.ConsumerLog.Errorf("NFInstancesStoreApi response body cannot close: %+v", resCloseErr) - } - }() - if res != nil && res.StatusCode == http.StatusTemporaryRedirect { - return nil, fmt.Errorf("Temporary Redirect For Non NRF Consumer") - } - - return &result, nil -} - -func SendNFInstancesUDR(nrfUri, id string) string { - targetNfType := models.NfType_UDR - requestNfType := models.NfType_PCF - localVarOptionals := Nnrf_NFDiscovery.SearchNFInstancesParamOpts{ - // DataSet: optional.NewInterface(models.DataSetId_SUBSCRIPTION), - } - // switch types { - // case NFDiscoveryToUDRParamSupi: - // localVarOptionals.Supi = optional.NewString(id) - // case NFDiscoveryToUDRParamExtGroupId: - // localVarOptionals.ExternalGroupIdentity = optional.NewString(id) - // case NFDiscoveryToUDRParamGpsi: - // localVarOptionals.Gpsi = optional.NewString(id) - // } - - result, err := SendSearchNFInstances(nrfUri, targetNfType, requestNfType, localVarOptionals) - if err != nil { - logger.ConsumerLog.Error(err.Error()) - return "" - } - for _, profile := range result.NfInstances { - if uri := util.SearchNFServiceUri(profile, models.ServiceName_NUDR_DR, models.NfServiceStatus_REGISTERED); uri != "" { - return uri - } - } - return "" -} - -func SendNFInstancesBSF(nrfUri string) string { - targetNfType := models.NfType_BSF - requestNfType := models.NfType_PCF - localVarOptionals := Nnrf_NFDiscovery.SearchNFInstancesParamOpts{} - - result, err := SendSearchNFInstances(nrfUri, targetNfType, requestNfType, localVarOptionals) - if err != nil { - logger.ConsumerLog.Error(err.Error()) - return "" - } - for _, profile := range result.NfInstances { - if uri := util.SearchNFServiceUri(profile, models.ServiceName_NBSF_MANAGEMENT, - models.NfServiceStatus_REGISTERED); uri != "" { - return uri - } - } - return "" -} - -func SendNFInstancesAMF(nrfUri string, guami models.Guami, serviceName models.ServiceName) string { - targetNfType := models.NfType_AMF - requestNfType := models.NfType_PCF - - localVarOptionals := Nnrf_NFDiscovery.SearchNFInstancesParamOpts{ - Guami: optional.NewInterface(util.MarshToJsonString(guami)), - } - // switch types { - // case NFDiscoveryToUDRParamSupi: - // localVarOptionals.Supi = optional.NewString(id) - // case NFDiscoveryToUDRParamExtGroupId: - // localVarOptionals.ExternalGroupIdentity = optional.NewString(id) - // case NFDiscoveryToUDRParamGpsi: - // localVarOptionals.Gpsi = optional.NewString(id) - // } - - result, err := SendSearchNFInstances(nrfUri, targetNfType, requestNfType, localVarOptionals) - if err != nil { - logger.ConsumerLog.Error(err.Error()) - return "" - } - for _, profile := range result.NfInstances { - return util.SearchNFServiceUri(profile, serviceName, models.NfServiceStatus_REGISTERED) - } - return "" -} diff --git a/internal/sbi/consumer/nf_management.go b/internal/sbi/consumer/nf_management.go deleted file mode 100644 index e0c00b3..0000000 --- a/internal/sbi/consumer/nf_management.go +++ /dev/null @@ -1,143 +0,0 @@ -package consumer - -import ( - "fmt" - "net/http" - "strings" - "time" - - "github.com/pkg/errors" - - "github.com/free5gc/openapi" - "github.com/free5gc/openapi/Nnrf_NFManagement" - "github.com/free5gc/openapi/models" - pcf_context "github.com/free5gc/pcf/internal/context" - "github.com/free5gc/pcf/internal/logger" -) - -func BuildNFInstance(context *pcf_context.PCFContext) (profile models.NfProfile, err error) { - profile.NfInstanceId = context.NfId - profile.NfType = models.NfType_PCF - profile.NfStatus = models.NfStatus_REGISTERED - profile.Ipv4Addresses = append(profile.Ipv4Addresses, context.RegisterIPv4) - service := []models.NfService{} - for _, nfService := range context.NfService { - service = append(service, nfService) - } - profile.NfServices = &service - profile.PcfInfo = &models.PcfInfo{ - DnnList: []string{ - "free5gc", - "internet", - }, - // SupiRanges: &[]models.SupiRange{ - // { - // //from TS 29.510 6.1.6.2.9 example2 - // //no need to set supirange in this moment 2019/10/4 - // Start: "123456789040000", - // End: "123456789059999", - // Pattern: "^imsi-12345678904[0-9]{4}$", - // }, - // }, - } - if context.Locality != "" { - profile.Locality = context.Locality - } - return -} - -func SendRegisterNFInstance(nrfUri, nfInstanceId string, profile models.NfProfile) ( - resouceNrfUri string, retrieveNfInstanceID string, err error, -) { - // Set client and set url - configuration := Nnrf_NFManagement.NewConfiguration() - configuration.SetBasePath(nrfUri) - apiClient := Nnrf_NFManagement.NewAPIClient(configuration) - - ctx, _, err := pcf_context.GetSelf().GetTokenCtx(models.ServiceName_NNRF_NFM, models.NfType_NRF) - if err != nil { - return "", "", - errors.Errorf("SendRegisterNFInstance error: %+v", err) - } - - var res *http.Response - var nf models.NfProfile - for { - nf, res, err = apiClient.NFInstanceIDDocumentApi.RegisterNFInstance(ctx, nfInstanceId, profile) - if err != nil || res == nil { - // TODO : add log - fmt.Println(fmt.Errorf("PCF register to NRF Error[%v]", err.Error())) - time.Sleep(2 * time.Second) - continue - } - defer func() { - if resCloseErr := res.Body.Close(); resCloseErr != nil { - logger.ConsumerLog.Errorf("RegisterNFInstance response body cannot close: %+v", resCloseErr) - } - }() - status := res.StatusCode - if status == http.StatusOK { - // NFUpdate - break - } else if status == http.StatusCreated { - // NFRegister - resourceUri := res.Header.Get("Location") - resouceNrfUri = resourceUri[:strings.Index(resourceUri, "/nnrf-nfm/")] - retrieveNfInstanceID = resourceUri[strings.LastIndex(resourceUri, "/")+1:] - - oauth2 := false - if nf.CustomInfo != nil { - v, ok := nf.CustomInfo["oauth2"].(bool) - if ok { - oauth2 = v - logger.MainLog.Infoln("OAuth2 setting receive from NRF:", oauth2) - } - } - pcf_context.GetSelf().OAuth2Required = oauth2 - - if oauth2 && pcf_context.GetSelf().NrfCertPem == "" { - logger.CfgLog.Error("OAuth2 enable but no nrfCertPem provided in config.") - } - break - } else { - fmt.Println("NRF return wrong status code", status) - } - } - return resouceNrfUri, retrieveNfInstanceID, err -} - -func SendDeregisterNFInstance() (problemDetails *models.ProblemDetails, err error) { - logger.ConsumerLog.Infof("Send Deregister NFInstance") - - ctx, pd, err := pcf_context.GetSelf().GetTokenCtx(models.ServiceName_NNRF_NFM, models.NfType_NRF) - if err != nil { - return pd, err - } - - pcfSelf := pcf_context.GetSelf() - // Set client and set url - configuration := Nnrf_NFManagement.NewConfiguration() - configuration.SetBasePath(pcfSelf.NrfUri) - client := Nnrf_NFManagement.NewAPIClient(configuration) - - var res *http.Response - - res, err = client.NFInstanceIDDocumentApi.DeregisterNFInstance(ctx, pcfSelf.NfId) - if err == nil { - return nil, nil - } else if res != nil { - defer func() { - if resCloseErr := res.Body.Close(); resCloseErr != nil { - logger.ConsumerLog.Errorf("DeregisterNFInstance response cannot close: %+v", resCloseErr) - } - }() - if res.Status != err.Error() { - return nil, err - } - problem := err.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails) - problemDetails = &problem - } else { - err = openapi.ReportError("server no response") - } - return problemDetails, err -} diff --git a/internal/sbi/consumer/nrf_service.go b/internal/sbi/consumer/nrf_service.go new file mode 100644 index 0000000..ed6072f --- /dev/null +++ b/internal/sbi/consumer/nrf_service.go @@ -0,0 +1,288 @@ +package consumer + +import ( + "context" + "fmt" + "net/http" + "strings" + "sync" + "time" + + "github.com/antihax/optional" + "github.com/pkg/errors" + + "github.com/free5gc/openapi" + "github.com/free5gc/openapi/Nnrf_NFDiscovery" + "github.com/free5gc/openapi/Nnrf_NFManagement" + "github.com/free5gc/openapi/models" + pcf_context "github.com/free5gc/pcf/internal/context" + "github.com/free5gc/pcf/internal/logger" + "github.com/free5gc/pcf/internal/util" +) + +type nnrfService struct { + consumer *Consumer + + nfMngmntMu sync.RWMutex + nfDiscMu sync.RWMutex + + nfMngmntClients map[string]*Nnrf_NFManagement.APIClient + nfDiscClients map[string]*Nnrf_NFDiscovery.APIClient +} + +func (s *nnrfService) getNFManagementClient(uri string) *Nnrf_NFManagement.APIClient { + if uri == "" { + return nil + } + s.nfMngmntMu.RLock() + client, ok := s.nfMngmntClients[uri] + if ok { + defer s.nfMngmntMu.RUnlock() + return client + } + + configuration := Nnrf_NFManagement.NewConfiguration() + configuration.SetBasePath(uri) + client = Nnrf_NFManagement.NewAPIClient(configuration) + + s.nfMngmntMu.RUnlock() + s.nfMngmntMu.Lock() + defer s.nfMngmntMu.Unlock() + s.nfMngmntClients[uri] = client + return client +} + +func (s *nnrfService) getNFDiscClient(uri string) *Nnrf_NFDiscovery.APIClient { + if uri == "" { + return nil + } + s.nfDiscMu.RLock() + client, ok := s.nfDiscClients[uri] + if ok { + defer s.nfDiscMu.RUnlock() + return client + } + + configuration := Nnrf_NFDiscovery.NewConfiguration() + configuration.SetBasePath(uri) + client = Nnrf_NFDiscovery.NewAPIClient(configuration) + + s.nfDiscMu.RUnlock() + s.nfDiscMu.Lock() + defer s.nfDiscMu.Unlock() + s.nfDiscClients[uri] = client + return client +} + +func (s *nnrfService) SendSearchNFInstances( + nrfUri string, targetNfType, requestNfType models.NfType, param Nnrf_NFDiscovery.SearchNFInstancesParamOpts) ( + *models.SearchResult, error, +) { + // Set client and set url + client := s.getNFDiscClient(nrfUri) + + ctx, _, err := s.consumer.Context().GetTokenCtx(models.ServiceName_NNRF_DISC, models.NfType_NRF) + if err != nil { + return nil, err + } + + result, res, err := client.NFInstancesStoreApi.SearchNFInstances(ctx, targetNfType, requestNfType, ¶m) + if err != nil { + logger.ConsumerLog.Errorf("SearchNFInstances failed: %+v", err) + } + + defer func() { + if resCloseErr := res.Body.Close(); resCloseErr != nil { + logger.ConsumerLog.Errorf("NFInstancesStoreApi response body cannot close: %+v", resCloseErr) + } + }() + + if res != nil && res.StatusCode == http.StatusTemporaryRedirect { + return nil, fmt.Errorf("Temporary Redirect For Non NRF Consumer") + } + + return &result, nil +} + +func (s *nnrfService) SendNFInstancesUDR(nrfUri, id string) string { + targetNfType := models.NfType_UDR + requestNfType := models.NfType_PCF + localVarOptionals := Nnrf_NFDiscovery.SearchNFInstancesParamOpts{ + // DataSet: optional.NewInterface(models.DataSetId_SUBSCRIPTION), + } + + result, err := s.SendSearchNFInstances(nrfUri, targetNfType, requestNfType, localVarOptionals) + if err != nil { + logger.ConsumerLog.Error(err.Error()) + return "" + } + for _, profile := range result.NfInstances { + if uri := util.SearchNFServiceUri(profile, models.ServiceName_NUDR_DR, models.NfServiceStatus_REGISTERED); uri != "" { + return uri + } + } + return "" +} + +func (s *nnrfService) SendNFInstancesBSF(nrfUri string) string { + targetNfType := models.NfType_BSF + requestNfType := models.NfType_PCF + localVarOptionals := Nnrf_NFDiscovery.SearchNFInstancesParamOpts{} + + result, err := s.SendSearchNFInstances(nrfUri, targetNfType, requestNfType, localVarOptionals) + if err != nil { + logger.ConsumerLog.Error(err.Error()) + return "" + } + for _, profile := range result.NfInstances { + if uri := util.SearchNFServiceUri(profile, models.ServiceName_NBSF_MANAGEMENT, + models.NfServiceStatus_REGISTERED); uri != "" { + return uri + } + } + return "" +} + +func (s *nnrfService) SendNFInstancesAMF(nrfUri string, guami models.Guami, serviceName models.ServiceName) string { + targetNfType := models.NfType_AMF + requestNfType := models.NfType_PCF + + localVarOptionals := Nnrf_NFDiscovery.SearchNFInstancesParamOpts{ + Guami: optional.NewInterface(util.MarshToJsonString(guami)), + } + + result, err := s.SendSearchNFInstances(nrfUri, targetNfType, requestNfType, localVarOptionals) + if err != nil { + logger.ConsumerLog.Error(err.Error()) + return "" + } + for _, profile := range result.NfInstances { + return util.SearchNFServiceUri(profile, serviceName, models.NfServiceStatus_REGISTERED) + } + return "" +} + +// management +func (s *nnrfService) BuildNFInstance(context *pcf_context.PCFContext) (profile models.NfProfile, err error) { + profile.NfInstanceId = context.NfId + profile.NfType = models.NfType_PCF + profile.NfStatus = models.NfStatus_REGISTERED + profile.Ipv4Addresses = append(profile.Ipv4Addresses, context.RegisterIPv4) + service := []models.NfService{} + for _, nfService := range context.NfService { + service = append(service, nfService) + } + profile.NfServices = &service + profile.PcfInfo = &models.PcfInfo{ + DnnList: []string{ + "free5gc", + "internet", + }, + // SupiRanges: &[]models.SupiRange{ + // { + // //from TS 29.510 6.1.6.2.9 example2 + // //no need to set supirange in this moment 2019/10/4 + // Start: "123456789040000", + // End: "123456789059999", + // Pattern: "^imsi-12345678904[0-9]{4}$", + // }, + // }, + } + if context.Locality != "" { + profile.Locality = context.Locality + } + return +} + +func (s *nnrfService) SendRegisterNFInstance(ctx context.Context) ( + resouceNrfUri string, retrieveNfInstanceID string, err error, +) { + // Set client and set url + pcfContext := s.consumer.Context() + + client := s.getNFManagementClient(pcfContext.NrfUri) + nfProfile, err := s.BuildNFInstance(pcfContext) + if err != nil { + return "", "", + errors.Wrap(err, "RegisterNFInstance buildNfProfile()") + } + + var nf models.NfProfile + var res *http.Response + for { + nf, res, err = client.NFInstanceIDDocumentApi.RegisterNFInstance(ctx, pcfContext.NfId, nfProfile) + if err != nil || res == nil { + logger.ConsumerLog.Errorf("PCF register to NRF Error[%v]", err) + time.Sleep(2 * time.Second) + continue + } + defer func() { + if resCloseErr := res.Body.Close(); resCloseErr != nil { + logger.ConsumerLog.Errorf("RegisterNFInstance response body cannot close: %+v", resCloseErr) + } + }() + status := res.StatusCode + if status == http.StatusOK { + // NFUpdate + break + } else if status == http.StatusCreated { + // NFRegister + resourceUri := res.Header.Get("Location") + resouceNrfUri = resourceUri[:strings.Index(resourceUri, "/nnrf-nfm/")] + retrieveNfInstanceID = resourceUri[strings.LastIndex(resourceUri, "/")+1:] + + oauth2 := false + if nf.CustomInfo != nil { + v, ok := nf.CustomInfo["oauth2"].(bool) + if ok { + oauth2 = v + logger.MainLog.Infoln("OAuth2 setting receive from NRF:", oauth2) + } + } + pcfContext.OAuth2Required = oauth2 + if oauth2 && pcfContext.NrfCertPem == "" { + logger.CfgLog.Error("OAuth2 enable but no nrfCertPem provided in config.") + } + + break + } else { + fmt.Println(fmt.Errorf("handler returned wrong status code %d", status)) + fmt.Println("NRF return wrong status code", status) + } + } + return resouceNrfUri, retrieveNfInstanceID, err +} + +func (s *nnrfService) SendDeregisterNFInstance() (problemDetails *models.ProblemDetails, err error) { + logger.ConsumerLog.Infof("Send Deregister NFInstance") + pcfContext := s.consumer.pcf.Context() + + ctx, pd, err := pcfContext.GetTokenCtx(models.ServiceName_NNRF_NFM, models.NfType_NRF) + if err != nil { + return pd, err + } + + // Set client and set url + client := s.getNFManagementClient(pcfContext.NrfUri) + + var res *http.Response + + res, err = client.NFInstanceIDDocumentApi.DeregisterNFInstance(ctx, pcfContext.NfId) + if err == nil { + return nil, nil + } else if res != nil { + defer func() { + if resCloseErr := res.Body.Close(); resCloseErr != nil { + logger.ConsumerLog.Errorf("DeregisterNFInstance response cannot close: %+v", resCloseErr) + } + }() + if res.Status != err.Error() { + return nil, err + } + problem := err.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails) + problemDetails = &problem + } else { + err = openapi.ReportError("server no response") + } + return problemDetails, err +} diff --git a/internal/sbi/consumer/influenceDataSubscription.go b/internal/sbi/consumer/udr_service.go similarity index 65% rename from internal/sbi/consumer/influenceDataSubscription.go rename to internal/sbi/consumer/udr_service.go index 6a873f9..e9643bb 100644 --- a/internal/sbi/consumer/influenceDataSubscription.go +++ b/internal/sbi/consumer/udr_service.go @@ -3,15 +3,47 @@ package consumer import ( "strconv" "strings" + "sync" "github.com/free5gc/openapi" + "github.com/free5gc/openapi/Nudr_DataRepository" "github.com/free5gc/openapi/models" pcf_context "github.com/free5gc/pcf/internal/context" "github.com/free5gc/pcf/internal/logger" "github.com/free5gc/pcf/internal/util" ) -func CreateInfluenceDataSubscription(ue *pcf_context.UeContext, request models.SmPolicyContextData) ( +type nudrService struct { + consumer *Consumer + + nfDataSubMu sync.RWMutex + + nfDataSubClients map[string]*Nudr_DataRepository.APIClient +} + +func (s *nudrService) getDataSubscription(uri string) *Nudr_DataRepository.APIClient { + if uri == "" { + return nil + } + s.nfDataSubMu.RLock() + client, ok := s.nfDataSubClients[uri] + if ok { + defer s.nfDataSubMu.RUnlock() + return client + } + + configuration := Nudr_DataRepository.NewConfiguration() + configuration.SetBasePath(uri) + client = Nudr_DataRepository.NewAPIClient(configuration) + + s.nfDataSubMu.RUnlock() + s.nfDataSubMu.Lock() + defer s.nfDataSubMu.Unlock() + s.nfDataSubClients[uri] = client + return client +} + +func (s *nudrService) CreateInfluenceDataSubscription(ue *pcf_context.UeContext, request models.SmPolicyContextData) ( subscriptionID string, problemDetails *models.ProblemDetails, err error, ) { if ue.UdrUri == "" { @@ -19,13 +51,13 @@ func CreateInfluenceDataSubscription(ue *pcf_context.UeContext, request models.S logger.ConsumerLog.Warnf("Can't find corresponding UDR with UE[%s]", ue.Supi) return "", &problemDetail, nil } - ctx, pd, err := pcf_context.GetSelf().GetTokenCtx(models.ServiceName_NUDR_DR, models.NfType_UDR) + ctx, pd, err := s.consumer.Context().GetTokenCtx(models.ServiceName_NUDR_DR, models.NfType_UDR) if err != nil { return "", pd, err } - udrClient := util.GetNudrClient(ue.UdrUri) - trafficInfluSub := buildTrafficInfluSub(request) - _, httpResp, localErr := udrClient.InfluenceDataSubscriptionsCollectionApi. + client := s.getDataSubscription(ue.UdrUri) + trafficInfluSub := s.buildTrafficInfluSub(request) + _, httpResp, localErr := client.InfluenceDataSubscriptionsCollectionApi. ApplicationDataInfluenceDataSubsToNotifyPost(ctx, trafficInfluSub) if localErr == nil { locationHeader := httpResp.Header.Get("Location") @@ -51,13 +83,13 @@ func CreateInfluenceDataSubscription(ue *pcf_context.UeContext, request models.S return "", problemDetails, err } -func buildTrafficInfluSub(request models.SmPolicyContextData) models.TrafficInfluSub { +func (s *nudrService) buildTrafficInfluSub(request models.SmPolicyContextData) models.TrafficInfluSub { trafficInfluSub := models.TrafficInfluSub{ Dnns: []string{request.Dnn}, Snssais: []models.Snssai{*request.SliceInfo}, InternalGroupIds: request.InterGrpIds, Supis: []string{request.Supi}, - NotificationUri: pcf_context.GetSelf().GetIPv4Uri() + + NotificationUri: s.consumer.Context().GetIPv4Uri() + pcf_context.InfluenceDataUpdateNotifyUri + "/" + request.Supi + "/" + strconv.Itoa(int(request.PduSessionId)), // TODO: support expiry time and resend subscription when expired @@ -65,7 +97,7 @@ func buildTrafficInfluSub(request models.SmPolicyContextData) models.TrafficInfl return trafficInfluSub } -func RemoveInfluenceDataSubscription(ue *pcf_context.UeContext, subscriptionID string) ( +func (s *nudrService) RemoveInfluenceDataSubscription(ue *pcf_context.UeContext, subscriptionID string) ( problemDetails *models.ProblemDetails, err error, ) { if ue.UdrUri == "" { @@ -73,12 +105,12 @@ func RemoveInfluenceDataSubscription(ue *pcf_context.UeContext, subscriptionID s logger.ConsumerLog.Warnf("Can't find corresponding UDR with UE[%s]", ue.Supi) return &problemDetail, nil } - ctx, pd, err := pcf_context.GetSelf().GetTokenCtx(models.ServiceName_NUDR_DR, models.NfType_UDR) + ctx, pd, err := s.consumer.Context().GetTokenCtx(models.ServiceName_NUDR_DR, models.NfType_UDR) if err != nil { return pd, err } - udrClient := util.GetNudrClient(ue.UdrUri) - httpResp, localErr := udrClient.IndividualInfluenceDataSubscriptionDocumentApi. + client := s.getDataSubscription(ue.UdrUri) + httpResp, localErr := client.IndividualInfluenceDataSubscriptionDocumentApi. ApplicationDataInfluenceDataSubsToNotifySubscriptionIdDelete(ctx, subscriptionID) if localErr == nil { logger.ConsumerLog.Debugf("Nudr_DataRepository Remove Influence Data Subscription Status %s", diff --git a/internal/sbi/httpcallback/amf_status_change_notify.go b/internal/sbi/httpcallback/amf_status_change_notify.go deleted file mode 100644 index ce4ba71..0000000 --- a/internal/sbi/httpcallback/amf_status_change_notify.go +++ /dev/null @@ -1,64 +0,0 @@ -package httpcallback - -import ( - "net/http" - - "github.com/gin-gonic/gin" - - "github.com/free5gc/openapi" - "github.com/free5gc/openapi/models" - "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/internal/sbi/producer" - "github.com/free5gc/util/httpwrapper" -) - -func HTTPAmfStatusChangeNotify(c *gin.Context) { - var amfStatusChangeNotification models.AmfStatusChangeNotification - - requestBody, err := c.GetRawData() - if err != nil { - problemDetail := models.ProblemDetails{ - Title: "System failure", - Status: http.StatusInternalServerError, - Detail: err.Error(), - Cause: "SYSTEM_FAILURE", - } - logger.CallbackLog.Errorf("Get Request Body error: %+v", err) - c.JSON(http.StatusInternalServerError, problemDetail) - return - } - - err = openapi.Deserialize(&amfStatusChangeNotification, 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, amfStatusChangeNotification) - - rsp := producer.HandleAmfStatusChangeNotify(req) - - if rsp.Status == http.StatusNoContent { - c.Status(rsp.Status) - } else { - 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 { - c.Data(rsp.Status, "application/json", responseBody) - } - } -} diff --git a/internal/sbi/httpcallback/router.go b/internal/sbi/httpcallback/router.go deleted file mode 100644 index 435e770..0000000 --- a/internal/sbi/httpcallback/router.go +++ /dev/null @@ -1,81 +0,0 @@ -package httpcallback - -import ( - "net/http" - "strings" - - "github.com/gin-gonic/gin" - - "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/pkg/factory" - logger_util "github.com/free5gc/util/logger" -) - -// Route is the information for every URI. -type Route struct { - // 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 - // HandlerFunc is the handler function of this route. - HandlerFunc gin.HandlerFunc -} - -// Routes is the list of the generated Route. -type Routes []Route - -// NewRouter returns a new router. -func NewRouter() *gin.Engine { - router := logger_util.NewGinWithLogrus(logger.GinLog) - AddService(router) - return router -} - -func AddService(engine *gin.Engine) *gin.RouterGroup { - group := engine.Group(factory.PcfCallbackResUriPrefix) - - for _, route := range routes { - switch route.Method { - case "POST": - group.POST(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{ - { - "Index", - "GET", - "/", - Index, - }, - - { - "HTTPUdrPolicyDataChangeNotify", - strings.ToUpper("Post"), - "/nudr-notify/policy-data/:supi", - HTTPUdrPolicyDataChangeNotify, - }, - - { - "HTTPUdrInfluenceDataUpdateNotify", - strings.ToUpper("Post"), - "/nudr-notify/influence-data/:supi/:pduSessionId", - HTTPUdrInfluenceDataUpdateNotify, - }, - - { - "HTTPAmfStatusChangeNotify", - strings.ToUpper("Post"), - "/amfstatus", - HTTPAmfStatusChangeNotify, - }, -} diff --git a/internal/sbi/httpcallback/sm_policy_notify.go b/internal/sbi/httpcallback/sm_policy_notify.go deleted file mode 100644 index be985ba..0000000 --- a/internal/sbi/httpcallback/sm_policy_notify.go +++ /dev/null @@ -1,112 +0,0 @@ -package httpcallback - -import ( - "net/http" - - "github.com/gin-gonic/gin" - - "github.com/free5gc/openapi" - "github.com/free5gc/openapi/models" - "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/internal/sbi/producer" - "github.com/free5gc/util/httpwrapper" -) - -// Nudr-Notify-smpolicy -func HTTPUdrPolicyDataChangeNotify(c *gin.Context) { - var policyDataChangeNotification models.PolicyDataChangeNotification - - requestBody, err := c.GetRawData() - if err != nil { - problemDetail := models.ProblemDetails{ - Title: "System failure", - Status: http.StatusInternalServerError, - Detail: err.Error(), - Cause: "SYSTEM_FAILURE", - } - logger.CallbackLog.Errorf("Get Request Body error: %+v", err) - c.JSON(http.StatusInternalServerError, problemDetail) - return - } - - err = openapi.Deserialize(&policyDataChangeNotification, 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, policyDataChangeNotification) - req.Params["supi"] = c.Params.ByName("supi") - - rsp := producer.HandlePolicyDataChangeNotify(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 { - c.Data(rsp.Status, "application/json", responseBody) - } -} - -// Influence Data Update Notification -func HTTPUdrInfluenceDataUpdateNotify(c *gin.Context) { - var trafficInfluDataNotif []models.TrafficInfluDataNotif - - requestBody, err := c.GetRawData() - if err != nil { - problemDetail := models.ProblemDetails{ - Title: "System failure", - Status: http.StatusInternalServerError, - Detail: err.Error(), - Cause: "SYSTEM_FAILURE", - } - logger.CallbackLog.Errorf("Get Request Body error: %+v", err) - c.JSON(http.StatusInternalServerError, problemDetail) - return - } - - err = openapi.Deserialize(&trafficInfluDataNotif, 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, trafficInfluDataNotif) - req.Params["supi"] = c.Params.ByName("supi") - req.Params["pduSessionId"] = c.Params.ByName("pduSessionId") - - rsp := producer.HandleInfluenceDataUpdateNotify(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 { - c.Data(rsp.Status, "application/json", responseBody) - } -} diff --git a/internal/sbi/oam/api_get_am_policy.go b/internal/sbi/oam/api_get_am_policy.go deleted file mode 100644 index 88f9e7b..0000000 --- a/internal/sbi/oam/api_get_am_policy.go +++ /dev/null @@ -1,44 +0,0 @@ -package oam - -import ( - "net/http" - - "github.com/gin-gonic/gin" - - "github.com/free5gc/openapi" - "github.com/free5gc/openapi/models" - "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/internal/sbi/producer" - "github.com/free5gc/util/httpwrapper" -) - -func setCorsHeader(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, Content-Length, Accept-Encoding, "+ - "X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With") - c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, PATCH, DELETE") -} - -func HTTPOAMGetAmPolicy(c *gin.Context) { - setCorsHeader(c) - - req := httpwrapper.NewRequest(c.Request, nil) - req.Params["supi"] = c.Params.ByName("supi") - - rsp := producer.HandleOAMGetAmPolicyRequest(req) - - responseBody, err := openapi.Serialize(rsp.Body, "application/json") - if err != nil { - logger.OamLog.Errorln(err) - problemDetails := models.ProblemDetails{ - Status: http.StatusInternalServerError, - Cause: "SYSTEM_FAILURE", - Detail: err.Error(), - } - c.JSON(http.StatusInternalServerError, problemDetails) - } else { - c.Data(rsp.Status, "application/json", responseBody) - } -} diff --git a/internal/sbi/oam/routers.go b/internal/sbi/oam/routers.go deleted file mode 100644 index 22091f5..0000000 --- a/internal/sbi/oam/routers.go +++ /dev/null @@ -1,74 +0,0 @@ -package oam - -import ( - "net/http" - - "github.com/gin-gonic/gin" - - "github.com/free5gc/openapi/models" - pcf_context "github.com/free5gc/pcf/internal/context" - "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/internal/util" - "github.com/free5gc/pcf/pkg/factory" - logger_util "github.com/free5gc/util/logger" -) - -// Route is the information for every URI. -type Route struct { - // 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 - // HandlerFunc is the handler function of this route. - HandlerFunc gin.HandlerFunc -} - -// Routes is the list of the generated Route. -type Routes []Route - -// NewRouter returns a new router. -func NewRouter() *gin.Engine { - router := logger_util.NewGinWithLogrus(logger.GinLog) - AddService(router) - return router -} - -func AddService(engine *gin.Engine) *gin.RouterGroup { - group := engine.Group(factory.PcfOamResUriPrefix) - - routerAuthorizationCheck := util.NewRouterAuthorizationCheck(models.ServiceName_NPCF_OAM) - group.Use(func(c *gin.Context) { - routerAuthorizationCheck.Check(c, pcf_context.GetSelf()) - }) - - for _, route := range routes { - switch route.Method { - case "GET": - group.GET(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{ - { - "Index", - http.MethodGet, - "/", - Index, - }, - - { - "Get UE AM Policy Data", - http.MethodGet, - "/am-policy/:supi", - HTTPOAMGetAmPolicy, - }, -} diff --git a/internal/sbi/policyauthorization/api_application_sessions_collection.go b/internal/sbi/policyauthorization/api_application_sessions_collection.go deleted file mode 100644 index 3da7033..0000000 --- a/internal/sbi/policyauthorization/api_application_sessions_collection.go +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Npcf_PolicyAuthorization Service API - * - * This is the Policy Authorization Service - * - * API version: 1.0.0 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package policyauthorization - -import ( - "net/http" - - "github.com/gin-gonic/gin" - - "github.com/free5gc/openapi" - "github.com/free5gc/openapi/models" - "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/internal/sbi/producer" - "github.com/free5gc/pcf/internal/util" - "github.com/free5gc/util/httpwrapper" -) - -// HTTPPostAppSessions - Creates a new Individual Application Session Context resource -func HTTPPostAppSessions(c *gin.Context) { - var appSessionContext models.AppSessionContext - - requestBody, err := c.GetRawData() - if err != nil { - problemDetail := models.ProblemDetails{ - Title: "System failure", - Status: http.StatusInternalServerError, - Detail: err.Error(), - Cause: "SYSTEM_FAILURE", - } - logger.PolicyAuthLog.Errorf("Get Request Body error: %+v", err) - c.JSON(http.StatusInternalServerError, problemDetail) - return - } - - err = openapi.Deserialize(&appSessionContext, requestBody, "application/json") - if err != nil { - problemDetail := "[Request Body] " + err.Error() - rsp := models.ProblemDetails{ - Title: "Malformed request syntax", - Status: http.StatusBadRequest, - Detail: problemDetail, - } - logger.PolicyAuthLog.Errorln(problemDetail) - c.JSON(http.StatusBadRequest, rsp) - return - } - - ascReqData := appSessionContext.AscReqData - if ascReqData == nil || ascReqData.SuppFeat == "" || ascReqData.NotifUri == "" { - // Check Mandatory IEs - rsp := util.GetProblemDetail("Errorneous/Missing Mandotory IE", util.ERROR_INITIAL_PARAMETERS) - logger.PolicyAuthLog.Errorln(rsp.Detail) - c.JSON(int(rsp.Status), rsp) - return - } - - req := httpwrapper.NewRequest(c.Request, appSessionContext) - rsp := producer.HandlePostAppSessionsContext(req) - - for key, val := range rsp.Header { - c.Header(key, val[0]) - } - responseBody, err := openapi.Serialize(rsp.Body, "application/json") - if err != nil { - logger.PolicyAuthLog.Errorln(err) - problemDetails := models.ProblemDetails{ - Status: http.StatusInternalServerError, - Cause: "SYSTEM_FAILURE", - Detail: err.Error(), - } - c.JSON(http.StatusInternalServerError, problemDetails) - } else { - c.Data(rsp.Status, "application/json", responseBody) - } -} diff --git a/internal/sbi/policyauthorization/api_events_subscription_document.go b/internal/sbi/policyauthorization/api_events_subscription_document.go deleted file mode 100644 index ef76473..0000000 --- a/internal/sbi/policyauthorization/api_events_subscription_document.go +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Npcf_PolicyAuthorization Service API - * - * This is the Policy Authorization Service - * - * API version: 1.0.0 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package policyauthorization - -import ( - "net/http" - - "github.com/gin-gonic/gin" - - "github.com/free5gc/openapi" - "github.com/free5gc/openapi/models" - "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/internal/sbi/producer" - "github.com/free5gc/pcf/internal/util" - "github.com/free5gc/util/httpwrapper" -) - -// HTTPDeleteEventsSubsc - deletes the Events Subscription subresource -func HTTPDeleteEventsSubsc(c *gin.Context) { - req := httpwrapper.NewRequest(c.Request, nil) - req.Params["appSessionId"], _ = c.Params.Get("appSessionId") - - rsp := producer.HandleDeleteEventsSubscContext(req) - - responseBody, err := openapi.Serialize(rsp.Body, "application/json") - if err != nil { - logger.PolicyAuthLog.Errorln(err) - problemDetails := models.ProblemDetails{ - Status: http.StatusInternalServerError, - Cause: "SYSTEM_FAILURE", - Detail: err.Error(), - } - c.JSON(http.StatusInternalServerError, problemDetails) - } else { - c.Data(rsp.Status, "application/json", responseBody) - } -} - -// HTTPUpdateEventsSubsc - creates or modifies an Events Subscription subresource -func HTTPUpdateEventsSubsc(c *gin.Context) { - var eventsSubscReqData models.EventsSubscReqData - - requestBody, err := c.GetRawData() - if err != nil { - problemDetail := models.ProblemDetails{ - Title: "System failure", - Status: http.StatusInternalServerError, - Detail: err.Error(), - Cause: "SYSTEM_FAILURE", - } - logger.PolicyAuthLog.Errorf("Get Request Body error: %+v", err) - c.JSON(http.StatusInternalServerError, problemDetail) - return - } - - err = openapi.Deserialize(&eventsSubscReqData, requestBody, "application/json") - if err != nil { - problemDetail := "[Request Body] " + err.Error() - rsp := models.ProblemDetails{ - Title: "Malformed request syntax", - Status: http.StatusBadRequest, - Detail: problemDetail, - } - logger.PolicyAuthLog.Errorln(problemDetail) - c.JSON(http.StatusBadRequest, rsp) - return - } - - if eventsSubscReqData.Events == nil || eventsSubscReqData.NotifUri == "" { - problemDetail := util.GetProblemDetail("Errorneous/Missing Mandotory IE", util.ERROR_REQUEST_PARAMETERS) - logger.PolicyAuthLog.Errorln(problemDetail.Detail) - c.JSON(int(problemDetail.Status), problemDetail) - return - } - - req := httpwrapper.NewRequest(c.Request, eventsSubscReqData) - req.Params["appSessionId"], _ = c.Params.Get("appSessionId") - - rsp := producer.HandleUpdateEventsSubscContext(req) - - responseBody, err := openapi.Serialize(rsp.Body, "application/json") - if err != nil { - logger.PolicyAuthLog.Errorln(err) - problemDetails := models.ProblemDetails{ - Status: http.StatusInternalServerError, - Cause: "SYSTEM_FAILURE", - Detail: err.Error(), - } - c.JSON(http.StatusInternalServerError, problemDetails) - } else { - c.Data(rsp.Status, "application/json", responseBody) - } -} diff --git a/internal/sbi/policyauthorization/api_individual_application_session_context_document.go b/internal/sbi/policyauthorization/api_individual_application_session_context_document.go deleted file mode 100644 index 42398dd..0000000 --- a/internal/sbi/policyauthorization/api_individual_application_session_context_document.go +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Npcf_PolicyAuthorization Service API - * - * This is the Policy Authorization Service - * - * API version: 1.0.0 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package policyauthorization - -import ( - "net/http" - - "github.com/gin-gonic/gin" - - "github.com/free5gc/openapi" - "github.com/free5gc/openapi/models" - "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/internal/sbi/producer" - "github.com/free5gc/util/httpwrapper" -) - -// HTTPDeleteAppSession - Deletes an existing Individual Application Session Context -func HTTPDeleteAppSession(c *gin.Context) { - var eventsSubscReqData *models.EventsSubscReqData - - requestBody, err := c.GetRawData() - if err != nil { - problemDetail := models.ProblemDetails{ - Title: "System failure", - Status: http.StatusInternalServerError, - Detail: err.Error(), - Cause: "SYSTEM_FAILURE", - } - logger.PolicyAuthLog.Errorf("Get Request Body error: %+v", err) - c.JSON(http.StatusInternalServerError, problemDetail) - return - } - - // EventsSubscReqData is Optional - if len(requestBody) > 0 { - err = openapi.Deserialize(&eventsSubscReqData, requestBody, "application/json") - if err != nil { - problemDetail := "[Request Body] " + err.Error() - rsp := models.ProblemDetails{ - Title: "Malformed request syntax", - Status: http.StatusBadRequest, - Detail: problemDetail, - } - logger.PolicyAuthLog.Errorln(problemDetail) - c.JSON(http.StatusBadRequest, rsp) - return - } - } - - req := httpwrapper.NewRequest(c.Request, eventsSubscReqData) - req.Params["appSessionId"], _ = c.Params.Get("appSessionId") - - rsp := producer.HandleDeleteAppSessionContext(req) - - responseBody, err := openapi.Serialize(rsp.Body, "application/json") - if err != nil { - logger.PolicyAuthLog.Errorln(err) - problemDetails := models.ProblemDetails{ - Status: http.StatusInternalServerError, - Cause: "SYSTEM_FAILURE", - Detail: err.Error(), - } - c.JSON(http.StatusInternalServerError, problemDetails) - } else { - c.Data(rsp.Status, "application/json", responseBody) - } -} - -// HTTPGetAppSession - Reads an existing Individual Application Session Context -func HTTPGetAppSession(c *gin.Context) { - req := httpwrapper.NewRequest(c.Request, nil) - req.Params["appSessionId"], _ = c.Params.Get("appSessionId") - - rsp := producer.HandleGetAppSessionContext(req) - - responseBody, err := openapi.Serialize(rsp.Body, "application/json") - if err != nil { - logger.PolicyAuthLog.Errorln(err) - problemDetails := models.ProblemDetails{ - Status: http.StatusInternalServerError, - Cause: "SYSTEM_FAILURE", - Detail: err.Error(), - } - c.JSON(http.StatusInternalServerError, problemDetails) - } else { - c.Data(rsp.Status, "application/json", responseBody) - } -} - -// HTTPModAppSession - Modifies an existing Individual Application Session Context -func HTTPModAppSession(c *gin.Context) { - var appSessionContextUpdateData models.AppSessionContextUpdateData - - requestBody, err := c.GetRawData() - if err != nil { - problemDetail := models.ProblemDetails{ - Title: "System failure", - Status: http.StatusInternalServerError, - Detail: err.Error(), - Cause: "SYSTEM_FAILURE", - } - logger.PolicyAuthLog.Errorf("Get Request Body error: %+v", err) - c.JSON(http.StatusInternalServerError, problemDetail) - return - } - - err = openapi.Deserialize(&appSessionContextUpdateData, requestBody, "application/json") - if err != nil { - problemDetail := "[Request Body] " + err.Error() - rsp := models.ProblemDetails{ - Title: "Malformed request syntax", - Status: http.StatusBadRequest, - Detail: problemDetail, - } - logger.PolicyAuthLog.Errorln(problemDetail) - c.JSON(http.StatusBadRequest, rsp) - return - } - - req := httpwrapper.NewRequest(c.Request, appSessionContextUpdateData) - req.Params["appSessionId"], _ = c.Params.Get("appSessionId") - - rsp := producer.HandleModAppSessionContext(req) - - responseBody, err := openapi.Serialize(rsp.Body, "application/json") - if err != nil { - logger.PolicyAuthLog.Errorln(err) - problemDetails := models.ProblemDetails{ - Status: http.StatusInternalServerError, - Cause: "SYSTEM_FAILURE", - Detail: err.Error(), - } - c.JSON(http.StatusInternalServerError, problemDetails) - } else { - c.Data(rsp.Status, "application/json", responseBody) - } -} diff --git a/internal/sbi/policyauthorization/routers.go b/internal/sbi/policyauthorization/routers.go deleted file mode 100644 index 6e35aac..0000000 --- a/internal/sbi/policyauthorization/routers.go +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Npcf_PolicyAuthorization Service API - * - * This is the Policy Authorization Service - * - * API version: 1.0.1 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package policyauthorization - -import ( - "net/http" - "strings" - - "github.com/gin-gonic/gin" - - "github.com/free5gc/openapi/models" - pcf_context "github.com/free5gc/pcf/internal/context" - "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/internal/util" - "github.com/free5gc/pcf/pkg/factory" - logger_util "github.com/free5gc/util/logger" -) - -// Route is the information for every URI. -type Route struct { - // 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 - // HandlerFunc is the handler function of this route. - HandlerFunc gin.HandlerFunc -} - -// Routes is the list of the generated Route. -type Routes []Route - -// NewRouter returns a new router. -func NewRouter() *gin.Engine { - router := logger_util.NewGinWithLogrus(logger.GinLog) - AddService(router) - return router -} - -func AddService(engine *gin.Engine) *gin.RouterGroup { - group := engine.Group(factory.PcfPolicyAuthResUriPrefix) - - routerAuthorizationCheck := util.NewRouterAuthorizationCheck(models.ServiceName_NPCF_POLICYAUTHORIZATION) - group.Use(func(c *gin.Context) { - routerAuthorizationCheck.Check(c, pcf_context.GetSelf()) - }) - - 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 "DELETE": - group.DELETE(route.Pattern, route.HandlerFunc) - case "PATCH": - group.PATCH(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{ - { - "Index", - "GET", - "/", - Index, - }, - - { - "HTTPPostAppSessions", - strings.ToUpper("Post"), - "/app-sessions", - HTTPPostAppSessions, - }, - - { - "HTTPDeleteEventsSubsc", - strings.ToUpper("Delete"), - "/app-sessions/:appSessionId/events-subscription", - HTTPDeleteEventsSubsc, - }, - - { - "HTTPUpdateEventsSubsc", - strings.ToUpper("Put"), - "/app-sessions/:appSessionId/events-subscription", - HTTPUpdateEventsSubsc, - }, - - { - "HTTPDeleteAppSession", - strings.ToUpper("Post"), - "/app-sessions/:appSessionId/delete", - HTTPDeleteAppSession, - }, - - { - "HTTPGetAppSession", - strings.ToUpper("Get"), - "/app-sessions/:appSessionId", - HTTPGetAppSession, - }, - - { - "HTTPModAppSession", - strings.ToUpper("Patch"), - "/app-sessions/:appSessionId", - HTTPModAppSession, - }, -} diff --git a/internal/sbi/producer/ampolicy.go b/internal/sbi/processor/ampolicy.go similarity index 77% rename from internal/sbi/producer/ampolicy.go rename to internal/sbi/processor/ampolicy.go index 5e816fb..f162a77 100644 --- a/internal/sbi/producer/ampolicy.go +++ b/internal/sbi/processor/ampolicy.go @@ -1,68 +1,49 @@ -package producer +package processor import ( "fmt" "net/http" "reflect" + "github.com/gin-gonic/gin" "github.com/mohae/deepcopy" "github.com/free5gc/openapi" "github.com/free5gc/openapi/models" pcf_context "github.com/free5gc/pcf/internal/context" "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/internal/sbi/consumer" "github.com/free5gc/pcf/internal/util" - "github.com/free5gc/util/httpwrapper" ) -func HandleDeletePoliciesPolAssoId(request *httpwrapper.Request) *httpwrapper.Response { +func (p *Processor) HandleDeletePoliciesPolAssoId( + c *gin.Context, + polAssoId string, +) { logger.AmPolicyLog.Infof("Handle AM Policy Association Delete") - polAssoId := request.Params["polAssoId"] - - problemDetails := DeletePoliciesPolAssoIdProcedure(polAssoId) - if problemDetails == nil { - return httpwrapper.NewResponse(http.StatusNoContent, nil, nil) - } else { - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) - } -} - -func DeletePoliciesPolAssoIdProcedure(polAssoId string) *models.ProblemDetails { - ue := pcf_context.GetSelf().PCFUeFindByPolicyId(polAssoId) + ue := p.Context().PCFUeFindByPolicyId(polAssoId) if ue == nil || ue.AMPolicyData[polAssoId] == nil { problemDetails := util.GetProblemDetail("polAssoId not found in PCF", util.CONTEXT_NOT_FOUND) - return &problemDetails + c.JSON(int(problemDetails.Status), problemDetails) } + delete(ue.AMPolicyData, polAssoId) - return nil + c.JSON(http.StatusNoContent, nil) } // PoliciesPolAssoIdGet - -func HandleGetPoliciesPolAssoId(request *httpwrapper.Request) *httpwrapper.Response { +func (p *Processor) HandleGetPoliciesPolAssoId( + c *gin.Context, + polAssoId string, +) { logger.AmPolicyLog.Infof("Handle AM Policy Association Get") - polAssoId := request.Params["polAssoId"] - - response, problemDetails := GetPoliciesPolAssoIdProcedure(polAssoId) - if response != nil { - return httpwrapper.NewResponse(http.StatusOK, nil, response) - } else if problemDetails != nil { - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) - } - problemDetails = &models.ProblemDetails{ - Status: http.StatusForbidden, - Cause: "UNSPECIFIED", - } - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) -} - -func GetPoliciesPolAssoIdProcedure(polAssoId string) (*models.PolicyAssociation, *models.ProblemDetails) { - ue := pcf_context.GetSelf().PCFUeFindByPolicyId(polAssoId) + // response, problemDetails := GetPoliciesPolAssoIdProcedure(polAssoId) + ue := p.Context().PCFUeFindByPolicyId(polAssoId) if ue == nil || ue.AMPolicyData[polAssoId] == nil { problemDetails := util.GetProblemDetail("polAssoId not found in PCF", util.CONTEXT_NOT_FOUND) - return nil, &problemDetails + c.JSON(int(problemDetails.Status), problemDetails) + return } amPolicyData := ue.AMPolicyData[polAssoId] rsp := models.PolicyAssociation{ @@ -83,32 +64,34 @@ func GetPoliciesPolAssoIdProcedure(polAssoId string) (*models.PolicyAssociation, } } } - return &rsp, nil + c.JSON(http.StatusOK, rsp) } -func HandleUpdatePostPoliciesPolAssoId(request *httpwrapper.Request) *httpwrapper.Response { +func (p *Processor) HandleUpdatePostPoliciesPolAssoId( + c *gin.Context, + polAssoId string, + policyAssociationUpdateRequest models.PolicyAssociationUpdateRequest, +) { logger.AmPolicyLog.Infof("Handle AM Policy Association Update") - polAssoId := request.Params["polAssoId"] - policyAssociationUpdateRequest := request.Body.(models.PolicyAssociationUpdateRequest) - - response, problemDetails := UpdatePostPoliciesPolAssoIdProcedure(polAssoId, policyAssociationUpdateRequest) + response, problemDetails := p.UpdatePostPoliciesPolAssoIdProcedure(polAssoId, policyAssociationUpdateRequest) if response != nil { - return httpwrapper.NewResponse(http.StatusOK, nil, response) + c.JSON(http.StatusOK, response) } else if problemDetails != nil { - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) + c.JSON(int(problemDetails.Status), problemDetails) } + problemDetails = &models.ProblemDetails{ Status: http.StatusForbidden, Cause: "UNSPECIFIED", } - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) + c.JSON(int(problemDetails.Status), problemDetails) } -func UpdatePostPoliciesPolAssoIdProcedure(polAssoId string, +func (p *Processor) UpdatePostPoliciesPolAssoIdProcedure(polAssoId string, policyAssociationUpdateRequest models.PolicyAssociationUpdateRequest, ) (*models.PolicyUpdate, *models.ProblemDetails) { - ue := pcf_context.GetSelf().PCFUeFindByPolicyId(polAssoId) + ue := p.Context().PCFUeFindByPolicyId(polAssoId) if ue == nil || ue.AMPolicyData[polAssoId] == nil { problemDetails := util.GetProblemDetail("polAssoId not found in PCF", util.CONTEXT_NOT_FOUND) return nil, &problemDetails @@ -179,33 +162,34 @@ func UpdatePostPoliciesPolAssoIdProcedure(polAssoId string, } // Create AM Policy -func HandlePostPolicies(request *httpwrapper.Request) *httpwrapper.Response { +func (p *Processor) HandlePostPolicies( + c *gin.Context, + polAssoId string, + policyAssociationRequest models.PolicyAssociationRequest, +) { logger.AmPolicyLog.Infof("Handle AM Policy Create Request") - polAssoId := request.Params["polAssoId"] - policyAssociationRequest := request.Body.(models.PolicyAssociationRequest) - - response, locationHeader, problemDetails := PostPoliciesProcedure(polAssoId, policyAssociationRequest) - headers := http.Header{ - "Location": {locationHeader}, - } + response, locationHeader, problemDetails := p.PostPoliciesProcedure(polAssoId, policyAssociationRequest) if response != nil { - return httpwrapper.NewResponse(http.StatusCreated, headers, response) + // TODO: set gin header + c.Header("Location", locationHeader) + c.JSON(http.StatusCreated, response) } else if problemDetails != nil { - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) + c.JSON(int(problemDetails.Status), problemDetails) } + problemDetails = &models.ProblemDetails{ Status: http.StatusForbidden, Cause: "UNSPECIFIED", } - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) + c.JSON(int(problemDetails.Status), problemDetails) } -func PostPoliciesProcedure(polAssoId string, +func (p *Processor) PostPoliciesProcedure(polAssoId string, policyAssociationRequest models.PolicyAssociationRequest, ) (*models.PolicyAssociation, string, *models.ProblemDetails) { var response models.PolicyAssociation - pcfSelf := pcf_context.GetSelf() + pcfSelf := p.Context() var ue *pcf_context.UeContext if val, ok := pcfSelf.UePool.Load(policyAssociationRequest.Supi); ok { ue = val.(*pcf_context.UeContext) @@ -220,7 +204,7 @@ func PostPoliciesProcedure(polAssoId string, ue = newUe } } - udrUri := getUdrUri(ue) + udrUri := p.getUdrUri(ue) if udrUri == "" { // Can't find any UDR support this Ue pcfSelf.UePool.Delete(ue.Supi) @@ -234,7 +218,7 @@ func PostPoliciesProcedure(polAssoId string, assolId := fmt.Sprintf("%s-%d", ue.Supi, ue.PolAssociationIDGenerator) amPolicy := ue.AMPolicyData[assolId] - ctx, pd, err := pcf_context.GetSelf().GetTokenCtx(models.ServiceName_NUDR_DR, models.NfType_UDR) + ctx, pd, err := p.Context().GetTokenCtx(models.ServiceName_NUDR_DR, models.NfType_UDR) if err != nil { return nil, "", pd } @@ -301,9 +285,11 @@ func PostPoliciesProcedure(polAssoId string, if needSubscribe { logger.AmPolicyLog.Debugf("Subscribe AMF status change[GUAMI: %+v]", *policyAssociationRequest.Guami) - amfUri := consumer.SendNFInstancesAMF(pcfSelf.NrfUri, *policyAssociationRequest.Guami, models.ServiceName_NAMF_COMM) + amfUri := p.Consumer().SendNFInstancesAMF(pcfSelf.NrfUri, + *policyAssociationRequest.Guami, models.ServiceName_NAMF_COMM) if amfUri != "" { - problemDetails, err := consumer.AmfStatusChangeSubscribe(amfUri, []models.Guami{*policyAssociationRequest.Guami}) + problemDetails, err := p.Consumer().AmfStatusChangeSubscribe(amfUri, + []models.Guami{*policyAssociationRequest.Guami}) if err != nil { logger.AmPolicyLog.Errorf("Subscribe AMF status change error[%+v]", err) } else if problemDetails != nil { @@ -320,7 +306,9 @@ func PostPoliciesProcedure(polAssoId string, } // Send AM Policy Update to AMF if policy has changed -func SendAMPolicyUpdateNotification(ue *pcf_context.UeContext, PolId string, request models.PolicyUpdate) { +func (p *Processor) SendAMPolicyUpdateNotification(ue *pcf_context.UeContext, + PolId string, request models.PolicyUpdate, +) { if ue == nil { logger.AmPolicyLog.Warnln("Policy Update Notification Error[Ue is nil]") return @@ -331,7 +319,7 @@ func SendAMPolicyUpdateNotification(ue *pcf_context.UeContext, PolId string, req return } - ctx, _, err := pcf_context.GetSelf().GetTokenCtx(models.ServiceName_NPCF_AM_POLICY_CONTROL, models.NfType_PCF) + ctx, _, err := p.Context().GetTokenCtx(models.ServiceName_NPCF_AM_POLICY_CONTROL, models.NfType_PCF) if err != nil { return } @@ -373,7 +361,7 @@ func SendAMPolicyUpdateNotification(ue *pcf_context.UeContext, PolId string, req } // Send AM Policy Update to AMF if policy has been terminated -func SendAMPolicyTerminationRequestNotification(ue *pcf_context.UeContext, +func (p *Processor) SendAMPolicyTerminationRequestNotification(ue *pcf_context.UeContext, PolId string, request models.TerminationNotification, ) { if ue == nil { @@ -389,7 +377,7 @@ func SendAMPolicyTerminationRequestNotification(ue *pcf_context.UeContext, client := util.GetNpcfAMPolicyCallbackClient() uri := amPolicyData.NotificationUri - ctx, _, err := pcf_context.GetSelf().GetTokenCtx(models.ServiceName_NPCF_AM_POLICY_CONTROL, models.NfType_PCF) + ctx, _, err := p.Context().GetTokenCtx(models.ServiceName_NPCF_AM_POLICY_CONTROL, models.NfType_PCF) if err != nil { return } @@ -430,9 +418,9 @@ func SendAMPolicyTerminationRequestNotification(ue *pcf_context.UeContext, } // returns UDR Uri of Ue, if ue.UdrUri dose not exist, query NRF to get supported Udr Uri -func getUdrUri(ue *pcf_context.UeContext) string { +func (p *Processor) getUdrUri(ue *pcf_context.UeContext) string { if ue.UdrUri != "" { return ue.UdrUri } - return consumer.SendNFInstancesUDR(pcf_context.GetSelf().NrfUri, ue.Supi) + return p.Consumer().SendNFInstancesUDR(p.Context().NrfUri, ue.Supi) } diff --git a/internal/sbi/producer/bdtpolicy.go b/internal/sbi/processor/bdtpolicy.go similarity index 58% rename from internal/sbi/producer/bdtpolicy.go rename to internal/sbi/processor/bdtpolicy.go index 7bb935e..b762c20 100644 --- a/internal/sbi/producer/bdtpolicy.go +++ b/internal/sbi/processor/bdtpolicy.go @@ -1,10 +1,11 @@ -package producer +package processor import ( "fmt" "net/http" "github.com/antihax/optional" + "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/mohae/deepcopy" @@ -13,99 +14,62 @@ import ( "github.com/free5gc/openapi/models" pcf_context "github.com/free5gc/pcf/internal/context" "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/internal/sbi/consumer" "github.com/free5gc/pcf/internal/util" - "github.com/free5gc/util/httpwrapper" ) -func HandleGetBDTPolicyContextRequest(request *httpwrapper.Request) *httpwrapper.Response { +func (p *Processor) HandleGetBDTPolicyContextRequest( + c *gin.Context, + bdtPolicyID string, +) { // step 1: log logger.BdtPolicyLog.Infof("Handle GetBDTPolicyContext") - // step 2: retrieve request - bdtPolicyID := request.Params["bdtPolicyId"] - - // step 3: handle the message - response, problemDetails := getBDTPolicyContextProcedure(bdtPolicyID) - - // step 4: process the return value from step 3 - if response != nil { - // status code is based on SPEC, and option headers - return httpwrapper.NewResponse(http.StatusOK, nil, response) - } else if problemDetails != nil { - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) - } - problemDetails = &models.ProblemDetails{ - Status: http.StatusForbidden, - Cause: "UNSPECIFIED", - } - return httpwrapper.NewResponse(http.StatusForbidden, nil, problemDetails) -} - -func getBDTPolicyContextProcedure(bdtPolicyID string) ( - response *models.BdtPolicy, problemDetails *models.ProblemDetails, -) { + // step 2: handle the message logger.BdtPolicyLog.Traceln("Handle BDT Policy GET") // check bdtPolicyID from pcfUeContext - if value, ok := pcf_context.GetSelf().BdtPolicyPool.Load(bdtPolicyID); ok { + if value, ok := p.Context().BdtPolicyPool.Load(bdtPolicyID); ok { bdtPolicy := value.(*models.BdtPolicy) - return bdtPolicy, nil + c.JSON(http.StatusOK, bdtPolicy) + return } else { // not found - problemDetail := util.GetProblemDetail("Can't find bdtPolicyID related resource", util.CONTEXT_NOT_FOUND) - logger.BdtPolicyLog.Warnf(problemDetail.Detail) - return nil, &problemDetail + problemDetails := util.GetProblemDetail("Can't find bdtPolicyID related resource", util.CONTEXT_NOT_FOUND) + logger.BdtPolicyLog.Warnf(problemDetails.Detail) + c.JSON(int(problemDetails.Status), problemDetails) + return } } // UpdateBDTPolicy - Update an Individual BDT policy (choose policy data) -func HandleUpdateBDTPolicyContextProcedure(request *httpwrapper.Request) *httpwrapper.Response { +func (p *Processor) HandleUpdateBDTPolicyContextProcedure( + c *gin.Context, + bdtPolicyID string, + bdtPolicyDataPatch models.BdtPolicyDataPatch, +) { // step 1: log logger.BdtPolicyLog.Infof("Handle UpdateBDTPolicyContext") - // step 2: retrieve request - requestDataType := request.Body.(models.BdtPolicyDataPatch) - bdtPolicyID := request.Params["bdtPolicyId"] - - // step 3: handle the message - response, problemDetails := updateBDTPolicyContextProcedure(requestDataType, bdtPolicyID) - - // step 4: process the return value from step 3 - if response != nil { - // status code is based on SPEC, and option headers - return httpwrapper.NewResponse(http.StatusOK, nil, response) - } else if problemDetails != nil { - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) - } - problemDetails = &models.ProblemDetails{ - Status: http.StatusForbidden, - Cause: "UNSPECIFIED", - } - return httpwrapper.NewResponse(http.StatusForbidden, nil, problemDetails) -} - -func updateBDTPolicyContextProcedure(request models.BdtPolicyDataPatch, bdtPolicyID string) ( - response *models.BdtPolicy, problemDetails *models.ProblemDetails, -) { + // step 2: handle the message logger.BdtPolicyLog.Infoln("Handle BDTPolicyUpdate") // check bdtPolicyID from pcfUeContext - pcfSelf := pcf_context.GetSelf() + pcfSelf := p.Context() var bdtPolicy *models.BdtPolicy - if value, ok := pcf_context.GetSelf().BdtPolicyPool.Load(bdtPolicyID); ok { + if value, ok := p.Context().BdtPolicyPool.Load(bdtPolicyID); ok { bdtPolicy = value.(*models.BdtPolicy) } else { // not found problemDetail := util.GetProblemDetail("Can't find bdtPolicyID related resource", util.CONTEXT_NOT_FOUND) logger.BdtPolicyLog.Warnf(problemDetail.Detail) - return nil, &problemDetail + c.JSON(int(problemDetail.Status), problemDetail) + return } for _, policy := range bdtPolicy.BdtPolData.TransfPolicies { - if policy.TransPolicyId == request.SelTransPolicyId { + if policy.TransPolicyId == bdtPolicyDataPatch.SelTransPolicyId { polData := bdtPolicy.BdtPolData polReq := bdtPolicy.BdtReqData - polData.SelTransPolicyId = request.SelTransPolicyId + polData.SelTransPolicyId = bdtPolicyDataPatch.SelTransPolicyId bdtData := models.BdtData{ AspId: polReq.AspId, TransPolicy: policy, @@ -117,10 +81,11 @@ func updateBDTPolicyContextProcedure(request models.BdtPolicyDataPatch, bdtPolic param := Nudr_DataRepository.PolicyDataBdtDataBdtReferenceIdPutParamOpts{ BdtData: optional.NewInterface(bdtData), } - client := util.GetNudrClient(getDefaultUdrUri(pcfSelf)) - ctx, pd, err := pcf_context.GetSelf().GetTokenCtx(models.ServiceName_NUDR_DR, models.NfType_UDR) + client := util.GetNudrClient(p.getDefaultUdrUri(pcfSelf)) + ctx, pd, err := p.Context().GetTokenCtx(models.ServiceName_NUDR_DR, models.NfType_UDR) if err != nil { - return nil, pd + c.JSON(int(pd.Status), pd) + return } rsp, err := client.DefaultApi.PolicyDataBdtDataBdtReferenceIdPut(ctx, bdtData.BdtRefId, ¶m) if err != nil { @@ -132,53 +97,44 @@ func updateBDTPolicyContextProcedure(request models.BdtPolicyDataPatch, bdtPolic } }() logger.BdtPolicyLog.Tracef("bdtPolicyID[%s] has Updated with SelTransPolicyId[%d]", - bdtPolicyID, request.SelTransPolicyId) - return bdtPolicy, nil + bdtPolicyID, bdtPolicyDataPatch.SelTransPolicyId) + c.JSON(http.StatusOK, bdtPolicy) + return } } problemDetail := util.GetProblemDetail( fmt.Sprintf("Can't find TransPolicyId[%d] in TransfPolicies with bdtPolicyID[%s]", - request.SelTransPolicyId, bdtPolicyID), + bdtPolicyDataPatch.SelTransPolicyId, bdtPolicyID), util.CONTEXT_NOT_FOUND) logger.BdtPolicyLog.Warnf(problemDetail.Detail) - return nil, &problemDetail + c.JSON(int(problemDetail.Status), problemDetail) } // CreateBDTPolicy - Create a new Individual BDT policy -func HandleCreateBDTPolicyContextRequest(request *httpwrapper.Request) *httpwrapper.Response { +func (p *Processor) HandleCreateBDTPolicyContextRequest( + c *gin.Context, + requestMsg models.BdtReqData, +) { // step 1: log logger.BdtPolicyLog.Infof("Handle CreateBDTPolicyContext") + var problemDetails *models.ProblemDetails + // step 2: retrieve request and check mandatory contents - requestMsg := request.Body.(models.BdtReqData) if requestMsg.AspId == "" || requestMsg.DesTimeInt == nil || requestMsg.NumOfUes == 0 || requestMsg.VolPerUe == nil { logger.BdtPolicyLog.Errorf("Required BdtReqData not found: AspId[%+v], DesTimeInt[%+v], NumOfUes[%+v], VolPerUe[%+v]", requestMsg.AspId, requestMsg.DesTimeInt, requestMsg.NumOfUes, requestMsg.VolPerUe) - return httpwrapper.NewResponse(http.StatusNotFound, nil, nil) + c.JSON(http.StatusNotFound, nil) + return } - // step 3: handle the message - header, response, problemDetails := createBDTPolicyContextProcedure(&requestMsg) - - // step 4: process the return value from step 3 - if response != nil { - // status code is based on SPEC, and option headers - return httpwrapper.NewResponse(http.StatusCreated, header, response) - } else if problemDetails != nil { - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) - } else { - return httpwrapper.NewResponse(http.StatusNotFound, nil, nil) - } -} + // // step 3: handle the message -func createBDTPolicyContextProcedure(request *models.BdtReqData) ( - header http.Header, response *models.BdtPolicy, problemDetails *models.ProblemDetails, -) { - response = &models.BdtPolicy{} + response := &models.BdtPolicy{} logger.BdtPolicyLog.Traceln("Handle BDT Policy Create") - pcfSelf := pcf_context.GetSelf() - udrUri := getDefaultUdrUri(pcfSelf) + pcfSelf := p.Context() + udrUri := p.getDefaultUdrUri(pcfSelf) if udrUri == "" { // Can't find any UDR support this Ue problemDetails = &models.ProblemDetails{ @@ -186,15 +142,17 @@ func createBDTPolicyContextProcedure(request *models.BdtReqData) ( Detail: "Can't find any UDR which supported to this PCF", } logger.BdtPolicyLog.Warnf(problemDetails.Detail) - return nil, nil, problemDetails + c.JSON(int(problemDetails.Status), problemDetails) + return } pcfSelf.DefaultUdrURI = udrUri pcfSelf.SetDefaultUdrURI(udrUri) // Query BDT DATA array from UDR - ctx, pd, err := pcf_context.GetSelf().GetTokenCtx(models.ServiceName_NUDR_DR, models.NfType_UDR) + ctx, pd, err := p.Context().GetTokenCtx(models.ServiceName_NUDR_DR, models.NfType_UDR) if err != nil { - return nil, nil, pd + c.JSON(int(pd.Status), pd) + return } client := util.GetNudrClient(udrUri) @@ -205,7 +163,8 @@ func createBDTPolicyContextProcedure(request *models.BdtReqData) ( Detail: "Query to UDR failed", } logger.BdtPolicyLog.Warnf("Query to UDR failed") - return nil, nil, problemDetails + c.JSON(int(problemDetails.Status), problemDetails) + return } defer func() { if rspCloseErr := httpResponse.Body.Close(); rspCloseErr != nil { @@ -213,12 +172,12 @@ func createBDTPolicyContextProcedure(request *models.BdtReqData) ( } }() // TODO: decide BDT Policy from other bdt policy data - response.BdtReqData = deepcopy.Copy(request).(*models.BdtReqData) + response.BdtReqData = deepcopy.Copy(requestMsg).(*models.BdtReqData) var bdtData *models.BdtData var bdtPolicyData models.BdtPolicyData for _, data := range bdtDatas { // If ASP has exist, use its background data policy - if request.AspId == data.AspId { + if requestMsg.AspId == data.AspId { bdtData = &data break } @@ -227,17 +186,17 @@ func createBDTPolicyContextProcedure(request *models.BdtReqData) ( if bdtData != nil { // found // modify policy according to new request - bdtData.TransPolicy.RecTimeInt = request.DesTimeInt + bdtData.TransPolicy.RecTimeInt = requestMsg.DesTimeInt } else { // use default bdt policy, TODO: decide bdt transfer data policy bdtData = &models.BdtData{ - AspId: request.AspId, + AspId: requestMsg.AspId, BdtRefId: uuid.New().String(), - TransPolicy: getDefaultTransferPolicy(1, *request.DesTimeInt), + TransPolicy: getDefaultTransferPolicy(1, *requestMsg.DesTimeInt), } } - if request.NwAreaInfo != nil { - bdtData.NwAreaInfo = *request.NwAreaInfo + if requestMsg.NwAreaInfo != nil { + bdtData.NwAreaInfo = *requestMsg.NwAreaInfo } bdtPolicyData.SelTransPolicyId = bdtData.TransPolicy.TransPolicyId // no support feature in subclause 5.8 of TS29554 @@ -251,7 +210,8 @@ func createBDTPolicyContextProcedure(request *models.BdtReqData) ( Detail: "Allocate bdtPolicyID failed", } logger.BdtPolicyLog.Warnf("Allocate bdtPolicyID failed") - return nil, nil, problemDetails + c.JSON(int(problemDetails.Status), problemDetails) + return } pcfSelf.BdtPolicyPool.Store(bdtPolicyID, response) @@ -275,14 +235,17 @@ func createBDTPolicyContextProcedure(request *models.BdtReqData) ( }() locationHeader := util.GetResourceUri(models.ServiceName_NPCF_BDTPOLICYCONTROL, bdtPolicyID) - header = http.Header{ - "Location": {locationHeader}, - } logger.BdtPolicyLog.Tracef("BDT Policy Id[%s] Create", bdtPolicyID) - return header, response, problemDetails + + if problemDetails != nil { + c.JSON(int(problemDetails.Status), problemDetails) + return + } + c.Header("Location", locationHeader) + c.JSON(http.StatusCreated, response) } -func getDefaultUdrUri(context *pcf_context.PCFContext) string { +func (p *Processor) getDefaultUdrUri(context *pcf_context.PCFContext) string { context.DefaultUdrURILock.RLock() defer context.DefaultUdrURILock.RUnlock() if context.DefaultUdrURI != "" { @@ -291,7 +254,7 @@ func getDefaultUdrUri(context *pcf_context.PCFContext) string { param := Nnrf_NFDiscovery.SearchNFInstancesParamOpts{ ServiceNames: optional.NewInterface([]models.ServiceName{models.ServiceName_NUDR_DR}), } - resp, err := consumer.SendSearchNFInstances(context.NrfUri, models.NfType_UDR, models.NfType_PCF, param) + resp, err := p.Consumer().SendSearchNFInstances(context.NrfUri, models.NfType_UDR, models.NfType_PCF, param) if err != nil { return "" } diff --git a/internal/sbi/producer/callback.go b/internal/sbi/processor/notifier.go similarity index 59% rename from internal/sbi/producer/callback.go rename to internal/sbi/processor/notifier.go index 29ab9dc..eb3b7ec 100644 --- a/internal/sbi/producer/callback.go +++ b/internal/sbi/processor/notifier.go @@ -1,76 +1,66 @@ -package producer +package processor import ( "fmt" "net/http" "strings" + "github.com/gin-gonic/gin" + "github.com/free5gc/openapi/models" - pcf_context "github.com/free5gc/pcf/internal/context" "github.com/free5gc/pcf/internal/logger" "github.com/free5gc/pcf/internal/util" - "github.com/free5gc/util/httpwrapper" ) -func HandleAmfStatusChangeNotify(request *httpwrapper.Request) *httpwrapper.Response { +func (p *Processor) HandleAmfStatusChangeNotify( + c *gin.Context, + amfStatusChangeNotification models.AmfStatusChangeNotification, +) { logger.CallbackLog.Warnf("[PCF] Handle Amf Status Change Notify is not implemented.") - notification := request.Body.(models.AmfStatusChangeNotification) - - AmfStatusChangeNotifyProcedure(notification) + // TODO: handle AMF Status Change Notify + logger.CallbackLog.Debugf("receive AMF status change notification[%+v]", amfStatusChangeNotification) - return httpwrapper.NewResponse(http.StatusNoContent, nil, nil) + c.JSON(http.StatusNoContent, nil) } -// TODO: handle AMF Status Change Notify -func AmfStatusChangeNotifyProcedure(notification models.AmfStatusChangeNotification) { - logger.CallbackLog.Debugf("receive AMF status change notification[%+v]", notification) -} - -func HandlePolicyDataChangeNotify(request *httpwrapper.Request) *httpwrapper.Response { +func (p *Processor) HandlePolicyDataChangeNotify( + c *gin.Context, + supi string, + policyDataChangeNotification models.PolicyDataChangeNotification, +) { logger.CallbackLog.Warnf("[PCF] Handle Policy Data Change Notify is not implemented.") - notification := request.Body.(models.PolicyDataChangeNotification) - supi := request.Params["supi"] - - PolicyDataChangeNotifyProcedure(supi, notification) + PolicyDataChangeNotifyProcedure(supi, policyDataChangeNotification) - return httpwrapper.NewResponse(http.StatusNotImplemented, nil, nil) + c.JSON(http.StatusNotImplemented, nil) } // TODO: handle Policy Data Change Notify func PolicyDataChangeNotifyProcedure(supi string, notification models.PolicyDataChangeNotification) { } -func HandleInfluenceDataUpdateNotify(request *httpwrapper.Request) *httpwrapper.Response { +func (p *Processor) HandleInfluenceDataUpdateNotify( + c *gin.Context, + supi string, + pduSessionId string, + trafficInfluDataNotif []models.TrafficInfluDataNotif, +) { logger.CallbackLog.Infof("[PCF] Handle Influence Data Update Notify") - notifications := request.Body.([]models.TrafficInfluDataNotif) - supi := request.Params["supi"] - pduSessionId := request.Params["pduSessionId"] - - if problemDetails := InfluenceDataUpdateNotifyProcedure(supi, pduSessionId, notifications); problemDetails != nil { - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) - } else { - return httpwrapper.NewResponse(http.StatusNoContent, nil, nil) - } -} - -func InfluenceDataUpdateNotifyProcedure(supi, pduSessionId string, - notifications []models.TrafficInfluDataNotif, -) *models.ProblemDetails { smPolicyID := fmt.Sprintf("%s-%s", supi, pduSessionId) - ue := pcf_context.GetSelf().PCFUeFindByPolicyId(smPolicyID) + ue := p.Context().PCFUeFindByPolicyId(smPolicyID) if ue == nil || ue.SmPolicyData[smPolicyID] == nil { problemDetail := util.GetProblemDetail("smPolicyID not found in PCF", util.CONTEXT_NOT_FOUND) logger.CallbackLog.Errorf(problemDetail.Detail) - return &problemDetail + c.JSON(int(problemDetail.Status), problemDetail) + return } smPolicy := ue.SmPolicyData[smPolicyID] decision := smPolicy.PolicyDecision influenceDataToPccRule := smPolicy.InfluenceDataToPccRule precedence := getAvailablePrecedence(smPolicy.PolicyDecision.PccRules) - for _, notification := range notifications { + for _, notification := range trafficInfluDataNotif { influenceID := getInfluenceID(notification.ResUri) if influenceID == "" { continue @@ -106,8 +96,8 @@ func InfluenceDataUpdateNotifyProcedure(supi, pduSessionId string, ResourceUri: util.GetResourceUri(models.ServiceName_NPCF_SMPOLICYCONTROL, smPolicyID), SmPolicyDecision: decision, } - go SendSMPolicyUpdateNotification(smPolicy.PolicyContext.NotificationUri, &smPolicyNotification) - return nil + go p.SendSMPolicyUpdateNotification(smPolicy.PolicyContext.NotificationUri, &smPolicyNotification) + c.JSON(http.StatusNoContent, nil) } func getInfluenceID(resUri string) string { diff --git a/internal/sbi/producer/oam.go b/internal/sbi/processor/oam.go similarity index 58% rename from internal/sbi/producer/oam.go rename to internal/sbi/processor/oam.go index d74dfa1..442ac46 100644 --- a/internal/sbi/producer/oam.go +++ b/internal/sbi/processor/oam.go @@ -1,13 +1,14 @@ -package producer +package processor import ( "net/http" "strconv" + "github.com/gin-gonic/gin" + "github.com/free5gc/openapi/models" "github.com/free5gc/pcf/internal/context" "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/util/httpwrapper" ) type UEAmPolicy struct { @@ -23,33 +24,17 @@ type UEAmPolicy struct { type UEAmPolicys []UEAmPolicy -func HandleOAMGetAmPolicyRequest(request *httpwrapper.Request) *httpwrapper.Response { +func (p *Processor) HandleOAMGetAmPolicyRequest( + c *gin.Context, + supi string, +) { // step 1: log logger.OamLog.Infof("Handle OAMGetAmPolicy") - // step 2: retrieve request - supi := request.Params["supi"] - // step 3: handle the message - response, problemDetails := OAMGetAmPolicyProcedure(supi) - - // step 4: process the return value from step 3 - if response != nil { - // status code is based on SPEC, and option headers - return httpwrapper.NewResponse(http.StatusOK, nil, response) - } else if problemDetails != nil { - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) - } - problemDetails = &models.ProblemDetails{ - Status: http.StatusForbidden, - Cause: "UNSPECIFIED", - } - return httpwrapper.NewResponse(http.StatusForbidden, nil, problemDetails) -} -func OAMGetAmPolicyProcedure(supi string) (response *UEAmPolicys, problemDetails *models.ProblemDetails) { logger.OamLog.Infof("Handle OAM Get Am Policy") - response = &UEAmPolicys{} + response := &UEAmPolicys{} pcfSelf := context.GetSelf() if val, exists := pcfSelf.UePool.Load(supi); exists { @@ -69,12 +54,14 @@ func OAMGetAmPolicyProcedure(supi string) (response *UEAmPolicys, problemDetails } *response = append(*response, ueAmPolicy) } - return response, nil + c.JSON(http.StatusOK, response) + return } else { - problemDetails = &models.ProblemDetails{ + problemDetails := &models.ProblemDetails{ Status: http.StatusNotFound, Cause: "CONTEXT_NOT_FOUND", } - return nil, problemDetails + c.JSON(int(problemDetails.Status), problemDetails) + return } } diff --git a/internal/sbi/producer/policyauthorization.go b/internal/sbi/processor/policyauthorization.go similarity index 87% rename from internal/sbi/producer/policyauthorization.go rename to internal/sbi/processor/policyauthorization.go index 7ecfd04..ddcce10 100644 --- a/internal/sbi/producer/policyauthorization.go +++ b/internal/sbi/processor/policyauthorization.go @@ -1,4 +1,4 @@ -package producer +package processor import ( "fmt" @@ -7,13 +7,13 @@ import ( "time" "github.com/cydev/zero" + "github.com/gin-gonic/gin" "github.com/free5gc/openapi" "github.com/free5gc/openapi/models" pcf_context "github.com/free5gc/pcf/internal/context" "github.com/free5gc/pcf/internal/logger" "github.com/free5gc/pcf/internal/util" - "github.com/free5gc/util/httpwrapper" ) const ( @@ -128,37 +128,38 @@ func handleMediaSubComponent(smPolicy *pcf_context.UeSmPolicyData, medComp *mode // Subscription to resources allocation outcome (DONE) // Invocation of Multimedia Priority Services (TODO) // Support of content versioning (TODO) -func HandlePostAppSessionsContext(request *httpwrapper.Request) *httpwrapper.Response { +func (p *Processor) HandlePostAppSessionsContext( + c *gin.Context, + appSessionContext models.AppSessionContext, +) { logger.PolicyAuthLog.Traceln("Handle Create AppSessions") - appSessCtx := request.Body.(models.AppSessionContext) - - response, locationHeader, problemDetails := postAppSessCtxProcedure(&appSessCtx) + response, locationHeader, problemDetails := p.postAppSessCtxProcedure(&appSessionContext) if response != nil { - headers := http.Header{ - "Location": {locationHeader}, - } - return httpwrapper.NewResponse(http.StatusCreated, headers, response) + c.Header("Location", locationHeader) + c.JSON(http.StatusCreated, response) + return } else if problemDetails != nil { - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) + c.JSON(int(problemDetails.Status), problemDetails) + return } problemDetails = &models.ProblemDetails{ Status: http.StatusForbidden, Cause: "UNSPECIFIED", } - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) + c.JSON(http.StatusForbidden, problemDetails) } -func postAppSessCtxProcedure(appSessCtx *models.AppSessionContext) (*models.AppSessionContext, +func (p *Processor) postAppSessCtxProcedure(appSessCtx *models.AppSessionContext) (*models.AppSessionContext, string, *models.ProblemDetails, ) { ascReqData := appSessCtx.AscReqData - pcfSelf := pcf_context.GetSelf() + pcfSelf := p.Context() // Initial BDT policy indication(the only one which is not related to session) if ascReqData.BdtRefId != "" { - if err := handleBDTPolicyInd(pcfSelf, appSessCtx); err != nil { + if err := p.handleBDTPolicyInd(pcfSelf, appSessCtx); err != nil { problemDetail := util.GetProblemDetail(err.Error(), util.ERROR_REQUEST_PARAMETERS) return nil, "", &problemDetail } @@ -428,36 +429,28 @@ func postAppSessCtxProcedure(appSessCtx *models.AppSessionContext) (*models.AppS ResourceUri: util.GetResourceUri(models.ServiceName_NPCF_SMPOLICYCONTROL, smPolicyID), SmPolicyDecision: smPolicy.PolicyDecision, } - go SendSMPolicyUpdateNotification(smPolicy.PolicyContext.NotificationUri, ¬ification) + go p.SendSMPolicyUpdateNotification(smPolicy.PolicyContext.NotificationUri, ¬ification) } return appSessCtx, locationHeader, nil } // HandleDeleteAppSession - Deletes an existing Individual Application Session Context -func HandleDeleteAppSessionContext(request *httpwrapper.Request) *httpwrapper.Response { - eventsSubscReqData := request.Body.(*models.EventsSubscReqData) - appSessID := request.Params["appSessionId"] - logger.PolicyAuthLog.Infof("Handle Del AppSessions, AppSessionId[%s]", appSessID) - - problemDetails := DeleteAppSessionContextProcedure(appSessID, eventsSubscReqData) - if problemDetails == nil { - return httpwrapper.NewResponse(http.StatusNoContent, nil, nil) - } else { - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) - } -} - -func DeleteAppSessionContextProcedure(appSessID string, +func (p *Processor) HandleDeleteAppSessionContext( + c *gin.Context, + appSessionId string, eventsSubscReqData *models.EventsSubscReqData, -) *models.ProblemDetails { - pcfSelf := pcf_context.GetSelf() +) { + logger.PolicyAuthLog.Infof("Handle Del AppSessions, AppSessionId[%s]", appSessionId) + + pcfSelf := p.Context() var appSession *pcf_context.AppSessionData - if val, ok := pcfSelf.AppSessionPool.Load(appSessID); ok { + if val, ok := pcfSelf.AppSessionPool.Load(appSessionId); ok { appSession = val.(*pcf_context.AppSessionData) } if appSession == nil { problemDetail := util.GetProblemDetail("can't find app session", util.APPLICATION_SESSION_CONTEXT_NOT_FOUND) - return &problemDetail + c.JSON(int(problemDetail.Status), problemDetail) + return } if eventsSubscReqData != nil { logger.PolicyAuthLog.Warnf("Delete AppSessions does not support with Event Subscription") @@ -471,9 +464,9 @@ func DeleteAppSessionContextProcedure(appSessID string, } } - delete(smPolicy.AppSessions, appSessID) + delete(smPolicy.AppSessions, appSessionId) - logger.PolicyAuthLog.Infof("App Session Id[%s] Del", appSessID) + logger.PolicyAuthLog.Infof("App Session Id[%s] Del", appSessionId) // TODO: AccUsageReport // if appSession.AccUsage != nil { @@ -487,7 +480,7 @@ func DeleteAppSessionContextProcedure(appSessID string, // } else { // } - pcfSelf.AppSessionPool.Delete(appSessID) + pcfSelf.AppSessionPool.Delete(appSessionId) smPolicy.ArrangeExistEventSubscription() @@ -497,79 +490,68 @@ func DeleteAppSessionContextProcedure(appSessID string, ResourceUri: util.GetResourceUri(models.ServiceName_NPCF_SMPOLICYCONTROL, smPolicyID), SmPolicyDecision: &deletedSmPolicyDec, } - go SendSMPolicyUpdateNotification(smPolicy.PolicyContext.NotificationUri, ¬ification) + go p.SendSMPolicyUpdateNotification(smPolicy.PolicyContext.NotificationUri, ¬ification) logger.PolicyAuthLog.Tracef("Send SM Policy[%s] Update Notification", smPolicyID) - return nil + c.JSON(http.StatusNoContent, nil) } // HandleGetAppSession - Reads an existing Individual Application Session Context -func HandleGetAppSessionContext(request *httpwrapper.Request) *httpwrapper.Response { - appSessID := request.Params["appSessionId"] - logger.PolicyAuthLog.Infof("Handle Get AppSessions, AppSessionId[%s]", appSessID) - - problemDetails, response := GetAppSessionContextProcedure(appSessID) - if problemDetails == nil { - return httpwrapper.NewResponse(http.StatusOK, nil, response) - } else { - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) - } -} +func (p *Processor) HandleGetAppSessionContext( + c *gin.Context, + appSessionId string, +) { + logger.PolicyAuthLog.Infof("Handle Get AppSessions, AppSessionId[%s]", appSessionId) -func GetAppSessionContextProcedure(appSessID string) (*models.ProblemDetails, *models.AppSessionContext) { - pcfSelf := pcf_context.GetSelf() + pcfSelf := p.Context() var appSession *pcf_context.AppSessionData - if val, ok := pcfSelf.AppSessionPool.Load(appSessID); ok { + if val, ok := pcfSelf.AppSessionPool.Load(appSessionId); ok { appSession = val.(*pcf_context.AppSessionData) } if appSession == nil { problemDetail := util.GetProblemDetail("can't find app session", util.APPLICATION_SESSION_CONTEXT_NOT_FOUND) - return &problemDetail, nil + c.JSON(int(problemDetail.Status), problemDetail) + return } - logger.PolicyAuthLog.Tracef("App Session Id[%s] Get", appSessID) - return nil, appSession.AppSessionContext + logger.PolicyAuthLog.Tracef("App Session Id[%s] Get", appSessionId) + c.JSON(http.StatusOK, appSession.AppSessionContext) } // HandleModAppSession - Modifies an existing Individual Application Session Context -func HandleModAppSessionContext(request *httpwrapper.Request) *httpwrapper.Response { - appSessID := request.Params["appSessionId"] - ascUpdateData := request.Body.(models.AppSessionContextUpdateData) - logger.PolicyAuthLog.Infof("Handle Modify AppSessions, AppSessionId[%s]", appSessID) - - problemDetails, response := ModAppSessionContextProcedure(appSessID, ascUpdateData) - if problemDetails == nil { - return httpwrapper.NewResponse(http.StatusOK, nil, response) - } else { - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) - } -} +func (p *Processor) HandleModAppSessionContext( + c *gin.Context, + appSessionId string, + appSessionContextUpdateData models.AppSessionContextUpdateData, +) { + logger.PolicyAuthLog.Infof("Handle Modify AppSessions, AppSessionId[%s]", appSessionId) -func ModAppSessionContextProcedure(appSessID string, - ascUpdateData models.AppSessionContextUpdateData, -) (*models.ProblemDetails, *models.AppSessionContext) { - pcfSelf := pcf_context.GetSelf() + pcfSelf := p.Context() var appSession *pcf_context.AppSessionData - if val, ok := pcfSelf.AppSessionPool.Load(appSessID); ok { + if val, ok := pcfSelf.AppSessionPool.Load(appSessionId); ok { appSession = val.(*pcf_context.AppSessionData) } if appSession == nil { problemDetail := util.GetProblemDetail("can't find app session", util.APPLICATION_SESSION_CONTEXT_NOT_FOUND) - return &problemDetail, nil + c.JSON((int)(problemDetail.Status), problemDetail) + return } appSessCtx := appSession.AppSessionContext - if ascUpdateData.BdtRefId != "" { - appSessCtx.AscReqData.BdtRefId = ascUpdateData.BdtRefId - if err := handleBDTPolicyInd(pcfSelf, appSessCtx); err != nil { + if appSessionContextUpdateData.BdtRefId != "" { + appSessCtx.AscReqData.BdtRefId = appSessionContextUpdateData.BdtRefId + if err := p.handleBDTPolicyInd(pcfSelf, appSessCtx); err != nil { problemDetail := util.GetProblemDetail(err.Error(), util.ERROR_REQUEST_PARAMETERS) - return &problemDetail, nil + c.JSON(int(problemDetail.Status), problemDetail) + return } - logger.PolicyAuthLog.Tracef("App Session Id[%s] Updated", appSessID) - return nil, appSessCtx + logger.PolicyAuthLog.Tracef("App Session Id[%s] Updated", appSessionId) + c.JSON(http.StatusOK, appSessCtx) + return } smPolicy := appSession.SmPolicyData if smPolicy == nil { problemDetail := util.GetProblemDetail("Can't find related PDU Session", util.REQUESTED_SERVICE_NOT_AUTHORIZED) - return &problemDetail, nil + c.JSON(int(problemDetail.Status), problemDetail) + return } // InfluenceOnTrafficRouting = 1 in 29514 & Traffic Steering Control support = 1 in 29512 traffRoutSupp := util.CheckSuppFeat(appSessCtx.AscRespData.SuppFeat, @@ -579,9 +561,9 @@ func ModAppSessionContextProcedure(appSessID string, eventSubs := make(map[models.AfEvent]models.AfNotifMethod) updateSMpolicy := false - if ascUpdateData.MedComponents != nil { + if appSessionContextUpdateData.MedComponents != nil { precedence := getAvailablePrecedence(smPolicy.PolicyDecision.PccRules) - for compN, medCompRm := range ascUpdateData.MedComponents { + for compN, medCompRm := range appSessionContextUpdateData.MedComponents { medComp := transferMedCompRmToMedComp(&medCompRm) removeMediaComp(appSession, compN) if zero.IsZero(medComp) { @@ -601,7 +583,8 @@ func ModAppSessionContextProcedure(appSessID string, for _, medSubComp := range medComp.MedSubComps { if tempPccRule, problemDetail := handleMediaSubComponent(smPolicy, medComp, &medSubComp, var5qi); problemDetail != nil { - return problemDetail, nil + c.JSON(int(problemDetail.Status), problemDetail) + return } else { pccRule = tempPccRule } @@ -614,13 +597,14 @@ func ModAppSessionContextProcedure(appSessID string, // if medComp.AfAppId has value -> find pccRule by reqData.AfAppId, otherwise create a new pcc rule appID = medComp.AfAppId routeReq = medComp.AfRoutReq - } else if ascUpdateData.AfAppId != "" { - appID = ascUpdateData.AfAppId + } else if appSessionContextUpdateData.AfAppId != "" { + appID = appSessionContextUpdateData.AfAppId routeReq = medComp.AfRoutReq } else { problemDetail := util.GetProblemDetail("Media Component needs flows of subComp or afAppId", util.REQUESTED_SERVICE_NOT_AUTHORIZED) - return &problemDetail, nil + c.JSON(int(problemDetail.Status), problemDetail) + return } pccRule = util.GetPccRuleByAfAppId(smPolicy.PolicyDecision.PccRules, appID) @@ -634,7 +618,8 @@ func ModAppSessionContextProcedure(appSessID string, var ul, dl bool qosData, ul, dl = updateQosInMedComp(qosData, medComp) if problemDetail := modifyRemainBitRate(smPolicy, &qosData, ul, dl); problemDetail != nil { - return problemDetail, nil + c.JSON(int(problemDetail.Status), problemDetail) + return } } util.SetPccRuleRelatedData(smPolicy.PolicyDecision, pccRule, nil, &qosData, nil, nil) @@ -651,7 +636,8 @@ func ModAppSessionContextProcedure(appSessID string, var ul, dl bool qosData, ul, dl = updateQosInMedComp(*smPolicy.PolicyDecision.QosDecs[qosID], medComp) if problemDetail := modifyRemainBitRate(smPolicy, &qosData, ul, dl); problemDetail != nil { - return problemDetail, nil + c.JSON(int(problemDetail.Status), problemDetail) + return } smPolicy.PolicyDecision.QosDecs[qosData.QosId] = &qosData } @@ -669,9 +655,9 @@ func ModAppSessionContextProcedure(appSessID string, // Update of traffic routing information // TODO: check ascUpdateData.AfAppId with appSessCtx.AscReqData.AfAppId (now ascUpdateData.AfAppId is empty) - if ascUpdateData.AfRoutReq != nil && traffRoutSupp { - logger.PolicyAuthLog.Infof("Update Traffic Routing info - [%+v]", ascUpdateData.AfRoutReq) - appSessCtx.AscReqData.AfRoutReq = transferAfRoutReqRmToAfRoutReq(ascUpdateData.AfRoutReq) + if appSessionContextUpdateData.AfRoutReq != nil && traffRoutSupp { + logger.PolicyAuthLog.Infof("Update Traffic Routing info - [%+v]", appSessionContextUpdateData.AfRoutReq) + appSessCtx.AscReqData.AfRoutReq = transferAfRoutReqRmToAfRoutReq(appSessionContextUpdateData.AfRoutReq) // Update SmPolicyDecision pccRule := provisioningOfTrafficRoutingInfo(smPolicy, appSessCtx.AscReqData.AfAppId, appSessCtx.AscReqData.AfRoutReq, "") @@ -685,8 +671,8 @@ func ModAppSessionContextProcedure(appSessID string, relatedPccRuleIds[key] = pccRuleID } - if ascUpdateData.EvSubsc != nil { - for _, subs := range ascUpdateData.EvSubsc.Events { + if appSessionContextUpdateData.EvSubsc != nil { + for _, subs := range appSessionContextUpdateData.EvSubsc.Events { if subs.NotifMethod == "" { // default value "EVENT_DETECTION" subs.NotifMethod = models.AfNotifMethod_EVENT_DETECTION @@ -730,13 +716,13 @@ func ModAppSessionContextProcedure(appSessID string, if appSessCtx.AscReqData.EvSubsc == nil { appSessCtx.AscReqData.EvSubsc = new(models.EventsSubscReqData) } - appSessCtx.AscReqData.EvSubsc.Events = ascUpdateData.EvSubsc.Events - if ascUpdateData.EvSubsc.NotifUri != "" { - appSessCtx.AscReqData.EvSubsc.NotifUri = ascUpdateData.EvSubsc.NotifUri - appSession.EventUri = ascUpdateData.EvSubsc.NotifUri + appSessCtx.AscReqData.EvSubsc.Events = appSessionContextUpdateData.EvSubsc.Events + if appSessionContextUpdateData.EvSubsc.NotifUri != "" { + appSessCtx.AscReqData.EvSubsc.NotifUri = appSessionContextUpdateData.EvSubsc.NotifUri + appSession.EventUri = appSessionContextUpdateData.EvSubsc.NotifUri } - if ascUpdateData.EvSubsc.UsgThres != nil { - appSessCtx.AscReqData.EvSubsc.UsgThres = threshRmToThresh(ascUpdateData.EvSubsc.UsgThres) + if appSessionContextUpdateData.EvSubsc.UsgThres != nil { + appSessCtx.AscReqData.EvSubsc.UsgThres = threshRmToThresh(appSessionContextUpdateData.EvSubsc.UsgThres) } } else { // remove eventSubs @@ -746,20 +732,22 @@ func ModAppSessionContextProcedure(appSessID string, } // Moification provisioning of sponsored connectivity information - if ascUpdateData.AspId != "" && ascUpdateData.SponId != "" { - umID := util.GetUmId(ascUpdateData.AspId, ascUpdateData.SponId) + if appSessionContextUpdateData.AspId != "" && appSessionContextUpdateData.SponId != "" { + umID := util.GetUmId(appSessionContextUpdateData.AspId, appSessionContextUpdateData.SponId) var umData *models.UsageMonitoringData if tempUmData, err := extractUmData(umID, eventSubs, - threshRmToThresh(ascUpdateData.EvSubsc.UsgThres)); err != nil { + threshRmToThresh(appSessionContextUpdateData.EvSubsc.UsgThres)); err != nil { problemDetail := util.GetProblemDetail(err.Error(), util.REQUESTED_SERVICE_NOT_AUTHORIZED) - return &problemDetail, nil + c.JSON(int(problemDetail.Status), problemDetail) + return } else { umData = tempUmData } - if err := handleSponsoredConnectivityInformation(smPolicy, relatedPccRuleIds, ascUpdateData.AspId, - ascUpdateData.SponId, ascUpdateData.SponStatus, umData, &updateSMpolicy); err != nil { + if err := handleSponsoredConnectivityInformation(smPolicy, relatedPccRuleIds, appSessionContextUpdateData.AspId, + appSessionContextUpdateData.SponId, appSessionContextUpdateData.SponStatus, umData, &updateSMpolicy); err != nil { problemDetail := util.GetProblemDetail(err.Error(), util.REQUESTED_SERVICE_NOT_AUTHORIZED) - return &problemDetail, nil + c.JSON(int(problemDetail.Status), problemDetail) + return } } @@ -798,7 +786,7 @@ func ModAppSessionContextProcedure(appSessID string, } // TODO: MPS Service - logger.PolicyAuthLog.Tracef("App Session Id[%s] Updated", appSessID) + logger.PolicyAuthLog.Tracef("App Session Id[%s] Updated", appSessionId) smPolicy.ArrangeExistEventSubscription() @@ -809,43 +797,36 @@ func ModAppSessionContextProcedure(appSessID string, ResourceUri: util.GetResourceUri(models.ServiceName_NPCF_SMPOLICYCONTROL, smPolicyID), SmPolicyDecision: smPolicy.PolicyDecision, } - go SendSMPolicyUpdateNotification(smPolicy.PolicyContext.NotificationUri, ¬ification) + go p.SendSMPolicyUpdateNotification(smPolicy.PolicyContext.NotificationUri, ¬ification) logger.PolicyAuthLog.Tracef("Send SM Policy[%s] Update Notification", smPolicyID) } - return nil, appSessCtx + c.JSON(http.StatusOK, appSessCtx) } // HandleDeleteEventsSubsc - deletes the Events Subscription subresource -func HandleDeleteEventsSubscContext(request *httpwrapper.Request) *httpwrapper.Response { - appSessID := request.Params["appSessID"] - logger.PolicyAuthLog.Tracef("Handle Del AppSessions Events Subsc, AppSessionId[%s]", appSessID) - - problemDetails := DeleteEventsSubscContextProcedure(appSessID) - if problemDetails == nil { - return httpwrapper.NewResponse(http.StatusNoContent, nil, nil) - } else { - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) - } -} +func (p *Processor) HandleDeleteEventsSubscContext( + c *gin.Context, + appSessionId string, +) { + logger.PolicyAuthLog.Tracef("Handle Del AppSessions Events Subsc, AppSessionId[%s]", appSessionId) -func DeleteEventsSubscContextProcedure(appSessID string) *models.ProblemDetails { - pcfSelf := pcf_context.GetSelf() + pcfSelf := p.Context() var appSession *pcf_context.AppSessionData - if val, ok := pcfSelf.AppSessionPool.Load(appSessID); ok { + if val, ok := pcfSelf.AppSessionPool.Load(appSessionId); ok { appSession = val.(*pcf_context.AppSessionData) } if appSession == nil { problemDetail := util.GetProblemDetail("can't find app session", util.APPLICATION_SESSION_CONTEXT_NOT_FOUND) - return &problemDetail + c.JSON(int(problemDetail.Status), problemDetail) + return } + appSession.Events = nil appSession.EventUri = "" appSession.AppSessionContext.EvsNotif = nil appSession.AppSessionContext.AscReqData.EvSubsc = nil - // changed := appSession.SmPolicyData.ArrangeExistEventSubscription() - - logger.PolicyAuthLog.Tracef("App Session Id[%s] Del Events Subsc success", appSessID) + logger.PolicyAuthLog.Tracef("App Session Id[%s] Del Events Subsc success", appSessionId) smPolicy := appSession.SmPolicyData // Send Notification to SMF @@ -855,95 +836,30 @@ func DeleteEventsSubscContextProcedure(appSessID string) *models.ProblemDetails ResourceUri: util.GetResourceUri(models.ServiceName_NPCF_SMPOLICYCONTROL, smPolicyID), SmPolicyDecision: smPolicy.PolicyDecision, } - go SendSMPolicyUpdateNotification(smPolicy.PolicyContext.NotificationUri, ¬ification) + go p.SendSMPolicyUpdateNotification(smPolicy.PolicyContext.NotificationUri, ¬ification) logger.PolicyAuthLog.Tracef("Send SM Policy[%s] Update Notification", smPolicyID) } - return nil + c.JSON(http.StatusNoContent, nil) } // HandleUpdateEventsSubsc - creates or modifies an Events Subscription subresource -func HandleUpdateEventsSubscContext(request *httpwrapper.Request) *httpwrapper.Response { - EventsSubscReqData := request.Body.(models.EventsSubscReqData) - appSessID := request.Params["appSessID"] - logger.PolicyAuthLog.Tracef("Handle Put AppSessions Events Subsc, AppSessionId[%s]", appSessID) - - response, locationHeader, status, problemDetails := UpdateEventsSubscContextProcedure(appSessID, EventsSubscReqData) - if problemDetails != nil { - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) - } else if status == http.StatusCreated { - headers := http.Header{ - "Location": {locationHeader}, - } - return httpwrapper.NewResponse(http.StatusCreated, headers, response) - } else if status == http.StatusOK { - return httpwrapper.NewResponse(http.StatusOK, nil, response) - } else if status == http.StatusNoContent { - return httpwrapper.NewResponse(http.StatusNoContent, nil, response) - } - problemDetails = &models.ProblemDetails{ - Status: http.StatusForbidden, - Cause: "UNSPECIFIED", - } - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) -} - -func SendAppSessionEventNotification(appSession *pcf_context.AppSessionData, request models.EventsNotification) { - logger.PolicyAuthLog.Tracef("Send App Session Event Notification") - if appSession == nil { - logger.PolicyAuthLog.Warnln("Send App Session Event Notification Error[appSession is nil]") - return - } - uri := appSession.EventUri - - if uri != "" { - ctx, _, err := pcf_context.GetSelf().GetTokenCtx(models.ServiceName_NPCF_POLICYAUTHORIZATION, models.NfType_PCF) - if err != nil { - return - } - - request.EvSubsUri = fmt.Sprintf("%s/events-subscription", - util.GetResourceUri(models.ServiceName_NPCF_POLICYAUTHORIZATION, appSession.AppSessionId)) - client := util.GetNpcfPolicyAuthorizationCallbackClient() - httpResponse, err := client.PolicyAuthorizationEventNotificationApi.PolicyAuthorizationEventNotification( - ctx, uri, request) - if err != nil { - if httpResponse != nil { - logger.PolicyAuthLog.Warnf("Send App Session Event Notification Error[%s]", httpResponse.Status) - } else { - logger.PolicyAuthLog.Warnf("Send App Session Event Notification Failed[%s]", err.Error()) - } - return - } else if httpResponse == nil { - logger.PolicyAuthLog.Warnln("Send App Session Event Notification Failed[HTTP Response is nil]") - return - } - defer func() { - if rspCloseErr := httpResponse.Body.Close(); rspCloseErr != nil { - logger.PolicyAuthLog.Errorf( - "PolicyAuthorizationEventNotification response body cannot close: %+v", - rspCloseErr) - } - }() - if httpResponse.StatusCode != http.StatusOK && httpResponse.StatusCode != http.StatusNoContent { - logger.PolicyAuthLog.Warnf("Send App Session Event Notification Failed") - } else { - logger.PolicyAuthLog.Tracef("Send App Session Event Notification Success") - } - } -} - -func UpdateEventsSubscContextProcedure(appSessID string, eventsSubscReqData models.EventsSubscReqData) ( - *models.UpdateEventsSubscResponse, string, int, *models.ProblemDetails, +func (p *Processor) HandleUpdateEventsSubscContext( + c *gin.Context, + appSessionId string, + eventsSubscReqData models.EventsSubscReqData, ) { - pcfSelf := pcf_context.GetSelf() + logger.PolicyAuthLog.Tracef("Handle Put AppSessions Events Subsc, AppSessionId[%s]", appSessionId) + + pcfSelf := p.Context() var appSession *pcf_context.AppSessionData - if val, ok := pcfSelf.AppSessionPool.Load(appSessID); ok { + if val, ok := pcfSelf.AppSessionPool.Load(appSessionId); ok { appSession = val.(*pcf_context.AppSessionData) } if appSession == nil { problemDetail := util.GetProblemDetail("can't find app session", util.APPLICATION_SESSION_CONTEXT_NOT_FOUND) - return nil, "", int(problemDetail.Status), &problemDetail + c.JSON(int(problemDetail.Status), problemDetail) + return } smPolicy := appSession.SmPolicyData eventSubs := make(map[models.AfEvent]models.AfNotifMethod) @@ -1052,24 +968,72 @@ func UpdateEventsSubscContextProcedure(appSessID string, eventsSubscReqData mode ResourceUri: util.GetResourceUri(models.ServiceName_NPCF_SMPOLICYCONTROL, smPolicyID), SmPolicyDecision: smPolicy.PolicyDecision, } - go SendSMPolicyUpdateNotification(smPolicy.PolicyContext.NotificationUri, ¬ification) + go p.SendSMPolicyUpdateNotification(smPolicy.PolicyContext.NotificationUri, ¬ification) logger.PolicyAuthLog.Tracef("Send SM Policy[%s] Update Notification", smPolicyID) } if created { locationHeader := fmt.Sprintf("%s/events-subscription", - util.GetResourceUri(models.ServiceName_NPCF_POLICYAUTHORIZATION, appSessID)) - logger.PolicyAuthLog.Tracef("App Session Id[%s] Create Subscription", appSessID) - return &resp, locationHeader, http.StatusCreated, nil + util.GetResourceUri(models.ServiceName_NPCF_POLICYAUTHORIZATION, appSessionId)) + logger.PolicyAuthLog.Tracef("App Session Id[%s] Create Subscription", appSessionId) + c.Header("Location", locationHeader) + c.JSON(http.StatusCreated, resp) } else if resp.EvsNotif != nil { - logger.PolicyAuthLog.Tracef("App Session Id[%s] Modify Subscription", appSessID) - return &resp, "", http.StatusOK, nil + logger.PolicyAuthLog.Tracef("App Session Id[%s] Modify Subscription", appSessionId) + c.JSON(http.StatusOK, resp) } else { - logger.PolicyAuthLog.Tracef("App Session Id[%s] Modify Subscription", appSessID) - return &resp, "", http.StatusNoContent, nil + logger.PolicyAuthLog.Tracef("App Session Id[%s] Modify Subscription", appSessionId) + c.JSON(http.StatusNoContent, nil) + } +} + +func (p *Processor) SendAppSessionEventNotification(appSession *pcf_context.AppSessionData, + request models.EventsNotification, +) { + logger.PolicyAuthLog.Tracef("Send App Session Event Notification") + if appSession == nil { + logger.PolicyAuthLog.Warnln("Send App Session Event Notification Error[appSession is nil]") + return + } + uri := appSession.EventUri + + if uri != "" { + ctx, _, err := p.Context().GetTokenCtx(models.ServiceName_NPCF_POLICYAUTHORIZATION, models.NfType_PCF) + if err != nil { + return + } + + request.EvSubsUri = fmt.Sprintf("%s/events-subscription", + util.GetResourceUri(models.ServiceName_NPCF_POLICYAUTHORIZATION, appSession.AppSessionId)) + client := util.GetNpcfPolicyAuthorizationCallbackClient() + httpResponse, err := client.PolicyAuthorizationEventNotificationApi.PolicyAuthorizationEventNotification( + ctx, uri, request) + if err != nil { + if httpResponse != nil { + logger.PolicyAuthLog.Warnf("Send App Session Event Notification Error[%s]", httpResponse.Status) + } else { + logger.PolicyAuthLog.Warnf("Send App Session Event Notification Failed[%s]", err.Error()) + } + return + } else if httpResponse == nil { + logger.PolicyAuthLog.Warnln("Send App Session Event Notification Failed[HTTP Response is nil]") + return + } + defer func() { + if rspCloseErr := httpResponse.Body.Close(); rspCloseErr != nil { + logger.PolicyAuthLog.Errorf( + "PolicyAuthorizationEventNotification response body cannot close: %+v", + rspCloseErr) + } + }() + if httpResponse.StatusCode != http.StatusOK && httpResponse.StatusCode != http.StatusNoContent { + logger.PolicyAuthLog.Warnf("Send App Session Event Notification Failed") + } else { + logger.PolicyAuthLog.Tracef("Send App Session Event Notification Success") + } } } -func SendAppSessionTermination(appSession *pcf_context.AppSessionData, request models.TerminationInfo) { +func (p *Processor) SendAppSessionTermination(appSession *pcf_context.AppSessionData, request models.TerminationInfo) { logger.PolicyAuthLog.Tracef("Send App Session Termination") if appSession == nil { logger.PolicyAuthLog.Warnln("Send App Session Termination Error[appSession is nil]") @@ -1078,7 +1042,7 @@ func SendAppSessionTermination(appSession *pcf_context.AppSessionData, request m uri := appSession.AppSessionContext.AscReqData.NotifUri if uri != "" { - ctx, _, err := pcf_context.GetSelf().GetTokenCtx(models.ServiceName_NPCF_POLICYAUTHORIZATION, models.NfType_PCF) + ctx, _, err := p.Context().GetTokenCtx(models.ServiceName_NPCF_POLICYAUTHORIZATION, models.NfType_PCF) if err != nil { return } @@ -1113,7 +1077,7 @@ func SendAppSessionTermination(appSession *pcf_context.AppSessionData, request m } // Handle Create/ Modify Background Data Transfer Policy Indication -func handleBDTPolicyInd(pcfSelf *pcf_context.PCFContext, +func (p *Processor) handleBDTPolicyInd(pcfSelf *pcf_context.PCFContext, appSessCtx *models.AppSessionContext, ) (err error) { req := appSessCtx.AscReqData @@ -1130,12 +1094,12 @@ func handleBDTPolicyInd(pcfSelf *pcf_context.PCFContext, requestSuppFeat).String(), } - ctx, _, err := pcf_context.GetSelf().GetTokenCtx(models.ServiceName_NUDR_DR, models.NfType_UDR) + ctx, _, err := p.Context().GetTokenCtx(models.ServiceName_NUDR_DR, models.NfType_UDR) if err != nil { return err } - client := util.GetNudrClient(getDefaultUdrUri(pcfSelf)) + client := util.GetNudrClient(p.getDefaultUdrUri(pcfSelf)) bdtData, resp, err1 := client.DefaultApi.PolicyDataBdtDataBdtReferenceIdGet(ctx, req.BdtRefId) if err1 != nil { return fmt.Errorf("UDR Get BdtData error[%s]", err1.Error()) diff --git a/internal/sbi/processor/processor.go b/internal/sbi/processor/processor.go new file mode 100644 index 0000000..59b64f8 --- /dev/null +++ b/internal/sbi/processor/processor.go @@ -0,0 +1,23 @@ +package processor + +import ( + "github.com/free5gc/pcf/internal/sbi/consumer" + "github.com/free5gc/pcf/pkg/app" +) + +type PCF interface { + app.App + Consumer() *consumer.Consumer +} + +type Processor struct { + PCF +} + +func NewProcessor(pcf PCF, consumer *consumer.Consumer) (*Processor, error) { + p := &Processor{ + PCF: pcf, + } + + return p, nil +} diff --git a/internal/sbi/producer/smpolicy.go b/internal/sbi/processor/smpolicy.go similarity index 86% rename from internal/sbi/producer/smpolicy.go rename to internal/sbi/processor/smpolicy.go index 2a38936..f30cfca 100644 --- a/internal/sbi/producer/smpolicy.go +++ b/internal/sbi/processor/smpolicy.go @@ -1,4 +1,4 @@ -package producer +package processor import ( "fmt" @@ -7,6 +7,7 @@ import ( "strings" "github.com/antihax/optional" + "github.com/gin-gonic/gin" "go.mongodb.org/mongo-driver/bson" "github.com/free5gc/openapi" @@ -14,10 +15,8 @@ import ( "github.com/free5gc/openapi/models" pcf_context "github.com/free5gc/pcf/internal/context" "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/internal/sbi/consumer" "github.com/free5gc/pcf/internal/util" "github.com/free5gc/util/flowdesc" - "github.com/free5gc/util/httpwrapper" "github.com/free5gc/util/mongoapi" ) @@ -27,52 +26,12 @@ const ( chargingDataColl = "policyData.ues.chargingData" ) -// SmPoliciesPost - -func HandleCreateSmPolicyRequest(request *httpwrapper.Request) *httpwrapper.Response { - // step 1: log +func (p *Processor) HandleCreateSmPolicyRequest( + c *gin.Context, + request models.SmPolicyContextData, +) { logger.SmPolicyLog.Infof("Handle CreateSmPolicy") - // step 2: retrieve request - requestDataType := request.Body.(models.SmPolicyContextData) - - // step 3: handle the message - header, response, problemDetails := createSMPolicyProcedure(requestDataType) - - // step 4: process the return value from step 3 - if response != nil { - // status code is based on SPEC, and option headers - return httpwrapper.NewResponse(http.StatusCreated, header, response) - } else if problemDetails != nil { - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) - } else { - return httpwrapper.NewResponse(http.StatusNotFound, nil, nil) - } -} -func newQosDataWithQosFlowMap(qosFlow map[string]interface{}) *models.QosData { - qosData := &models.QosData{ - QosId: strconv.Itoa(int(qosFlow["qosRef"].(float64))), - Qnc: false, - Var5qi: int32(qosFlow["5qi"].(float64)), - } - if qosFlow["mbrUL"] != nil { - qosData.MaxbrUl = qosFlow["mbrUL"].(string) - } - if qosFlow["mbrDL"] != nil { - qosData.MaxbrDl = qosFlow["mbrDL"].(string) - } - if qosFlow["gbrUL"] != nil { - qosData.GbrUl = qosFlow["gbrUL"].(string) - } - if qosFlow["gbrDL"] != nil { - qosData.GbrDl = qosFlow["gbrDL"].(string) - } - - return qosData -} - -func createSMPolicyProcedure(request models.SmPolicyContextData) ( - header http.Header, response *models.SmPolicyDecision, problemDetails *models.ProblemDetails, -) { var err error queryStrength := 2 // 2: case-insensitive, 3: case-sensitive logger.SmPolicyLog.Tracef("Handle Create SM Policy Request") @@ -80,10 +39,11 @@ func createSMPolicyProcedure(request models.SmPolicyContextData) ( if request.Supi == "" || request.SliceInfo == nil || len(request.SliceInfo.Sd) != 6 { problemDetail := util.GetProblemDetail("Errorneous/Missing Mandotory IE", util.ERROR_INITIAL_PARAMETERS) logger.SmPolicyLog.Warnln("Errorneous/Missing Mandotory IE", util.ERROR_INITIAL_PARAMETERS) - return nil, nil, &problemDetail + c.JSON(int(problemDetail.Status), problemDetail) + return } - pcfSelf := pcf_context.GetSelf() + pcfSelf := p.Context() var ue *pcf_context.UeContext if val, exist := pcfSelf.UePool.Load(request.Supi); exist { ue = val.(*pcf_context.UeContext) @@ -92,13 +52,15 @@ func createSMPolicyProcedure(request models.SmPolicyContextData) ( if ue == nil { problemDetail := util.GetProblemDetail("Supi is not supported in PCF", util.USER_UNKNOWN) logger.SmPolicyLog.Warnf("Supi[%s] is not supported in PCF", request.Supi) - return nil, nil, &problemDetail + c.JSON(int(problemDetail.Status), problemDetail) + return } - udrUri := getUdrUri(ue) + udrUri := p.getUdrUri(ue) if udrUri == "" { problemDetail := util.GetProblemDetail("Can't find corresponding UDR with UE", util.USER_UNKNOWN) logger.SmPolicyLog.Warnf("Can't find corresponding UDR with UE[%s]", ue.Supi) - return nil, nil, &problemDetail + c.JSON(int(problemDetail.Status), problemDetail) + return } var smData models.SmPolicyData smPolicyID := fmt.Sprintf("%s-%d", ue.Supi, request.PduSessionId) @@ -111,16 +73,18 @@ func createSMPolicyProcedure(request models.SmPolicyContextData) ( } var response *http.Response - ctx, pd, err1 := pcf_context.GetSelf().GetTokenCtx(models.ServiceName_NUDR_DR, models.NfType_UDR) + ctx, pd, err1 := p.Context().GetTokenCtx(models.ServiceName_NUDR_DR, models.NfType_UDR) if err1 != nil { - return nil, nil, pd + c.JSON(int(pd.Status), pd) + return } smData, response, err1 = client.DefaultApi.PolicyDataUesUeIdSmDataGet(ctx, ue.Supi, ¶m) if err1 != nil || response == nil || response.StatusCode != http.StatusOK { problemDetail := util.GetProblemDetail("Can't find UE SM Policy Data in UDR", util.USER_UNKNOWN) logger.SmPolicyLog.Warnf("Can't find UE[%s] SM Policy Data in UDR", ue.Supi) - return nil, nil, &problemDetail + c.JSON(int(problemDetail.Status), problemDetail) + return } defer func() { if rspCloseErr := response.Body.Close(); rspCloseErr != nil { @@ -136,8 +100,8 @@ func createSMPolicyProcedure(request models.SmPolicyContextData) ( if amPolicy == nil { problemDetail := util.GetProblemDetail("Can't find corresponding AM Policy", util.POLICY_CONTEXT_DENIED) logger.SmPolicyLog.Warnf("Can't find corresponding AM Policy") - // message.SendHttpResponseMessage(httpChannel, nil, int(rsp.Status), rsp) - return nil, nil, &problemDetail + c.JSON(int(problemDetail.Status), problemDetail) + return } // TODO: check service restrict if ue.Gpsi == "" { @@ -247,11 +211,12 @@ func createSMPolicyProcedure(request models.SmPolicyContextData) ( logger.SmPolicyLog.Errorf("chargingInterface %+v", chargingInterface) util.SetPccRuleRelatedData(&decision, pcc, nil, nil, nil, nil) } else if chargingInterface != nil { - rg, err1 := pcf_context.GetSelf().RatingGroupIdGenerator.Allocate() + rg, err1 := p.Context().RatingGroupIdGenerator.Allocate() if err1 != nil { logger.SmPolicyLog.Error("rating group allocate error") problemDetails := util.GetProblemDetail("rating group allocate error", util.ERROR_IDGENERATOR) - return nil, nil, &problemDetails + c.JSON(int(problemDetails.Status), problemDetails) + return } chgData := &models.ChargingData{ ChgId: util.GetChgId(smPolicyData.ChargingIdGenerator), @@ -342,11 +307,12 @@ func createSMPolicyProcedure(request models.SmPolicyContextData) ( if err != nil { logger.SmPolicyLog.Errorf("Fail to get charging data to mongoDB err: %+v", err) } else { - rg, err1 := pcf_context.GetSelf().RatingGroupIdGenerator.Allocate() + rg, err1 := p.Context().RatingGroupIdGenerator.Allocate() if err1 != nil { logger.SmPolicyLog.Error("rating group allocate error") problemDetails := util.GetProblemDetail("rating group allocate error", util.ERROR_IDGENERATOR) - return nil, nil, &problemDetails + c.JSON(int(problemDetails.Status), problemDetails) + return } chgData := &models.ChargingData{ ChgId: util.GetChgId(smPolicyData.ChargingIdGenerator), @@ -407,9 +373,10 @@ func createSMPolicyProcedure(request models.SmPolicyContextData) ( Supis: optional.NewInterface([]string{request.Supi}), } - ctx, pd, err := pcf_context.GetSelf().GetTokenCtx(models.ServiceName_NUDR_DR, models.NfType_UDR) + ctx, pd, err := p.Context().GetTokenCtx(models.ServiceName_NUDR_DR, models.NfType_UDR) if err != nil { - return nil, nil, pd + c.JSON(int(pd.Status), pd) + return } udrClient := util.GetNudrClient(udrUri) @@ -441,7 +408,7 @@ func createSMPolicyProcedure(request models.SmPolicyContextData) ( } // Subscribe to Traffic Influence Data in UDR - subscriptionID, problemDetail, err := consumer.CreateInfluenceDataSubscription(ue, request) + subscriptionID, problemDetail, err := p.Consumer().CreateInfluenceDataSubscription(ue, request) if problemDetail != nil { logger.SmPolicyLog.Errorf("Subscribe UDR Influence Data Failed Problem[%+v]", problemDetail) } else if err != nil { @@ -450,7 +417,7 @@ func createSMPolicyProcedure(request models.SmPolicyContextData) ( smPolicyData.SubscriptionID = subscriptionID // Create PCF binding data to BSF - policyAuthorizationService := pcf_context.GetSelf().NfService[models.ServiceName_NPCF_POLICYAUTHORIZATION] + policyAuthorizationService := p.Context().NfService[models.ServiceName_NPCF_POLICYAUTHORIZATION] pcfBinding := models.PcfBinding{ Supi: request.Supi, Gpsi: request.Gpsi, @@ -464,13 +431,14 @@ func createSMPolicyProcedure(request models.SmPolicyContextData) ( } // TODO: Record BSF URI instead of discovering from NRF every time - bsfUri := consumer.SendNFInstancesBSF(pcf_context.GetSelf().NrfUri) + bsfUri := p.Consumer().SendNFInstancesBSF(p.Context().NrfUri) if bsfUri != "" { bsfClient := util.GetNbsfClient(bsfUri) - ctx, pd, err = pcf_context.GetSelf().GetTokenCtx(models.ServiceName_NBSF_MANAGEMENT, models.NfType_BSF) + ctx, pd, err = p.Context().GetTokenCtx(models.ServiceName_NBSF_MANAGEMENT, models.NfType_BSF) if err != nil { - return nil, nil, pd + c.JSON(int(pd.Status), pd) + return } _, resp, err = bsfClient.PCFBindingsCollectionApi.CreatePCFBinding(ctx, pcfBinding) @@ -487,48 +455,55 @@ func createSMPolicyProcedure(request models.SmPolicyContextData) ( } } locationHeader := util.GetResourceUri(models.ServiceName_NPCF_SMPOLICYCONTROL, smPolicyID) - header = http.Header{ - "Location": {locationHeader}, - } + c.Header("Location", locationHeader) logger.SmPolicyLog.Tracef("SMPolicy PduSessionId[%d] Create", request.PduSessionId) + c.JSON(http.StatusCreated, decision) +} - return header, &decision, nil +func newQosDataWithQosFlowMap(qosFlow map[string]interface{}) *models.QosData { + qosData := &models.QosData{ + QosId: strconv.Itoa(int(qosFlow["qosRef"].(float64))), + Qnc: false, + Var5qi: int32(qosFlow["5qi"].(float64)), + } + if qosFlow["mbrUL"] != nil { + qosData.MaxbrUl = qosFlow["mbrUL"].(string) + } + if qosFlow["mbrDL"] != nil { + qosData.MaxbrDl = qosFlow["mbrDL"].(string) + } + if qosFlow["gbrUL"] != nil { + qosData.GbrUl = qosFlow["gbrUL"].(string) + } + if qosFlow["gbrDL"] != nil { + qosData.GbrDl = qosFlow["gbrDL"].(string) + } + + return qosData } // SmPoliciessmPolicyIDDeletePost - -func HandleDeleteSmPolicyContextRequest(request *httpwrapper.Request) *httpwrapper.Response { - // step 1: log +func (p *Processor) HandleDeleteSmPolicyContextRequest( + c *gin.Context, + smPolicyId string, +) { logger.SmPolicyLog.Infof("Handle DeleteSmPolicyContext") - // step 2: retrieve request - smPolicyID := request.Params["smPolicyId"] - - // step 3: handle the message - problemDetails := deleteSmPolicyContextProcedure(smPolicyID) - - // step 4: process the return value from step 3 - if problemDetails != nil { - // status code is based on SPEC, and option headers - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) - } else { - return httpwrapper.NewResponse(http.StatusNoContent, nil, nil) - } -} - -func deleteSmPolicyContextProcedure(smPolicyID string) *models.ProblemDetails { + // handle the message logger.AmPolicyLog.Traceln("Handle SM Policy Delete") - ue := pcf_context.GetSelf().PCFUeFindByPolicyId(smPolicyID) - if ue == nil || ue.SmPolicyData[smPolicyID] == nil { + ue := p.Context().PCFUeFindByPolicyId(smPolicyId) + if ue == nil || ue.SmPolicyData[smPolicyId] == nil { problemDetail := util.GetProblemDetail("smPolicyID not found in PCF", util.CONTEXT_NOT_FOUND) logger.SmPolicyLog.Warnf(problemDetail.Detail) - return &problemDetail + c.JSON(int(problemDetail.Status), problemDetail) + return } - pcfSelf := pcf_context.GetSelf() - smPolicy := ue.SmPolicyData[smPolicyID] + pcfSelf := p.Context() + smPolicy := ue.SmPolicyData[smPolicyId] - problemDetail, err := consumer.RemoveInfluenceDataSubscription(ue, smPolicy.SubscriptionID) + problemDetail, err := p.Consumer().RemoveInfluenceDataSubscription(ue, smPolicy.SubscriptionID) if problemDetail != nil { logger.SmPolicyLog.Errorf("Remove UDR Influence Data Subscription Failed Problem[%+v]", problemDetail) } else if err != nil { @@ -536,8 +511,8 @@ func deleteSmPolicyContextProcedure(smPolicyID string) *models.ProblemDetails { } // Unsubscrice UDR - delete(ue.SmPolicyData, smPolicyID) - logger.SmPolicyLog.Tracef("SMPolicy smPolicyID[%s] DELETE", smPolicyID) + delete(ue.SmPolicyData, smPolicyId) + logger.SmPolicyLog.Tracef("SMPolicy smPolicyID[%s] DELETE", smPolicyId) // Release related App Session terminationInfo := models.TerminationInfo{ @@ -546,13 +521,13 @@ func deleteSmPolicyContextProcedure(smPolicyID string) *models.ProblemDetails { for appSessionID := range smPolicy.AppSessions { if val, exist := pcfSelf.AppSessionPool.Load(appSessionID); exist { appSession := val.(*pcf_context.AppSessionData) - SendAppSessionTermination(appSession, terminationInfo) + p.SendAppSessionTermination(appSession, terminationInfo) pcfSelf.AppSessionPool.Delete(appSessionID) - logger.SmPolicyLog.Tracef("SMPolicy[%s] DELETE Related AppSession[%s]", smPolicyID, appSessionID) + logger.SmPolicyLog.Tracef("SMPolicy[%s] DELETE Related AppSession[%s]", smPolicyId, appSessionID) } } - for _, ratingGroup := range ue.RatingGroupData[smPolicyID] { + for _, ratingGroup := range ue.RatingGroupData[smPolicyId] { pcfSelf.RatingGroupIdGenerator.FreeID(int64(ratingGroup)) filterCharging := bson.M{ @@ -563,92 +538,51 @@ func deleteSmPolicyContextProcedure(smPolicyID string) *models.ProblemDetails { logger.SmPolicyLog.Errorf("Fail to delete charging data, ratingGroup: %+v, err: %+v", ratingGroup, err) } } - delete(ue.RatingGroupData, smPolicyID) - return nil -} - -// SmPoliciessmPolicyIDGet - -func HandleGetSmPolicyContextRequest(request *httpwrapper.Request) *httpwrapper.Response { - // step 1: log - logger.SmPolicyLog.Infof("Handle GetSmPolicyContext") - - // step 2: retrieve request - smPolicyID := request.Params["smPolicyId"] - // step 3: handle the message - response, problemDetails := getSmPolicyContextProcedure(smPolicyID) - - // step 4: process the return value from step 3 - if response != nil { - // status code is based on SPEC, and option headers - return httpwrapper.NewResponse(http.StatusOK, nil, response) - } else if problemDetails != nil { - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) - } - problemDetails = &models.ProblemDetails{ - Status: http.StatusForbidden, - Cause: "UNSPECIFIED", - } - return httpwrapper.NewResponse(http.StatusForbidden, nil, problemDetails) + delete(ue.RatingGroupData, smPolicyId) + c.JSON(http.StatusNoContent, nil) } -func getSmPolicyContextProcedure(smPolicyID string) ( - response *models.SmPolicyControl, problemDetails *models.ProblemDetails, +func (p *Processor) HandleGetSmPolicyContextRequest( + c *gin.Context, + smPolicyId string, ) { + logger.SmPolicyLog.Infof("Handle GetSmPolicyContext") + // handle the message logger.SmPolicyLog.Traceln("Handle GET SM Policy Request") - ue := pcf_context.GetSelf().PCFUeFindByPolicyId(smPolicyID) - if ue == nil || ue.SmPolicyData[smPolicyID] == nil { + ue := p.Context().PCFUeFindByPolicyId(smPolicyId) + if ue == nil || ue.SmPolicyData[smPolicyId] == nil { problemDetail := util.GetProblemDetail("smPolicyID not found in PCF", util.CONTEXT_NOT_FOUND) logger.SmPolicyLog.Warnf(problemDetail.Detail) - return nil, &problemDetail + c.JSON(int(problemDetail.Status), problemDetail) + return } - smPolicyData := ue.SmPolicyData[smPolicyID] - response = &models.SmPolicyControl{ + smPolicyData := ue.SmPolicyData[smPolicyId] + response := &models.SmPolicyControl{ Policy: smPolicyData.PolicyDecision, Context: smPolicyData.PolicyContext, } - logger.SmPolicyLog.Tracef("SMPolicy smPolicyID[%s] GET", smPolicyID) - return response, nil + logger.SmPolicyLog.Tracef("SMPolicy smPolicyID[%s] GET", smPolicyId) + c.JSON(http.StatusOK, response) } -// SmPoliciessmPolicyIDUpdatePost - -func HandleUpdateSmPolicyContextRequest(request *httpwrapper.Request) *httpwrapper.Response { - // step 1: log +func (p *Processor) HandleUpdateSmPolicyContextRequest( + c *gin.Context, + smPolicyId string, + request models.SmPolicyUpdateContextData, +) { logger.SmPolicyLog.Infof("Handle UpdateSmPolicyContext") - // step 2: retrieve request - requestDataType := request.Body.(models.SmPolicyUpdateContextData) - smPolicyID := request.Params["smPolicyId"] - - // step 3: handle the message - response, problemDetails := updateSmPolicyContextProcedure(requestDataType, smPolicyID) - - // step 4: process the return value from step 3 - if response != nil { - // status code is based on SPEC, and option headers - return httpwrapper.NewResponse(http.StatusOK, nil, response) - } else if problemDetails != nil { - return httpwrapper.NewResponse(int(problemDetails.Status), nil, problemDetails) - } - problemDetails = &models.ProblemDetails{ - Status: http.StatusForbidden, - Cause: "UNSPECIFIED", - } - return httpwrapper.NewResponse(http.StatusForbidden, nil, problemDetails) -} - -func updateSmPolicyContextProcedure(request models.SmPolicyUpdateContextData, smPolicyID string) ( - response *models.SmPolicyDecision, problemDetails *models.ProblemDetails, -) { logger.SmPolicyLog.Traceln("Handle updateSmPolicyContext") - ue := pcf_context.GetSelf().PCFUeFindByPolicyId(smPolicyID) - if ue == nil || ue.SmPolicyData[smPolicyID] == nil { + ue := p.Context().PCFUeFindByPolicyId(smPolicyId) + if ue == nil || ue.SmPolicyData[smPolicyId] == nil { problemDetail := util.GetProblemDetail("smPolicyID not found in PCF", util.CONTEXT_NOT_FOUND) logger.SmPolicyLog.Warnf(problemDetail.Detail) - return nil, &problemDetail + c.JSON(int(problemDetail.Status), problemDetail) + return } - smPolicy := ue.SmPolicyData[smPolicyID] + smPolicy := ue.SmPolicyData[smPolicyId] smPolicyDecision := smPolicy.PolicyDecision smPolicyContext := smPolicy.PolicyContext errCause := "" @@ -706,7 +640,8 @@ func updateSmPolicyContextProcedure(request models.SmPolicyUpdateContextData, sm if err != nil { problemDetail := util.GetProblemDetail(err.Error(), util.ERROR_TRAFFIC_MAPPING_INFO_REJECTED) logger.SmPolicyLog.Warnf(problemDetail.Detail) - return nil, &problemDetail + c.JSON(int(problemDetail.Status), problemDetail) + return } if qosData.GbrDl != "" { logger.SmPolicyLog.Tracef("SM Policy Dnn[%s] Data Aggregate decrease %s and then DL GBR remain[%.2f Kbps]", @@ -754,7 +689,8 @@ func updateSmPolicyContextProcedure(request models.SmPolicyUpdateContextData, sm smPolicy.RemainGbrUL = origUl problemDetail := util.GetProblemDetail(err.Error(), util.ERROR_TRAFFIC_MAPPING_INFO_REJECTED) logger.SmPolicyLog.Warnf(problemDetail.Detail) - return nil, &problemDetail + c.JSON(int(problemDetail.Status), problemDetail) + return } qosData.Var5qi = req.ReqQos.Var5qi qosData.GbrDl = gbrDl @@ -996,26 +932,26 @@ func updateSmPolicyContextProcedure(request models.SmPolicyUpdateContextData, sm afEventsNotification.EvNotifs = append(afEventsNotification.EvNotifs, afNotif) } if afEventsNotification.EvNotifs != nil { - sendSmPolicyRelatedAppSessionNotification( + p.sendSmPolicyRelatedAppSessionNotification( smPolicy, afEventsNotification, request.AccuUsageReports, successRules, failRules) } if errCause != "" { problemDetail := util.GetProblemDetail(errCause, util.ERROR_TRIGGER_EVENT) logger.SmPolicyLog.Warnf(errCause) - return nil, &problemDetail + c.JSON(int(problemDetail.Status), problemDetail) + return } - logger.SmPolicyLog.Tracef("SMPolicy smPolicyID[%s] Update", smPolicyID) - // message.SendHttpResponseMessage(httpChannel, nil, http.StatusOK, *smPolicyDecision) - return smPolicyDecision, nil + logger.SmPolicyLog.Tracef("SMPolicy smPolicyID[%s] Update", smPolicyId) + c.JSON(http.StatusOK, smPolicyDecision) } -func sendSmPolicyRelatedAppSessionNotification(smPolicy *pcf_context.UeSmPolicyData, +func (p *Processor) sendSmPolicyRelatedAppSessionNotification(smPolicy *pcf_context.UeSmPolicyData, notification models.EventsNotification, usageReports []models.AccuUsageReport, successRules, failRules []models.RuleReport, ) { for appSessionId := range smPolicy.AppSessions { - if val, exist := pcf_context.GetSelf().AppSessionPool.Load(appSessionId); exist { + if val, exist := p.Context().AppSessionPool.Load(appSessionId); exist { appSession := val.(*pcf_context.AppSessionData) if len(appSession.Events) == 0 { continue @@ -1174,13 +1110,13 @@ func sendSmPolicyRelatedAppSessionNotification(smPolicy *pcf_context.UeSmPolicyD } } if sessionNotif.EvNotifs != nil { - SendAppSessionEventNotification(appSession, sessionNotif) + p.SendAppSessionEventNotification(appSession, sessionNotif) } } } } -func SendSMPolicyUpdateNotification( +func (p *Processor) SendSMPolicyUpdateNotification( uri string, request *models.SmPolicyNotification, ) { if uri == "" { @@ -1188,7 +1124,7 @@ func SendSMPolicyUpdateNotification( return } - ctx, _, err := pcf_context.GetSelf().GetTokenCtx(models.ServiceName_NPCF_SMPOLICYCONTROL, models.NfType_PCF) + ctx, _, err := p.Context().GetTokenCtx(models.ServiceName_NPCF_SMPOLICYCONTROL, models.NfType_PCF) if err != nil { return } @@ -1222,7 +1158,7 @@ func SendSMPolicyUpdateNotification( } } -func SendSMPolicyTerminationRequestNotification( +func (p *Processor) SendSMPolicyTerminationRequestNotification( uri string, request *models.TerminationNotification, ) { if uri == "" { @@ -1230,7 +1166,7 @@ func SendSMPolicyTerminationRequestNotification( return } - ctx, _, err := pcf_context.GetSelf().GetTokenCtx(models.ServiceName_NPCF_SMPOLICYCONTROL, models.NfType_PCF) + ctx, _, err := p.Context().GetTokenCtx(models.ServiceName_NPCF_SMPOLICYCONTROL, models.NfType_PCF) if err != nil { return } diff --git a/internal/sbi/server.go b/internal/sbi/server.go new file mode 100644 index 0000000..997d810 --- /dev/null +++ b/internal/sbi/server.go @@ -0,0 +1,182 @@ +package sbi + +import ( + "context" + "fmt" + "log" + "net/http" + "runtime/debug" + "sync" + "time" + + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" + + "github.com/free5gc/openapi/models" + "github.com/free5gc/pcf/internal/logger" + "github.com/free5gc/pcf/internal/sbi/consumer" + "github.com/free5gc/pcf/internal/sbi/processor" + "github.com/free5gc/pcf/internal/util" + "github.com/free5gc/pcf/pkg/app" + "github.com/free5gc/pcf/pkg/factory" + "github.com/free5gc/util/httpwrapper" + logger_util "github.com/free5gc/util/logger" +) + +type Route struct { + Method string + Pattern string + APIFunc gin.HandlerFunc +} + +func applyRoutes(group *gin.RouterGroup, routes []Route) { + for _, route := range routes { + switch route.Method { + case "GET": + group.GET(route.Pattern, route.APIFunc) + case "POST": + group.POST(route.Pattern, route.APIFunc) + case "PUT": + group.PUT(route.Pattern, route.APIFunc) + case "PATCH": + group.PATCH(route.Pattern, route.APIFunc) + case "DELETE": + group.DELETE(route.Pattern, route.APIFunc) + } + } +} + +type pcf interface { + app.App + Processor() *processor.Processor + Consumer() *consumer.Consumer +} + +type Server struct { + pcf + + httpServer *http.Server + router *gin.Engine +} + +func NewServer(pcf pcf, tlsKeyLogPath string) (*Server, error) { + s := &Server{ + pcf: pcf, + router: logger_util.NewGinWithLogrus(logger.GinLog), + } + + smPolicyRoutes := s.getSmPolicyRoutes() + smPolicyGroup := s.router.Group(factory.PcfSMpolicyCtlResUriPrefix) + applyRoutes(smPolicyGroup, smPolicyRoutes) + + amPolicyRoutes := s.getAmPolicyRoutes() + amPolicyGroup := s.router.Group(factory.PcfAMpolicyCtlResUriPrefix) + amRouterAuthorizationCheck := util.NewRouterAuthorizationCheck(models.ServiceName_NPCF_AM_POLICY_CONTROL) + amPolicyGroup.Use(func(c *gin.Context) { + amRouterAuthorizationCheck.Check(c, s.Context()) + }) + applyRoutes(amPolicyGroup, amPolicyRoutes) + + bdtPolicyRoutes := s.getBdtPolicyRoutes() + bdtPolicyGroup := s.router.Group(factory.PcfBdtPolicyCtlResUriPrefix) + bdtRouterAuthorizationCheck := util.NewRouterAuthorizationCheck(models.ServiceName_NPCF_BDTPOLICYCONTROL) + bdtPolicyGroup.Use(func(c *gin.Context) { + bdtRouterAuthorizationCheck.Check(c, s.Context()) + }) + applyRoutes(bdtPolicyGroup, bdtPolicyRoutes) + + httpcallbackRoutes := s.getHttpCallBackRoutes() + httpcallbackGroup := s.router.Group(factory.PcfCallbackResUriPrefix) + applyRoutes(httpcallbackGroup, httpcallbackRoutes) + + oamRoutes := s.getOamRoutes() + oamGroup := s.router.Group(factory.PcfOamResUriPrefix) + oamRouterAuthorizationCheck := util.NewRouterAuthorizationCheck(models.ServiceName_NPCF_OAM) + oamGroup.Use(func(c *gin.Context) { + oamRouterAuthorizationCheck.Check(c, s.Context()) + }) + applyRoutes(oamGroup, oamRoutes) + + policyAuthorizationRoutes := s.getPolicyAuthorizationRoutes() + policyAuthorizationGroup := s.router.Group(factory.PcfPolicyAuthResUriPrefix) + policyAuthorizationRouterAuthorizationCheck := util. + NewRouterAuthorizationCheck(models.ServiceName_NPCF_POLICYAUTHORIZATION) + policyAuthorizationGroup.Use(func(c *gin.Context) { + policyAuthorizationRouterAuthorizationCheck.Check(c, s.Context()) + }) + applyRoutes(policyAuthorizationGroup, policyAuthorizationRoutes) + + uePolicyRoutes := s.getUePolicyRoutes() + uePolicyGroup := s.router.Group(factory.PcfUePolicyCtlResUriPrefix) + applyRoutes(uePolicyGroup, uePolicyRoutes) + + cfg := s.Config() + bindAddr := cfg.GetSbiBindingAddr() + logger.SBILog.Infof("Binding addr: [%s]", bindAddr) + var err error + if s.httpServer, err = httpwrapper.NewHttp2Server(bindAddr, tlsKeyLogPath, s.router); err != nil { + logger.InitLog.Errorf("Initialize HTTP server failed: %v", err) + return nil, err + } + s.httpServer.ErrorLog = log.New(logger.SBILog.WriterLevel(logrus.ErrorLevel), "HTTP2: ", 0) + + return s, nil +} + +func (s *Server) Run(traceCtx context.Context, wg *sync.WaitGroup) error { + var err error + _, s.Context().NfId, err = s.Consumer().SendRegisterNFInstance(context.Background()) + if err != nil { + logger.InitLog.Errorf("PCF register to NRF Error[%s]", err.Error()) + } + + wg.Add(1) + go s.startServer(wg) + + return nil +} + +func (s *Server) Shutdown(traceCtx context.Context) { + const defaultShutdownTimeout time.Duration = 2 * time.Second + + if s.httpServer != nil { + logger.SBILog.Infof("Stop SBI server (listen on %s)", s.httpServer.Addr) + toCtx, cancel := context.WithTimeout(context.Background(), defaultShutdownTimeout) + defer cancel() + if err := s.httpServer.Shutdown(toCtx); err != nil { + logger.SBILog.Errorf("Could not close SBI server: %#v", err) + } + } +} + +func (s *Server) startServer(wg *sync.WaitGroup) { + defer func() { + if p := recover(); p != nil { + // Print stack for panic to log. Fatalf() will let program exit. + logger.SBILog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) + s.Terminate() + } + + wg.Done() + }() + + logger.SBILog.Infof("Start SBI server (listen on %s)", s.httpServer.Addr) + + var err error + cfg := s.Config() + scheme := cfg.GetSbiScheme() + if scheme == "http" { + err = s.httpServer.ListenAndServe() + } else if scheme == "https" { + err = s.httpServer.ListenAndServeTLS( + cfg.GetCertPemPath(), + cfg.GetCertKeyPath()) + } else { + err = fmt.Errorf("No support this scheme[%s]", scheme) + } + + if err != nil && err != http.ErrServerClosed { + logger.SBILog.Errorf("SBI server error: %v", err) + } + logger.SBILog.Infof("SBI server (listen on %s) stopped", s.httpServer.Addr) +} diff --git a/internal/sbi/smpolicy/api_default.go b/internal/sbi/smpolicy/api_default.go deleted file mode 100644 index 417068f..0000000 --- a/internal/sbi/smpolicy/api_default.go +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Npcf_SMPolicyControl - * - * Session Management Policy Control Service - * - * API version: 1.0.1 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package smpolicy - -import ( - "net/http" - - "github.com/gin-gonic/gin" - - "github.com/free5gc/openapi" - "github.com/free5gc/openapi/models" - "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/internal/sbi/producer" - "github.com/free5gc/util/httpwrapper" -) - -// SmPoliciesPost - -func HTTPSmPoliciesPost(c *gin.Context) { - var smPolicyContextData models.SmPolicyContextData - // step 1: retrieve http request body - requestBody, err := c.GetRawData() - if err != nil { - problemDetail := models.ProblemDetails{ - Title: "System failure", - Status: http.StatusInternalServerError, - Detail: err.Error(), - Cause: "SYSTEM_FAILURE", - } - logger.SmPolicyLog.Errorf("Get Request Body error: %+v", err) - c.JSON(http.StatusInternalServerError, problemDetail) - return - } - - // step 2: convert requestBody to openapi models - err = openapi.Deserialize(&smPolicyContextData, requestBody, "application/json") - if err != nil { - problemDetail := "[Request Body] " + err.Error() - rsp := models.ProblemDetails{ - Title: "Malformed request syntax", - Status: http.StatusBadRequest, - Detail: problemDetail, - } - logger.SmPolicyLog.Errorln(problemDetail) - c.JSON(http.StatusBadRequest, rsp) - return - } - - req := httpwrapper.NewRequest(c.Request, smPolicyContextData) - rsp := producer.HandleCreateSmPolicyRequest(req) - - // step 5: response - for key, val := range rsp.Header { // header response is optional - c.Header(key, val[0]) - } - responseBody, err := openapi.Serialize(rsp.Body, "application/json") - if err != nil { - logger.SmPolicyLog.Errorln(err) - problemDetails := models.ProblemDetails{ - Status: http.StatusInternalServerError, - Cause: "SYSTEM_FAILURE", - Detail: err.Error(), - } - c.JSON(http.StatusInternalServerError, problemDetails) - } else { - c.Data(rsp.Status, "application/json", responseBody) - } -} - -// SmPoliciesSmPolicyIdDeletePost - -func HTTPSmPoliciesSmPolicyIdDeletePost(c *gin.Context) { - req := httpwrapper.NewRequest(c.Request, nil) - req.Params["smPolicyId"] = c.Params.ByName("smPolicyId") - - rsp := producer.HandleDeleteSmPolicyContextRequest(req) - - responseBody, err := openapi.Serialize(rsp.Body, "application/json") - if err != nil { - logger.SmPolicyLog.Errorln(err) - problemDetails := models.ProblemDetails{ - Status: http.StatusInternalServerError, - Cause: "SYSTEM_FAILURE", - Detail: err.Error(), - } - c.JSON(http.StatusInternalServerError, problemDetails) - } else { - c.Data(rsp.Status, "application/json", responseBody) - } -} - -// SmPoliciesSmPolicyIdGet - -func HTTPSmPoliciesSmPolicyIDGet(c *gin.Context) { - req := httpwrapper.NewRequest(c.Request, nil) - req.Params["smPolicyId"] = c.Params.ByName("smPolicyId") - - rsp := producer.HandleGetSmPolicyContextRequest(req) - - responseBody, err := openapi.Serialize(rsp.Body, "application/json") - if err != nil { - logger.SmPolicyLog.Errorln(err) - problemDetails := models.ProblemDetails{ - Status: http.StatusInternalServerError, - Cause: "SYSTEM_FAILURE", - Detail: err.Error(), - } - c.JSON(http.StatusInternalServerError, problemDetails) - } else { - c.Data(rsp.Status, "application/json", responseBody) - } -} - -// SmPoliciesSmPolicyIdUpdatePost - -func HTTPSmPoliciesSmPolicyIdUpdatePost(c *gin.Context) { - var smPolicyUpdateContextData models.SmPolicyUpdateContextData - // step 1: retrieve http request body - requestBody, err := c.GetRawData() - if err != nil { - problemDetail := models.ProblemDetails{ - Title: "System failure", - Status: http.StatusInternalServerError, - Detail: err.Error(), - Cause: "SYSTEM_FAILURE", - } - logger.SmPolicyLog.Errorf("Get Request Body error: %+v", err) - c.JSON(http.StatusInternalServerError, problemDetail) - return - } - - // step 2: convert requestBody to openapi models - err = openapi.Deserialize(&smPolicyUpdateContextData, requestBody, "application/json") - if err != nil { - problemDetail := "[Request Body] " + err.Error() - rsp := models.ProblemDetails{ - Title: "Malformed request syntax", - Status: http.StatusBadRequest, - Detail: problemDetail, - } - logger.SmPolicyLog.Errorln(problemDetail) - c.JSON(http.StatusBadRequest, rsp) - return - } - - req := httpwrapper.NewRequest(c.Request, smPolicyUpdateContextData) - req.Params["smPolicyId"] = c.Params.ByName("smPolicyId") - - rsp := producer.HandleUpdateSmPolicyContextRequest(req) - - responseBody, err := openapi.Serialize(rsp.Body, "application/json") - if err != nil { - logger.SmPolicyLog.Errorln(err) - problemDetails := models.ProblemDetails{ - Status: http.StatusInternalServerError, - Cause: "SYSTEM_FAILURE", - Detail: err.Error(), - } - c.JSON(http.StatusInternalServerError, problemDetails) - } else { - c.Data(rsp.Status, "application/json", responseBody) - } -} diff --git a/internal/sbi/smpolicy/routers.go b/internal/sbi/smpolicy/routers.go deleted file mode 100644 index 1c05dec..0000000 --- a/internal/sbi/smpolicy/routers.go +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Npcf_SMPolicyControl - * - * Session Management Policy Control Service - * - * API version: 1.0.1 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package smpolicy - -import ( - "net/http" - "strings" - - "github.com/gin-gonic/gin" - - "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/pkg/factory" - logger_util "github.com/free5gc/util/logger" -) - -// Route is the information for every URI. -type Route struct { - // 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 - // HandlerFunc is the handler function of this route. - HandlerFunc gin.HandlerFunc -} - -// Routes is the list of the generated Route. -type Routes []Route - -// NewRouter returns a new router. -func NewRouter() *gin.Engine { - router := logger_util.NewGinWithLogrus(logger.GinLog) - AddService(router) - return router -} - -func AddService(engine *gin.Engine) *gin.RouterGroup { - group := engine.Group(factory.PcfSMpolicyCtlResUriPrefix) - - 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 "DELETE": - group.DELETE(route.Pattern, route.HandlerFunc) - case "PATCH": - group.PATCH(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{ - { - "Index", - "GET", - "/", - Index, - }, - - { - "SmPoliciesPost", - strings.ToUpper("Post"), - "/sm-policies", - HTTPSmPoliciesPost, - }, - - { - "SmPoliciesSmPolicyIdDeletePost", - strings.ToUpper("Post"), - "/sm-policies/:smPolicyId/delete", - HTTPSmPoliciesSmPolicyIdDeletePost, - }, - - { - "SmPoliciesSmPolicyIdGet", - strings.ToUpper("Get"), - "/sm-policies/:smPolicyId", - HTTPSmPoliciesSmPolicyIDGet, - }, - - { - "SmPoliciesSmPolicyIdUpdatePost", - strings.ToUpper("Post"), - "/sm-policies/:smPolicyId/update", - HTTPSmPoliciesSmPolicyIdUpdatePost, - }, -} diff --git a/internal/sbi/uepolicy/api_default.go b/internal/sbi/uepolicy/api_default.go deleted file mode 100644 index a1ba9f1..0000000 --- a/internal/sbi/uepolicy/api_default.go +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Npcf_UEPolicyControl - * - * UE Policy Control Service API - * - * API version: 1.0.0 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package uepolicy - -import ( - "github.com/gin-gonic/gin" -) - -// PoliciesPolAssoIdDelete - -func PoliciesPolAssoIdDelete(c *gin.Context) { -} - -// PoliciesPolAssoIdGet - -func PoliciesPolAssoIdGet(c *gin.Context) { -} - -// PoliciesPolAssoIdUpdatePost - -func PoliciesPolAssoIdUpdatePost(c *gin.Context) { -} - -// PoliciesPost - -func PoliciesPost(c *gin.Context) { -} diff --git a/internal/sbi/uepolicy/routers.go b/internal/sbi/uepolicy/routers.go deleted file mode 100644 index 10644a9..0000000 --- a/internal/sbi/uepolicy/routers.go +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Npcf_UEPolicyControl - * - * UE Policy Control Service API - * - * API version: 1.0.0 - * Generated by: OpenAPI Generator (https://openapi-generator.tech) - */ - -package uepolicy - -import ( - "net/http" - "strings" - - "github.com/gin-gonic/gin" - - "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/pkg/factory" - logger_util "github.com/free5gc/util/logger" -) - -type Route struct { - // 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 - // HandlerFunc is the handler function of this route. - HandlerFunc gin.HandlerFunc -} - -type Routes []Route - -func NewRouter() *gin.Engine { - router := logger_util.NewGinWithLogrus(logger.GinLog) - AddService(router) - return router -} - -func AddService(engine *gin.Engine) *gin.RouterGroup { - group := engine.Group(factory.PcfUePolicyCtlResUriPrefix) - - for _, route := range routes { - switch route.Method { - case "GET": - group.GET(route.Pattern, route.HandlerFunc) - case "POST": - group.POST(route.Pattern, route.HandlerFunc) - case "PATCH": - group.PATCH(route.Pattern, route.HandlerFunc) - } - } - return group -} - -func Index(c *gin.Context) { - c.String(http.StatusOK, "Hello World!") -} - -var routes = Routes{ - { - "Index", - "GET", - "", - Index, - }, - - { - "PoliciesPolAssoIdDelete", - strings.ToUpper("Delete"), - "/policies/{polAssoId}", - PoliciesPolAssoIdDelete, - }, - - { - "PoliciesPolAssoIdGet", - strings.ToUpper("Get"), - "/policies/{polAssoId}", - PoliciesPolAssoIdGet, - }, - - { - "PoliciesPolAssoIdUpdatePost", - strings.ToUpper("Post"), - "/policies/{polAssoId}/update", - PoliciesPolAssoIdUpdatePost, - }, - - { - "PoliciesPost", - strings.ToUpper("Post"), - "/policies", - PoliciesPost, - }, -} diff --git a/pkg/app/app.go b/pkg/app/app.go new file mode 100644 index 0000000..e1b0d62 --- /dev/null +++ b/pkg/app/app.go @@ -0,0 +1,18 @@ +package app + +import ( + pcf_context "github.com/free5gc/pcf/internal/context" + "github.com/free5gc/pcf/pkg/factory" +) + +type App interface { + SetLogEnable(enable bool) + SetLogLevel(level string) + SetReportCaller(reportCaller bool) + + Start() + Terminate() + + Context() *pcf_context.PCFContext + Config() *factory.Config +} diff --git a/pkg/factory/config.go b/pkg/factory/config.go index 433e70c..d4dfab6 100644 --- a/pkg/factory/config.go +++ b/pkg/factory/config.go @@ -6,6 +6,7 @@ package factory import ( "fmt" + "os" "strconv" "sync" @@ -227,6 +228,59 @@ func appendInvalid(err error) error { return error(errs) } +func (c *Config) GetSbiBindingIP() string { + c.RLock() + defer c.RUnlock() + bindIP := "0.0.0.0" + if c.Configuration == nil || c.Configuration.Sbi == nil { + return bindIP + } + if c.Configuration.Sbi.BindingIPv4 != "" { + if bindIP = os.Getenv(c.Configuration.Sbi.BindingIPv4); bindIP != "" { + logger.CfgLog.Infof("Parsing ServerIPv4 [%s] from ENV Variable", bindIP) + } else { + bindIP = c.Configuration.Sbi.BindingIPv4 + } + } + return bindIP +} + +func (c *Config) GetSbiPort() int { + c.RLock() + defer c.RUnlock() + if c.Configuration != nil && c.Configuration.Sbi != nil && c.Configuration.Sbi.Port != 0 { + return c.Configuration.Sbi.Port + } + return PcfSbiDefaultPort +} + +func (c *Config) GetSbiBindingAddr() string { + c.RLock() + defer c.RUnlock() + return c.GetSbiBindingIP() + ":" + strconv.Itoa(c.GetSbiPort()) +} + +func (c *Config) GetSbiScheme() string { + c.RLock() + defer c.RUnlock() + if c.Configuration != nil && c.Configuration.Sbi != nil && c.Configuration.Sbi.Scheme != "" { + return c.Configuration.Sbi.Scheme + } + return PcfSbiDefaultScheme +} + +func (c *Config) GetCertPemPath() string { + c.RLock() + defer c.RUnlock() + return c.Configuration.Sbi.Tls.Pem +} + +func (c *Config) GetCertKeyPath() string { + c.RLock() + defer c.RUnlock() + return c.Configuration.Sbi.Tls.Key +} + func (c *Config) GetVersion() string { c.RLock() defer c.RUnlock() diff --git a/pkg/service/init.go b/pkg/service/init.go index 07c4d03..fefcb25 100644 --- a/pkg/service/init.go +++ b/pkg/service/init.go @@ -1,51 +1,99 @@ package service import ( - "fmt" + "context" "io" "os" - "os/signal" "runtime/debug" - "syscall" + "sync" - "github.com/antihax/optional" - "github.com/gin-contrib/cors" "github.com/sirupsen/logrus" - "github.com/free5gc/openapi/Nnrf_NFDiscovery" - "github.com/free5gc/openapi/models" pcf_context "github.com/free5gc/pcf/internal/context" "github.com/free5gc/pcf/internal/logger" - "github.com/free5gc/pcf/internal/sbi/ampolicy" - "github.com/free5gc/pcf/internal/sbi/bdtpolicy" + "github.com/free5gc/pcf/internal/sbi" "github.com/free5gc/pcf/internal/sbi/consumer" - "github.com/free5gc/pcf/internal/sbi/httpcallback" - "github.com/free5gc/pcf/internal/sbi/oam" - "github.com/free5gc/pcf/internal/sbi/policyauthorization" - "github.com/free5gc/pcf/internal/sbi/smpolicy" - "github.com/free5gc/pcf/internal/sbi/uepolicy" - "github.com/free5gc/pcf/internal/util" + "github.com/free5gc/pcf/internal/sbi/processor" + "github.com/free5gc/pcf/pkg/app" "github.com/free5gc/pcf/pkg/factory" - "github.com/free5gc/util/httpwrapper" - logger_util "github.com/free5gc/util/logger" ) +var PCF *PcfApp + +var _ app.App = &PcfApp{} + type PcfApp struct { + app.App cfg *factory.Config pcfCtx *pcf_context.PCFContext + ctx context.Context + cancel context.CancelFunc + + consumer *consumer.Consumer + processor *processor.Processor + sbiServer *sbi.Server + wg sync.WaitGroup } -func NewApp(cfg *factory.Config) (*PcfApp, error) { - pcf := &PcfApp{cfg: cfg} +func NewApp( + ctx context.Context, + cfg *factory.Config, + tlsKeyLogPath string, +) (*PcfApp, error) { + pcf := &PcfApp{ + cfg: cfg, + wg: sync.WaitGroup{}, + } pcf.SetLogEnable(cfg.GetLogEnable()) pcf.SetLogLevel(cfg.GetLogLevel()) pcf.SetReportCaller(cfg.GetLogReportCaller()) + pcf.ctx, pcf.cancel = context.WithCancel(ctx) pcf_context.Init() pcf.pcfCtx = pcf_context.GetSelf() + + // consumer + consumer, err := consumer.NewConsumer(pcf) + if err != nil { + return pcf, err + } + pcf.consumer = consumer + + // processor + p, err := processor.NewProcessor(pcf, consumer) + if err != nil { + return pcf, err + } + pcf.processor = p + + if pcf.sbiServer, err = sbi.NewServer(pcf, tlsKeyLogPath); err != nil { + return nil, err + } + PCF = pcf + return pcf, nil } +func (a *PcfApp) Config() *factory.Config { + return a.cfg +} + +func (a *PcfApp) Context() *pcf_context.PCFContext { + return a.pcfCtx +} + +func (a *PcfApp) CancelContext() context.Context { + return a.ctx +} + +func (a *PcfApp) Consumer() *consumer.Consumer { + return a.consumer +} + +func (a *PcfApp) Processor() *processor.Processor { + return a.processor +} + func (a *PcfApp) SetLogEnable(enable bool) { logger.MainLog.Infof("Log enable is set to [%v]", enable) if enable && logger.Log.Out == os.Stderr { @@ -88,108 +136,44 @@ func (a *PcfApp) SetReportCaller(reportCaller bool) { logger.Log.SetReportCaller(reportCaller) } -func (a *PcfApp) Start(tlsKeyLogPath string) { +func (a *PcfApp) Start() { logger.InitLog.Infoln("Server started") - router := logger_util.NewGinWithLogrus(logger.GinLog) - - bdtpolicy.AddService(router) - smpolicy.AddService(router) - ampolicy.AddService(router) - uepolicy.AddService(router) - policyauthorization.AddService(router) - httpcallback.AddService(router) - oam.AddService(router) - - router.Use(cors.New(cors.Config{ - AllowMethods: []string{"GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE"}, - AllowHeaders: []string{ - "Origin", "Content-Length", "Content-Type", "User-Agent", - "Referrer", "Host", "Token", "X-Requested-With", - }, - ExposeHeaders: []string{"Content-Length"}, - AllowCredentials: true, - AllowAllOrigins: true, - MaxAge: 86400, - })) - - pemPath := factory.PcfDefaultCertPemPath - keyPath := factory.PcfDefaultPrivateKeyPath - sbi := factory.PcfConfig.Configuration.Sbi - if sbi.Tls != nil { - pemPath = sbi.Tls.Pem - keyPath = sbi.Tls.Key - } - - self := a.pcfCtx - pcf_context.InitpcfContext(self) - - addr := fmt.Sprintf("%s:%d", self.BindingIPv4, self.SBIPort) - - profile, err := consumer.BuildNFInstance(self) - if err != nil { - logger.InitLog.Error("Build PCF Profile Error") - } - _, self.NfId, err = consumer.SendRegisterNFInstance(self.NrfUri, self.NfId, profile) - if err != nil { - logger.InitLog.Errorf("PCF register to NRF Error[%s]", err.Error()) + a.wg.Add(1) + go a.listenShutdownEvent() + if err := a.sbiServer.Run(context.Background(), &a.wg); err != nil { + logger.InitLog.Fatalf("Run SBI server failed: %+v", err) } + a.WaitRoutineStopped() +} - param := Nnrf_NFDiscovery.SearchNFInstancesParamOpts{ - ServiceNames: optional.NewInterface([]models.ServiceName{models.ServiceName_NUDR_DR}), - } - resp, err := consumer.SendSearchNFInstances(self.NrfUri, models.NfType_UDR, models.NfType_PCF, param) - for _, nfProfile := range resp.NfInstances { - udruri := util.SearchNFServiceUri(nfProfile, models.ServiceName_NUDR_DR, models.NfServiceStatus_REGISTERED) - if udruri != "" { - self.SetDefaultUdrURI(udruri) - break +func (a *PcfApp) listenShutdownEvent() { + defer func() { + if p := recover(); p != nil { + // Print stack for panic to log. Fatalf() will let program exit. + logger.InitLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) } - } - if err != nil { - logger.InitLog.Errorln(err) - } - - signalChannel := make(chan os.Signal, 1) - signal.Notify(signalChannel, os.Interrupt, syscall.SIGTERM) - go func() { - defer func() { - if p := recover(); p != nil { - // Print stack for panic to log. Fatalf() will let program exit. - logger.InitLog.Fatalf("panic: %v\n%s", p, string(debug.Stack())) - } - }() - - <-signalChannel - a.Terminate() - os.Exit(0) + a.wg.Done() }() - server, err := httpwrapper.NewHttp2Server(addr, tlsKeyLogPath, router) - if server == nil { - logger.InitLog.Errorf("Initialize HTTP server failed: %+v", err) - return - } - - if err != nil { - logger.InitLog.Warnf("Initialize HTTP server: +%v", err) - } - - serverScheme := factory.PcfConfig.Configuration.Sbi.Scheme - if serverScheme == "http" { - err = server.ListenAndServe() - } else if serverScheme == "https" { - err = server.ListenAndServeTLS(pemPath, keyPath) - } + <-a.ctx.Done() + a.terminateProcedure() +} - if err != nil { - logger.InitLog.Fatalf("HTTP server setup failed: %+v", err) +func (a *PcfApp) CallServerStop() { + if a.sbiServer != nil { + a.sbiServer.Shutdown(context.Background()) } } func (a *PcfApp) Terminate() { - logger.InitLog.Infof("Terminating PCF...") + a.cancel() +} + +func (a *PcfApp) terminateProcedure() { + logger.MainLog.Infof("Terminating PCF...") + a.CallServerStop() // deregister with NRF - problemDetails, err := consumer.SendDeregisterNFInstance() + problemDetails, err := a.Consumer().SendDeregisterNFInstance() if problemDetails != nil { logger.InitLog.Errorf("Deregister NF instance Failed Problem[%+v]", problemDetails) } else if err != nil { @@ -199,3 +183,8 @@ func (a *PcfApp) Terminate() { } logger.InitLog.Infof("PCF terminated") } + +func (a *PcfApp) WaitRoutineStopped() { + a.wg.Wait() + logger.MainLog.Infof("PCF App is terminated") +}