diff --git a/internal/sbi/consumer/consumer.go b/internal/sbi/consumer/consumer.go new file mode 100644 index 0000000..da52db1 --- /dev/null +++ b/internal/sbi/consumer/consumer.go @@ -0,0 +1,31 @@ +package consumer + +import ( + "github.com/free5gc/ausf/pkg/app" + + "github.com/free5gc/openapi/Nnrf_NFDiscovery" + "github.com/free5gc/openapi/Nnrf_NFManagement" +) + +type ConsumerAusf interface { + app.App +} + +type Consumer struct { + ConsumerAusf + + *nnrfService +} + +func NewConsumer(ausf ConsumerAusf) (*Consumer, error) { + c := &Consumer{ + ConsumerAusf: ausf, + } + + c.nnrfService = &nnrfService{ + consumer: c, + nfMngmntClients: make(map[string]*Nnrf_NFManagement.APIClient), + nfDiscClients: make(map[string]*Nnrf_NFDiscovery.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 3c97013..0000000 --- a/internal/sbi/consumer/nf_discovery.go +++ /dev/null @@ -1,40 +0,0 @@ -package consumer - -import ( - "fmt" - "net/http" - - ausf_context "github.com/free5gc/ausf/internal/context" - "github.com/free5gc/ausf/internal/logger" - "github.com/free5gc/openapi/Nnrf_NFDiscovery" - "github.com/free5gc/openapi/models" -) - -func SendSearchNFInstances(nrfUri string, targetNfType, requestNfType models.NfType, - param Nnrf_NFDiscovery.SearchNFInstancesParamOpts, -) (*models.SearchResult, error) { - ctx, _, err := ausf_context.GetSelf().GetTokenCtx(models.ServiceName_NNRF_DISC, models.NfType_NRF) - if err != nil { - return nil, err - } - - configuration := Nnrf_NFDiscovery.NewConfiguration() - configuration.SetBasePath(nrfUri) - client := Nnrf_NFDiscovery.NewAPIClient(configuration) - - result, rsp, rspErr := client.NFInstancesStoreApi.SearchNFInstances(ctx, - targetNfType, requestNfType, ¶m) - - if rspErr != nil { - return nil, fmt.Errorf("NFInstancesStoreApi Response error: %+w", rspErr) - } - defer func() { - if rspCloseErr := rsp.Body.Close(); rspCloseErr != nil { - logger.ConsumerLog.Errorf("NFInstancesStoreApi Response cannot close: %v", rspCloseErr) - } - }() - if rsp != nil && rsp.StatusCode == http.StatusTemporaryRedirect { - return nil, fmt.Errorf("Temporary Redirect For Non NRF Consumer") - } - return &result, nil -} diff --git a/internal/sbi/consumer/nf_management.go b/internal/sbi/consumer/nf_management.go deleted file mode 100644 index 816ae73..0000000 --- a/internal/sbi/consumer/nf_management.go +++ /dev/null @@ -1,125 +0,0 @@ -package consumer - -import ( - "fmt" - "net/http" - "strings" - "time" - - ausf_context "github.com/free5gc/ausf/internal/context" - "github.com/free5gc/ausf/internal/logger" - "github.com/free5gc/openapi" - "github.com/free5gc/openapi/Nnrf_NFManagement" - "github.com/free5gc/openapi/models" -) - -func BuildNFInstance(ausfContext *ausf_context.AUSFContext) (profile models.NfProfile, err error) { - profile.NfInstanceId = ausfContext.NfId - profile.NfType = models.NfType_AUSF - profile.NfStatus = models.NfStatus_REGISTERED - profile.Ipv4Addresses = append(profile.Ipv4Addresses, ausfContext.RegisterIPv4) - services := []models.NfService{} - for _, nfService := range ausfContext.NfService { - services = append(services, nfService) - } - if len(services) > 0 { - profile.NfServices = &services - } - var ausfInfo models.AusfInfo - ausfInfo.GroupId = ausfContext.GroupID - profile.AusfInfo = &ausfInfo - profile.PlmnList = &ausfContext.PlmnList - return -} - -// func SendRegisterNFInstance(nrfUri, nfInstanceId string, profile models.NfProfile, -// ) (resouceNrfUri string,retrieveNfInstanceID string, err error) { -func SendRegisterNFInstance(nrfUri, nfInstanceId string, profile models.NfProfile) (string, string, error) { - configuration := Nnrf_NFManagement.NewConfiguration() - configuration.SetBasePath(nrfUri) - client := Nnrf_NFManagement.NewAPIClient(configuration) - - ctx, _, err := ausf_context.GetSelf().GetTokenCtx(models.ServiceName_NNRF_NFM, models.NfType_NRF) - if err != nil { - return "", "", err - } - - var res *http.Response - for { - nf, resTmp, err := client.NFInstanceIDDocumentApi.RegisterNFInstance(ctx, nfInstanceId, profile) - if err != nil || resTmp == nil { - logger.ConsumerLog.Errorf("AUSF register to NRF Error[%v]", err) - time.Sleep(2 * time.Second) - continue - } else { - res = resTmp - } - defer func() { - if resCloseErr := res.Body.Close(); resCloseErr != nil { - logger.ConsumerLog.Errorf("AUSF NFInstanceIDDocumentApi 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") - resourceNrfUri := 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) - } - } - ausf_context.GetSelf().OAuth2Required = oauth2 - if oauth2 && ausf_context.GetSelf().NrfCertPem == "" { - logger.CfgLog.Error("OAuth2 enable but no nrfCertPem provided in config.") - } - - return resourceNrfUri, retrieveNfInstanceID, nil - } else { - fmt.Println(fmt.Errorf("handler returned wrong status code %d", status)) - fmt.Println(fmt.Errorf("NRF return wrong status code %d", status)) - } - } - return "", "", nil -} - -func SendDeregisterNFInstance() (*models.ProblemDetails, error) { - logger.ConsumerLog.Infof("Send Deregister NFInstance") - - ctx, pd, err := ausf_context.GetSelf().GetTokenCtx(models.ServiceName_NNRF_NFM, models.NfType_NRF) - if err != nil { - return pd, err - } - - ausfSelf := ausf_context.GetSelf() - // Set client and set url - configuration := Nnrf_NFManagement.NewConfiguration() - configuration.SetBasePath(ausfSelf.NrfUri) - client := Nnrf_NFManagement.NewAPIClient(configuration) - - res, err := client.NFInstanceIDDocumentApi.DeregisterNFInstance(ctx, ausfSelf.NfId) - if err == nil { - return nil, err - } else if res != nil { - defer func() { - if resCloseErr := res.Body.Close(); resCloseErr != nil { - logger.ConsumerLog.Errorf("NFInstanceIDDocumentApi response body cannot close: %+v", resCloseErr) - } - }() - if res.Status != err.Error() { - return nil, err - } - problem := err.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails) - return &problem, err - } else { - return nil, openapi.ReportError("server no response") - } -} diff --git a/internal/sbi/consumer/nrf_service.go b/internal/sbi/consumer/nrf_service.go new file mode 100644 index 0000000..637b93f --- /dev/null +++ b/internal/sbi/consumer/nrf_service.go @@ -0,0 +1,218 @@ +package consumer + +import ( + "context" + "fmt" + "net/http" + "strings" + "sync" + "time" + + // "github.com/free5gc/openapi/nrf/NFManagement" // R17 + ausf_context "github.com/free5gc/ausf/internal/context" + "github.com/free5gc/ausf/internal/logger" + "github.com/free5gc/openapi" + "github.com/free5gc/openapi/Nnrf_NFDiscovery" + "github.com/free5gc/openapi/Nnrf_NFManagement" + "github.com/free5gc/openapi/models" + "github.com/pkg/errors" +) + +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 + ausfContext := s.consumer.Context() + + client := s.getNFDiscClient(ausfContext.NrfUri) + + ctx, _, err := ausf_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 (s *nnrfService) SendDeregisterNFInstance() (problemDetails *models.ProblemDetails, err error) { + logger.ConsumerLog.Infof("Send Deregister NFInstance") + + ctx, pd, err := ausf_context.GetSelf().GetTokenCtx(models.ServiceName_NNRF_NFM, models.NfType_NRF) + if err != nil { + return pd, err + } + + ausfContext := s.consumer.Context() + client := s.getNFManagementClient(ausfContext.NrfUri) + + var res *http.Response + + res, err = client.NFInstanceIDDocumentApi.DeregisterNFInstance(ctx, ausfContext.NfId) + if err == nil { + return problemDetails, err + } 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 problemDetails, err + } + problem := err.(openapi.GenericOpenAPIError).Model().(models.ProblemDetails) + problemDetails = &problem + } else { + err = openapi.ReportError("server no response") + } + return problemDetails, err +} + +func (s *nnrfService) RegisterNFInstance(ctx context.Context) ( + resouceNrfUri string, retrieveNfInstanceID string, err error) { + ausfContext := s.consumer.Context() + + client := s.getNFManagementClient(ausfContext.NrfUri) + nfProfile, err := s.buildNfProfile(ausfContext) + if err != nil { + return "", "", errors.Wrap(err, "RegisterNFInstance buildNfProfile()") + } + + var nf models.NfProfile + var res *http.Response + for { + nf, res, err = client.NFInstanceIDDocumentApi.RegisterNFInstance(context.TODO(), ausfContext.NfId, nfProfile) + if err != nil || res == nil { + logger.ConsumerLog.Errorf("AUSF 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) + } + } + ausf_context.GetSelf().OAuth2Required = oauth2 + if oauth2 && ausf_context.GetSelf().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) buildNfProfile(ausfContext *ausf_context.AUSFContext) (profile models.NfProfile, err error) { + profile.NfInstanceId = ausfContext.NfId + profile.NfType = models.NfType_AUSF + profile.NfStatus = models.NfStatus_REGISTERED + profile.Ipv4Addresses = append(profile.Ipv4Addresses, ausfContext.RegisterIPv4) + services := []models.NfService{} + for _, nfService := range ausfContext.NfService { + services = append(services, nfService) + } + if len(services) > 0 { + profile.NfServices = &services + } + profile.AusfInfo = &models.AusfInfo{ + // Todo + // 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}$", + // }, + // }, + } + return +}