diff --git a/backend/nfconfig/config.go b/backend/nfconfig/config.go index 4f1cafb8..85169ca4 100644 --- a/backend/nfconfig/config.go +++ b/backend/nfconfig/config.go @@ -244,13 +244,15 @@ func extractIpDomains(groupNames []string, deviceGroupMap map[string]configmodel logger.NfConfigLog.Warnf("Device group %s not found", name) continue } - ip := nfConfigApi.NewIpDomain( - dg.IpDomainExpanded.Dnn, - dg.IpDomainExpanded.DnsPrimary, - dg.IpDomainExpanded.UeIpPool, - dg.IpDomainExpanded.Mtu, - ) - ipDomains = append(ipDomains, *ip) + for _, ipDomainExp := range dg.IpDomainExpanded { + ip := nfConfigApi.NewIpDomain( + ipDomainExp.Dnn, + ipDomainExp.DnsPrimary, + ipDomainExp.UeIpPool, // Now accessing the correct field from the slice element + ipDomainExp.Mtu, + ) + ipDomains = append(ipDomains, *ip) + } } return ipDomains } diff --git a/backend/nfconfig/config_session_management_test.go b/backend/nfconfig/config_session_management_test.go index 1ae066cb..5f901768 100644 --- a/backend/nfconfig/config_session_management_test.go +++ b/backend/nfconfig/config_session_management_test.go @@ -78,11 +78,13 @@ type deviceGroupParams struct { func makeDeviceGroup(p deviceGroupParams) (string, configmodels.DeviceGroups) { return p.name, configmodels.DeviceGroups{ - IpDomainExpanded: configmodels.DeviceGroupsIpDomainExpanded{ - Dnn: p.dnn, - DnsPrimary: p.dnsPrimary, - UeIpPool: p.ueIpPool, - Mtu: p.mtu, + IpDomainExpanded: []configmodels.DeviceGroupsIpDomainExpanded{ + { + Dnn: p.dnn, + DnsPrimary: p.dnsPrimary, + UeIpPool: p.ueIpPool, + Mtu: p.mtu, + }, }, } } diff --git a/configapi/api/configapi.yaml b/configapi/api/configapi.yaml index f5196df3..1902c395 100644 --- a/configapi/api/configapi.yaml +++ b/configapi/api/configapi.yaml @@ -124,7 +124,7 @@ components: ip-domain-name: example: pool1 type: string - ip-domain-expanded: + ip-domains: $ref: '#/components/schemas/device_groups_ip_domain_expanded' type: object slice: diff --git a/configapi/api_default_test.go b/configapi/api_default_test.go index ce652eeb..36072515 100644 --- a/configapi/api_default_test.go +++ b/configapi/api_default_test.go @@ -163,33 +163,56 @@ func (m *MockMongoClientFoundNetworkSlice) RestfulAPIGetOne(coll string, filter } func deviceGroup(name string) configmodels.DeviceGroups { - traffic_class := configmodels.TrafficClassInfo{ + traffic_class1 := configmodels.TrafficClassInfo{ Name: "platinum", Qci: 8, Arp: 6, Pdb: 300, Pelr: 6, } - qos := configmodels.DeviceGroupsIpDomainExpandedUeDnnQos{ + qos1 := configmodels.DeviceGroupsIpDomainExpandedUeDnnQos{ DnnMbrUplink: 10000000, DnnMbrDownlink: 10000000, BitrateUnit: "kbps", - TrafficClass: &traffic_class, + TrafficClass: &traffic_class1, } - ipdomain := configmodels.DeviceGroupsIpDomainExpanded{ + ipdomain1 := configmodels.DeviceGroupsIpDomainExpanded{ Dnn: "internet", UeIpPool: "172.250.1.0/16", DnsPrimary: "1.1.1.1", DnsSecondary: "8.8.8.8", Mtu: 1460, - UeDnnQos: &qos, + UeDnnQos: &qos1, + } + + // Define the second traffic class and QoS + traffic_class2 := configmodels.TrafficClassInfo{ + Name: "gold", + Qci: 7, + Arp: 5, + Pdb: 150, + Pelr: 5, + } + qos2 := configmodels.DeviceGroupsIpDomainExpandedUeDnnQos{ + DnnMbrUplink: 5000000, + DnnMbrDownlink: 5000000, + BitrateUnit: "kbps", + TrafficClass: &traffic_class2, + } + ipdomain2 := configmodels.DeviceGroupsIpDomainExpanded{ + Dnn: "ims", + UeIpPool: "172.248.0.0/16", + DnsPrimary: "4.4.4.4", + DnsSecondary: "8.8.4.4", + Mtu: 1400, + UeDnnQos: &qos2, } deviceGroup := configmodels.DeviceGroups{ DeviceGroupName: name, Imsis: []string{"1234", "5678"}, SiteInfo: "demo", IpDomainName: "pool1", - IpDomainExpanded: ipdomain, + IpDomainExpanded: []configmodels.DeviceGroupsIpDomainExpanded{ipdomain1, ipdomain2}, } return deviceGroup } @@ -297,7 +320,7 @@ func TestGetDeviceGroupByNameDoesExists(t *testing.T) { t.Fatalf("failed to read response body: %v", err) } body := string(body_bytes) - expected := `{"group-name":"group1","imsis":["1234","5678"],"site-info":"demo","ip-domain-name":"pool1","ip-domain-expanded":{"dnn":"internet","ue-ip-pool":"172.250.1.0/16","dns-primary":"1.1.1.1","dns-secondary":"8.8.8.8","mtu":1460,"ue-dnn-qos":{"dnn-mbr-uplink":10000000,"dnn-mbr-downlink":10000000,"bitrate-unit":"kbps","traffic-class":{"name":"platinum","qci":8,"arp":6,"pdb":300,"pelr":6}}}}` + expected := "{\"group-name\":\"group1\",\"imsis\":[\"1234\",\"5678\"],\"site-info\":\"demo\",\"ip-domain-name\":\"pool1\",\"ip-domains\":[{\"dnn\":\"internet\",\"ue-ip-pool\":\"172.250.1.0/16\",\"dns-primary\":\"1.1.1.1\",\"dns-secondary\":\"8.8.8.8\",\"mtu\":1460,\"ue-dnn-qos\":{\"dnn-mbr-uplink\":10000000,\"dnn-mbr-downlink\":10000000,\"bitrate-unit\":\"kbps\",\"traffic-class\":{\"name\":\"platinum\",\"qci\":8,\"arp\":6,\"pdb\":300,\"pelr\":6}}},{\"dnn\":\"ims\",\"ue-ip-pool\":\"172.248.0.0/16\",\"dns-primary\":\"4.4.4.4\",\"dns-secondary\":\"8.8.4.4\",\"mtu\":1400,\"ue-dnn-qos\":{\"dnn-mbr-uplink\":5000000,\"dnn-mbr-downlink\":5000000,\"bitrate-unit\":\"kbps\",\"traffic-class\":{\"name\":\"gold\",\"qci\":7,\"arp\":5,\"pdb\":150,\"pelr\":5}}}]}" if body != expected { t.Errorf("Expected %v, got %v", expected, body) } diff --git a/configapi/api_subscriber_config_test.go b/configapi/api_subscriber_config_test.go index dc94f9a5..7f38ed96 100644 --- a/configapi/api_subscriber_config_test.go +++ b/configapi/api_subscriber_config_test.go @@ -974,33 +974,56 @@ func TestSubscriberDeleteSuccessWithDeviceGroup(t *testing.T) { } func deviceGroupWithImsis(name string, imsis []string) configmodels.DeviceGroups { - trafficClass := configmodels.TrafficClassInfo{ + traffic_class1 := configmodels.TrafficClassInfo{ Name: "platinum", Qci: 8, Arp: 6, Pdb: 300, Pelr: 6, } - qos := configmodels.DeviceGroupsIpDomainExpandedUeDnnQos{ + qos1 := configmodels.DeviceGroupsIpDomainExpandedUeDnnQos{ DnnMbrUplink: 10000000, DnnMbrDownlink: 10000000, BitrateUnit: "kbps", - TrafficClass: &trafficClass, + TrafficClass: &traffic_class1, } - ipDomain := configmodels.DeviceGroupsIpDomainExpanded{ + ipdomain1 := configmodels.DeviceGroupsIpDomainExpanded{ Dnn: "internet", UeIpPool: "172.250.1.0/16", DnsPrimary: "1.1.1.1", DnsSecondary: "8.8.8.8", Mtu: 1460, - UeDnnQos: &qos, + UeDnnQos: &qos1, + } + + // Define the second traffic class and QoS + traffic_class2 := configmodels.TrafficClassInfo{ + Name: "gold", + Qci: 7, + Arp: 5, + Pdb: 150, + Pelr: 5, + } + qos2 := configmodels.DeviceGroupsIpDomainExpandedUeDnnQos{ + DnnMbrUplink: 5000000, + DnnMbrDownlink: 5000000, + BitrateUnit: "kbps", + TrafficClass: &traffic_class2, + } + ipdomain2 := configmodels.DeviceGroupsIpDomainExpanded{ + Dnn: "ims", + UeIpPool: "172.248.0.0/16", + DnsPrimary: "4.4.4.4", + DnsSecondary: "8.8.4.4", + Mtu: 1400, + UeDnnQos: &qos2, } deviceGroup := configmodels.DeviceGroups{ DeviceGroupName: name, Imsis: imsis, SiteInfo: "demo", IpDomainName: "pool1", - IpDomainExpanded: ipDomain, + IpDomainExpanded: []configmodels.DeviceGroupsIpDomainExpanded{ipdomain1, ipdomain2}, } return deviceGroup } diff --git a/configapi/device_group_helpers.go b/configapi/device_group_helpers.go index 3975fb00..14d34e52 100644 --- a/configapi/device_group_helpers.go +++ b/configapi/device_group_helpers.go @@ -89,28 +89,27 @@ func updateDeviceGroupInNetworkSlices(groupName string) error { func deviceGroupPostHelper(requestDeviceGroup configmodels.DeviceGroups, msgOp int, groupName string) (int, error) { logger.ConfigLog.Infof("received device group: %s", groupName) - ipdomain := &requestDeviceGroup.IpDomainExpanded + ipdomains := &requestDeviceGroup.IpDomainExpanded logger.ConfigLog.Infof("imsis.size: %v, Imsis: %s", len(requestDeviceGroup.Imsis), requestDeviceGroup.Imsis) - logger.ConfigLog.Infof("IP Domain Name: %s", requestDeviceGroup.IpDomainName) - logger.ConfigLog.Infof("IP Domain details: %+v", ipdomain) - logger.ConfigLog.Infof("dnn name: %s", ipdomain.Dnn) - logger.ConfigLog.Infof("ue pool: %s", ipdomain.UeIpPool) - logger.ConfigLog.Infof("dns Primary: %s", ipdomain.DnsPrimary) - logger.ConfigLog.Infof("dns Secondary: %s", ipdomain.DnsSecondary) - logger.ConfigLog.Infof("ip mtu: %v", ipdomain.Mtu) - logger.ConfigLog.Infof("device Group Name: %s", groupName) - - if ipdomain.UeDnnQos != nil { - ipdomain.UeDnnQos.DnnMbrDownlink = convertToBps(ipdomain.UeDnnQos.DnnMbrDownlink, ipdomain.UeDnnQos.BitrateUnit) - if ipdomain.UeDnnQos.DnnMbrDownlink < 0 { - ipdomain.UeDnnQos.DnnMbrDownlink = math.MaxInt64 - } - logger.ConfigLog.Infof("MbrDownLink: %v", ipdomain.UeDnnQos.DnnMbrDownlink) - ipdomain.UeDnnQos.DnnMbrUplink = convertToBps(ipdomain.UeDnnQos.DnnMbrUplink, ipdomain.UeDnnQos.BitrateUnit) - if ipdomain.UeDnnQos.DnnMbrUplink < 0 { - ipdomain.UeDnnQos.DnnMbrUplink = math.MaxInt64 + for i, ipdomain := range *ipdomains { + logger.ConfigLog.Infof("IP Domain details [%d]: %+v", i, ipdomain) + logger.ConfigLog.Infof("DNN Name : %v", ipdomain.Dnn) + logger.ConfigLog.Infof("UE Pool : %v", ipdomain.UeIpPool) + logger.ConfigLog.Infof("DNS Primary : %v", ipdomain.DnsPrimary) + logger.ConfigLog.Infof("DNS Secondary : %v", ipdomain.DnsSecondary) + logger.ConfigLog.Infof("IP MTU : %v", ipdomain.Mtu) + if ipdomain.UeDnnQos != nil { + ipdomain.UeDnnQos.DnnMbrDownlink = convertToBps(ipdomain.UeDnnQos.DnnMbrDownlink, ipdomain.UeDnnQos.BitrateUnit) + if ipdomain.UeDnnQos.DnnMbrDownlink < 0 { + ipdomain.UeDnnQos.DnnMbrDownlink = math.MaxInt64 + } + logger.ConfigLog.Infof("MBR DownLink : %v", ipdomain.UeDnnQos.DnnMbrDownlink) + ipdomain.UeDnnQos.DnnMbrUplink = convertToBps(ipdomain.UeDnnQos.DnnMbrUplink, ipdomain.UeDnnQos.BitrateUnit) + if ipdomain.UeDnnQos.DnnMbrUplink < 0 { + ipdomain.UeDnnQos.DnnMbrUplink = math.MaxInt64 + } + logger.ConfigLog.Infof("MBR UpLink : %v", ipdomain.UeDnnQos.DnnMbrUplink) } - logger.ConfigLog.Infof("MbrUpLink: %v", ipdomain.UeDnnQos.DnnMbrUplink) } prevDevGroup := getDeviceGroupByName(groupName) @@ -214,18 +213,25 @@ func syncDeviceGroupSubscriber(devGroup *configmodels.DeviceGroups, prevDevGroup Sst: int32(sVal), } var errorOccured bool + dnnMap := make(map[string][]configmodels.DeviceGroupsIpDomainExpandedUeDnnQos) for _, imsi := range devGroup.Imsis { /* update all current IMSIs */ subscriberAuthData := DatabaseSubscriberAuthenticationData{} if subscriberAuthData.SubscriberAuthenticationDataGet("imsi-"+imsi) != nil { - dnn := devGroup.IpDomainExpanded.Dnn + for _, ipDomain := range devGroup.IpDomainExpanded { + dnn := ipDomain.Dnn + + // Ensure UeDnnQos is not nil before appending + if ipDomain.UeDnnQos != nil { + dnnMap[dnn] = append(dnnMap[dnn], *ipDomain.UeDnnQos) // Directly append the UeDnnQos + } + } err = updatePolicyAndProvisionedData( imsi, slice.SiteInfo.Plmn.Mcc, slice.SiteInfo.Plmn.Mnc, snssai, - dnn, - devGroup.IpDomainExpanded.UeDnnQos, + dnnMap, ) if err != nil { logger.DbLog.Errorf("updatePolicyAndProvisionedData failed for IMSI %s: %+v", imsi, err) diff --git a/configapi/device_group_helpers_test.go b/configapi/device_group_helpers_test.go index 1948a9f6..856da4a2 100644 --- a/configapi/device_group_helpers_test.go +++ b/configapi/device_group_helpers_test.go @@ -63,8 +63,12 @@ func Test_handleDeviceGroupPost(t *testing.T) { deviceGroup("group_no_imsis"), deviceGroup("group_no_traf_class"), deviceGroup("group_no_qos"), } deviceGroups[2].Imsis = []string{} - deviceGroups[3].IpDomainExpanded.UeDnnQos.TrafficClass = nil - deviceGroups[4].IpDomainExpanded.UeDnnQos = nil + if len(deviceGroups[3].IpDomainExpanded) > 0 { + deviceGroups[3].IpDomainExpanded[0].UeDnnQos.TrafficClass = nil + } + if len(deviceGroups[4].IpDomainExpanded) > 0 { + deviceGroups[4].IpDomainExpanded[0].UeDnnQos = nil + } factory.WebUIConfig.Configuration.Mode5G = true for _, testGroup := range deviceGroups { @@ -157,9 +161,12 @@ func Test_handleDeviceGroupPost_alreadyExists(t *testing.T) { deviceGroup("group_no_qos"), } deviceGroups[2].Imsis = []string{} - deviceGroups[3].IpDomainExpanded.UeDnnQos.TrafficClass = nil - deviceGroups[4].IpDomainExpanded.UeDnnQos = nil - + if len(deviceGroups[3].IpDomainExpanded) > 0 { + deviceGroups[3].IpDomainExpanded[0].UeDnnQos.TrafficClass = nil + } + if len(deviceGroups[4].IpDomainExpanded) > 0 { + deviceGroups[4].IpDomainExpanded[0].UeDnnQos = nil + } factory.WebUIConfig.Configuration.Mode5G = true for _, testGroup := range deviceGroups { @@ -248,25 +255,27 @@ const DEVICE_GROUP_CONFIG = `{ "imsis": [ "string" ], - "ip-domain-expanded": { - "dnn": "string", - "dns-primary": "string", - "dns-secondary": "string", - "mtu": 0, - "ue-dnn-qos": { - "bitrate-unit": "string", - "dnn-mbr-downlink": 0, - "dnn-mbr-uplink": 0, - "traffic-class": { - "arp": 0, - "name": "string", - "pdb": 0, - "pelr": 0, - "qci": 0 - } - }, - "ue-ip-pool": "string" - }, + "ip-domains": [ + { + "dnn": "string", + "dns-primary": "string", + "dns-secondary": "string", + "mtu": 0, + "ue-dnn-qos": { + "bitrate-unit": "string", + "dnn-mbr-downlink": 0, + "dnn-mbr-uplink": 0, + "traffic-class": { + "arp": 0, + "name": "string", + "pdb": 0, + "pelr": 0, + "qci": 0 + } + }, + "ue-ip-pool": "string" + } + ], "ip-domain-name": "string", "site-info": "string" }` diff --git a/configapi/slice_helpers.go b/configapi/slice_helpers.go index 0a60a591..cf1518b2 100644 --- a/configapi/slice_helpers.go +++ b/configapi/slice_helpers.go @@ -217,6 +217,8 @@ var syncSubscribersOnSliceCreateOrUpdate = func(slice configmodels.Slice, prevSl Sd: slice.SliceId.Sd, Sst: int32(sVal), } + mcc := slice.SiteInfo.Plmn.Mcc + mnc := slice.SiteInfo.Plmn.Mnc for _, dgName := range slice.SiteDeviceGroup { logger.ConfigLog.Debugf("dgName: %s", dgName) devGroupConfig := getDeviceGroupByName(dgName) @@ -224,23 +226,13 @@ var syncSubscribersOnSliceCreateOrUpdate = func(slice configmodels.Slice, prevSl logger.ConfigLog.Warnf("Device group not found: %s", dgName) continue } - - for _, imsi := range devGroupConfig.Imsis { - subscriberAuthData := DatabaseSubscriberAuthenticationData{} - if subscriberAuthData.SubscriberAuthenticationDataGet("imsi-"+imsi) != nil { - err := updatePolicyAndProvisionedData( - imsi, - slice.SiteInfo.Plmn.Mcc, - slice.SiteInfo.Plmn.Mnc, - snssai, - devGroupConfig.IpDomainExpanded.Dnn, - devGroupConfig.IpDomainExpanded.UeDnnQos, - ) - if err != nil { - logger.DbLog.Errorf("updatePolicyAndProvisionedData failed for IMSI %s: %+v", imsi, err) - return http.StatusInternalServerError, err - } - } + if len(devGroupConfig.IpDomainExpanded) == 0 { + logger.ConfigLog.Warnln("IPDomainExpanded is nil or empty for dgName:", dgName) + continue + } + _, err := processDeviceGroup(devGroupConfig, snssai, mcc, mnc) + if err != nil { + return http.StatusInternalServerError, err } } if err := cleanupDeviceGroups(slice, prevSlice); err != nil { @@ -249,6 +241,36 @@ var syncSubscribersOnSliceCreateOrUpdate = func(slice configmodels.Slice, prevSl return http.StatusOK, nil } +func processDeviceGroup(devGroupConfig *configmodels.DeviceGroups, snssai *models.Snssai, mcc, mnc string) (int, error) { + dnnMap := make(map[string][]configmodels.DeviceGroupsIpDomainExpandedUeDnnQos) // Stores multiple DNNs & their QoS per IMSI + for _, imsi := range devGroupConfig.Imsis { + subscriberAuthData := DatabaseSubscriberAuthenticationData{} + if subscriberAuthData.SubscriberAuthenticationDataGet("imsi-"+imsi) != nil { + // Process each IP domain for this IMSI + for _, ipDomain := range devGroupConfig.IpDomainExpanded { + dnn := ipDomain.Dnn + + // Ensure UeDnnQos is not nil before appending + if ipDomain.UeDnnQos != nil { + dnnMap[dnn] = append(dnnMap[dnn], *ipDomain.UeDnnQos) // Directly append the UeDnnQos + } + } + err := updatePolicyAndProvisionedData( + imsi, + mcc, + mnc, + snssai, + dnnMap, + ) + if err != nil { + logger.DbLog.Errorf("updatePolicyAndProvisionedData failed for IMSI %s: %+v", imsi, err) + return http.StatusInternalServerError, err + } + } + } + return http.StatusOK, nil +} + func cleanupDeviceGroups(slice, prevSlice configmodels.Slice) error { dgnames := getDeletedDeviceGroupsList(slice, prevSlice) for _, dgName := range dgnames { @@ -270,24 +292,24 @@ func cleanupDeviceGroups(slice, prevSlice configmodels.Slice) error { return nil } -func updatePolicyAndProvisionedData(imsi string, mcc string, mnc string, snssai *models.Snssai, dnn string, qos *configmodels.DeviceGroupsIpDomainExpandedUeDnnQos) error { +func updatePolicyAndProvisionedData(imsi string, mcc string, mnc string, snssai *models.Snssai, dnnMap map[string][]configmodels.DeviceGroupsIpDomainExpandedUeDnnQos) error { err := updateAmPolicyData(imsi) if err != nil { return fmt.Errorf("updateAmPolicyData failed: %w", err) } - err = updateSmPolicyData(snssai, dnn, imsi) + err = updateSmPolicyData(snssai, dnnMap, imsi) if err != nil { return fmt.Errorf("updateSmPolicyData failed: %w", err) } - err = updateAmProvisionedData(snssai, qos, mcc, mnc, imsi) + err = updateAmProvisionedData(snssai, dnnMap, mcc, mnc, imsi) if err != nil { return fmt.Errorf("updateAmProvisionedData failed: %w", err) } - err = updateSmProvisionedData(snssai, qos, mcc, mnc, dnn, imsi) + err = updateSmProvisionedData(snssai, dnnMap, mcc, mnc, imsi) if err != nil { return fmt.Errorf("updateSmProvisionedData failed: %w", err) } - err = updateSmfSelectionProvisionedData(snssai, mcc, mnc, dnn, imsi) + err = updateSmfSelectionProvisionedData(snssai, mcc, mnc, dnnMap, imsi) if err != nil { return fmt.Errorf("updateSmfSelectionProvisionedData failed: %w", err) } @@ -309,14 +331,19 @@ func updateAmPolicyData(imsi string) error { return nil } -func updateSmPolicyData(snssai *models.Snssai, dnn string, imsi string) error { +func updateSmPolicyData(snssai *models.Snssai, dnnMap map[string][]configmodels.DeviceGroupsIpDomainExpandedUeDnnQos, imsi string) error { var smPolicyData models.SmPolicyData var smPolicySnssaiData models.SmPolicySnssaiData - dnnData := map[string]models.SmPolicyDnnData{ - dnn: { + // Iterate over all DNNs in the map + dnnData := make(map[string]models.SmPolicyDnnData) + + for dnn := range dnnMap { // Extract each DNN from the map + dnnData[dnn] = models.SmPolicyDnnData{ Dnn: dnn, - }, + } } + logger.DbLog.Infof("Input dnnMap: %+v", dnnMap) + logger.DbLog.Infof("dnnMap length: %d", len(dnnMap)) // smpolicydata smPolicySnssaiData.Snssai = snssai smPolicySnssaiData.SmPolicyDnnData = dnnData @@ -325,6 +352,7 @@ func updateSmPolicyData(snssai *models.Snssai, dnn string, imsi string) error { smPolicyDatBsonA := configmodels.ToBsonM(smPolicyData) smPolicyDatBsonA["ueId"] = "imsi-" + imsi filter := bson.M{"ueId": "imsi-" + imsi} + logger.DbLog.Infof("Data to be sent to database - smPolicyData: %+v", smPolicyDatBsonA) _, err := dbadapter.CommonDBClient.RestfulAPIPost(smPolicyDataColl, filter, smPolicyDatBsonA) if err != nil { logger.DbLog.Errorf("failed to update SM Policy Data for IMSI %s: %+v", imsi, err) @@ -334,98 +362,178 @@ func updateSmPolicyData(snssai *models.Snssai, dnn string, imsi string) error { return nil } -func updateAmProvisionedData(snssai *models.Snssai, qos *configmodels.DeviceGroupsIpDomainExpandedUeDnnQos, mcc, mnc, imsi string) error { - amData := models.AccessAndMobilitySubscriptionData{ - Gpsis: []string{ - "msisdn-0900000000", - }, - Nssai: &models.Nssai{ - DefaultSingleNssais: []models.Snssai{*snssai}, - SingleNssais: []models.Snssai{*snssai}, - }, - SubscribedUeAmbr: &models.AmbrRm{ - Downlink: convertToString(uint64(qos.DnnMbrDownlink)), - Uplink: convertToString(uint64(qos.DnnMbrUplink)), - }, - } - amDataBsonA := configmodels.ToBsonM(amData) - amDataBsonA["ueId"] = "imsi-" + imsi - amDataBsonA["servingPlmnId"] = mcc + mnc +func updateAmProvisionedData(snssai *models.Snssai, dnnMap map[string][]configmodels.DeviceGroupsIpDomainExpandedUeDnnQos, mcc, mnc, imsi string) error { + for dnn, ueDnnQosList := range dnnMap { + aggregatedQoS := aggregateQoS(ueDnnQosList) // Combine multiple QoS into one if needed + amData := models.AccessAndMobilitySubscriptionData{ + Gpsis: []string{"msisdn-0900000000"}, + Nssai: &models.Nssai{ + DefaultSingleNssais: []models.Snssai{*snssai}, + SingleNssais: []models.Snssai{*snssai}, + }, + SubscribedUeAmbr: &models.AmbrRm{ + Downlink: convertToString(uint64(aggregatedQoS.DnnMbrDownlink)), + Uplink: convertToString(uint64(aggregatedQoS.DnnMbrUplink)), + }, + } + amDataBsonA := configmodels.ToBsonM(amData) + amDataBsonA["ueId"] = "imsi-" + imsi + amDataBsonA["servingPlmnId"] = mcc + mnc + amDataBsonA["dnn"] = dnn // Keep DNN in the record since each DNN has different QoS + + filter := bson.M{ + "ueId": "imsi-" + imsi, + "servingPlmnId": mcc + mnc, + } + logger.DbLog.Infof("Data to be sent to database - AmProvisionedData: %+v", amDataBsonA) + _, err := dbadapter.CommonDBClient.RestfulAPIPost(amDataColl, filter, amDataBsonA) + if err != nil { + logger.DbLog.Errorf("failed to update AM provisioned Data for IMSI %s, DNN %s: %+v", imsi, dnn, err) + return err // This will exit the function on first error + } + logger.DbLog.Debugf("succeeded to update AM provisioned Data for IMSI %s, DNN %s", imsi, dnn) + } + return nil +} + +func updateSmProvisionedData(snssai *models.Snssai, dnnMap map[string][]configmodels.DeviceGroupsIpDomainExpandedUeDnnQos, mcc, mnc, imsi string) error { + // Define the filter to find the existing record for this UE filter := bson.M{ - "ueId": "imsi-" + imsi, - "$or": []bson.M{ - {"servingPlmnId": mcc + mnc}, - {"servingPlmnId": bson.M{"$exists": false}}, - }, + "ueId": "imsi-" + imsi, + "servingPlmnId": mcc + mnc, } - _, err := dbadapter.CommonDBClient.RestfulAPIPost(amDataColl, filter, amDataBsonA) - if err != nil { - logger.DbLog.Errorf("failed to update AM provisioned Data for IMSI %s: %+v", imsi, err) + + // Fetch the existing record from the database + existingRecord, err := dbadapter.CommonDBClient.RestfulAPIGetOne(smDataColl, filter) + if err != nil && err.Error() != "mongo: no documents in result" { + logger.DbLog.Warnf("Failed to fetch existing record for ueId: %s, error: %v", imsi, err) return err } - logger.DbLog.Debugf("succeeded to update AM provisioned Data for IMSI %s", imsi) - return nil -} -func updateSmProvisionedData(snssai *models.Snssai, qos *configmodels.DeviceGroupsIpDomainExpandedUeDnnQos, mcc, mnc, dnn, imsi string) error { - smData := models.SessionManagementSubscriptionData{ - SingleNssai: snssai, - DnnConfigurations: map[string]models.DnnConfiguration{ - dnn: { - PduSessionTypes: &models.PduSessionTypes{ - DefaultSessionType: models.PduSessionType_IPV4, - AllowedSessionTypes: []models.PduSessionType{models.PduSessionType_IPV4}, - }, - SscModes: &models.SscModes{ - DefaultSscMode: models.SscMode__1, - AllowedSscModes: []models.SscMode{ - "SSC_MODE_2", - "SSC_MODE_3", - }, - }, - SessionAmbr: &models.Ambr{ - Downlink: convertToString(uint64(qos.DnnMbrDownlink)), - Uplink: convertToString(uint64(qos.DnnMbrUplink)), + var smData models.SessionManagementSubscriptionData + if existingRecord == nil { + // No existing record, create a new one + smData = models.SessionManagementSubscriptionData{ + SingleNssai: snssai, + DnnConfigurations: make(map[string]models.DnnConfiguration), + } + } else { + // Convert existing record to struct + bsonBytes, errMarshal := bson.Marshal(existingRecord) + if errMarshal != nil { + logger.DbLog.Errorf("Failed to marshal existing record: %v", errMarshal) + return errMarshal + } + + // Unmarshal BSON into struct + errUnmarshal := bson.Unmarshal(bsonBytes, &smData) + if errUnmarshal != nil { + logger.DbLog.Errorf("Failed to unmarshal existing record: %v", errUnmarshal) + return errUnmarshal + } + + // Ensure DnnConfigurations is initialized + if smData.DnnConfigurations == nil { + smData.DnnConfigurations = make(map[string]models.DnnConfiguration) + } + } + + // Iterate over DNNs and add/update their configurations + for dnn, ueDnnQosList := range dnnMap { + aggregatedQoS := aggregateQoS(ueDnnQosList) // Combine multiple QoS per DNN + + smData.DnnConfigurations[dnn] = models.DnnConfiguration{ + PduSessionTypes: &models.PduSessionTypes{ + DefaultSessionType: models.PduSessionType_IPV4, + AllowedSessionTypes: []models.PduSessionType{models.PduSessionType_IPV4}, + }, + SscModes: &models.SscModes{ + DefaultSscMode: models.SscMode__1, + AllowedSscModes: []models.SscMode{ + "SSC_MODE_2", + "SSC_MODE_3", }, - Var5gQosProfile: &models.SubscribedDefaultQos{ - Var5qi: 9, - Arp: &models.Arp{ - PriorityLevel: 8, - }, + }, + SessionAmbr: &models.Ambr{ + Downlink: convertToString(uint64(aggregatedQoS.DnnMbrDownlink)), + Uplink: convertToString(uint64(aggregatedQoS.DnnMbrUplink)), + }, + Var5gQosProfile: &models.SubscribedDefaultQos{ + Var5qi: aggregatedQoS.TrafficClass.Qci, + Arp: &models.Arp{ PriorityLevel: 8, }, + PriorityLevel: 8, }, - }, + } } - smDataBsonA := configmodels.ToBsonM(smData) - smDataBsonA["ueId"] = "imsi-" + imsi - smDataBsonA["servingPlmnId"] = mcc + mnc - filter := bson.M{"ueId": "imsi-" + imsi, "servingPlmnId": mcc + mnc} - _, err := dbadapter.CommonDBClient.RestfulAPIPost(smDataColl, filter, smDataBsonA) + + // Convert to BSON format + bsonBytes, err := bson.Marshal(smData) + if err != nil { + logger.DbLog.Errorf("Failed to marshal smData: %v", err) + return err + } + + // Unmarshal BSON into map[string]interface{} for updating MongoDB + var smDataBsonA map[string]interface{} + err = bson.Unmarshal(bsonBytes, &smDataBsonA) if err != nil { - logger.DbLog.Errorf("failed to update SM provisioned Data for IMSI %s: %+v", imsi, err) + logger.DbLog.Errorf("Failed to unmarshal smData BSON: %v", err) return err } + + // Add required fields + smDataBsonA["ueId"] = "imsi-" + imsi + smDataBsonA["servingPlmnId"] = mcc + mnc + + // Update the database + logger.DbLog.Infof("Data to be sent to database - SmProvisionedData: %+v", smDataBsonA) + _, errPost := dbadapter.CommonDBClient.RestfulAPIPost(smDataColl, filter, smDataBsonA) + if errPost != nil { + logger.DbLog.Errorf("failed to update SM provisioned Data for IMSI %s: %+v", imsi, errPost) + return errPost + } logger.DbLog.Debugf("updated SM provisioned Data for IMSI %s", imsi) return nil } -func updateSmfSelectionProvisionedData(snssai *models.Snssai, mcc, mnc, dnn, imsi string) error { +func updateSmfSelectionProvisionedData(snssai *models.Snssai, mcc, mnc string, dnnMap map[string][]configmodels.DeviceGroupsIpDomainExpandedUeDnnQos, imsi string) error { smfSelData := models.SmfSelectionSubscriptionData{ - SubscribedSnssaiInfos: map[string]models.SnssaiInfo{ - SnssaiModelsToHex(*snssai): { - DnnInfos: []models.DnnInfo{ - { - Dnn: dnn, - }, - }, - }, - }, + SubscribedSnssaiInfos: map[string]models.SnssaiInfo{}, + } + + // Prepare SnssaiInfo for this snssai + snssaiInfo := models.SnssaiInfo{ + DnnInfos: []models.DnnInfo{}, } + + // Iterate through the dnnMap to populate DnnInfos + for dnn := range dnnMap { + // Append each DNN's info to DnnInfos + snssaiInfo.DnnInfos = append(snssaiInfo.DnnInfos, models.DnnInfo{ + Dnn: dnn, + }) + } + + // Add the SnssaiInfo to the map using the hex representation of the snssai + smfSelData.SubscribedSnssaiInfos[SnssaiModelsToHex(*snssai)] = snssaiInfo + + // Convert to BSON format smfSelecDataBsonA := configmodels.ToBsonM(smfSelData) smfSelecDataBsonA["ueId"] = "imsi-" + imsi smfSelecDataBsonA["servingPlmnId"] = mcc + mnc - filter := bson.M{"ueId": "imsi-" + imsi, "servingPlmnId": mcc + mnc} + + // Define the filter for the database operation + filter := bson.M{ + "ueId": "imsi-" + imsi, + "servingPlmnId": mcc + mnc, + } + + // Log the data to be sent to the database + logger.DbLog.Infof("Data to be sent to database - smf selection: %+v", smfSelecDataBsonA) + + // Perform the database post operation _, err := dbadapter.CommonDBClient.RestfulAPIPost(smfSelDataColl, filter, smfSelecDataBsonA) if err != nil { logger.DbLog.Errorf("failed to update SMF selection provisioned data for IMSI %s: %+v", imsi, err) @@ -531,3 +639,16 @@ func getDeletedDeviceGroupsList(slice, prevSlice configmodels.Slice) []string { } return deleted } + +func aggregateQoS(qosList []configmodels.DeviceGroupsIpDomainExpandedUeDnnQos) configmodels.DeviceGroupsIpDomainExpandedUeDnnQos { + var aggregated configmodels.DeviceGroupsIpDomainExpandedUeDnnQos + for _, qos := range qosList { + aggregated.DnnMbrUplink += qos.DnnMbrUplink + aggregated.DnnMbrDownlink += qos.DnnMbrDownlink + aggregated.BitrateUnit = qos.BitrateUnit + if qos.TrafficClass != nil { + aggregated.TrafficClass = qos.TrafficClass + } + } + return aggregated +} diff --git a/configmodels/model_device_groups.go b/configmodels/model_device_groups.go index a9d53dc5..5c7d3d3b 100644 --- a/configmodels/model_device_groups.go +++ b/configmodels/model_device_groups.go @@ -23,5 +23,5 @@ type DeviceGroups struct { IpDomainName string `json:"ip-domain-name,omitempty"` - IpDomainExpanded DeviceGroupsIpDomainExpanded `json:"ip-domain-expanded,omitempty"` + IpDomainExpanded []DeviceGroupsIpDomainExpanded `json:"ip-domains,omitempty"` } diff --git a/go.mod b/go.mod index c01d9f3f..47b4d73f 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,12 @@ module github.com/omec-project/webconsole go 1.24.0 require ( + github.com/5GC-DEV/config5g-cdac v0.2.1 github.com/gin-contrib/cors v1.7.6 github.com/gin-gonic/gin v1.10.1 github.com/golang-jwt/jwt/v5 v5.2.2 github.com/google/uuid v1.6.0 github.com/mitchellh/mapstructure v1.5.0 - github.com/omec-project/config5g v1.6.2 github.com/omec-project/openapi v1.5.0 github.com/omec-project/util v1.3.2 github.com/prometheus/client_golang v1.22.0 diff --git a/go.sum b/go.sum index 20788a10..d1967d33 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/5GC-DEV/config5g-cdac v0.2.1 h1:7Cqwmqh5jqeWtwYTrITflrGotglIRBIb1P3EF2f85OA= +github.com/5GC-DEV/config5g-cdac v0.2.1/go.mod h1:0OEL6UoNKKlx2tlNSoKpRtzckwxR2SL+pRBDAOCT4BU= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= @@ -95,8 +97,6 @@ github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8 github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/omec-project/config5g v1.6.2 h1:bfLxwVSt6LAWCQtBmjUuks5CO3qambvcy3VjSVAMHdk= -github.com/omec-project/config5g v1.6.2/go.mod h1:LuaRiJTCJXCkQF7nKB5fOcz6oVMF/8oid+EKCYFAy0E= github.com/omec-project/openapi v1.5.0 h1:WV3JjYwqio1xRTb1Nvc6kpSGgf+/VzrScU9WS9PBNwE= github.com/omec-project/openapi v1.5.0/go.mod h1:4nwAVKA4GUXw5OnjxYOU8LAoRrpGTG/ruJLgKiE/Ccs= github.com/omec-project/util v1.3.2 h1:5JP4ZYqMPPESSYrgc6LQNlcvJ/Xhrq8u5jSV405iuX8= diff --git a/proto/server/clientEvtHandler.go b/proto/server/clientEvtHandler.go index 2754d0cb..f5a9e13e 100644 --- a/proto/server/clientEvtHandler.go +++ b/proto/server/clientEvtHandler.go @@ -14,7 +14,7 @@ import ( "strings" "time" - protos "github.com/omec-project/config5g/proto/sdcoreConfig" + protos "github.com/5GC-DEV/config5g-cdac/proto/sdcoreConfig" "github.com/omec-project/webconsole/backend/factory" "github.com/omec-project/webconsole/backend/logger" "github.com/omec-project/webconsole/configmodels" @@ -252,27 +252,31 @@ func fillSite(siteInfoConf *configmodels.SliceSiteInfo, siteInfoProto *protos.Si func fillDeviceGroup(groupName string, devGroupConfig *configmodels.DeviceGroups, devGroupProto *protos.DeviceGroup) { devGroupProto.Name = groupName - ipdomain := &protos.IpDomain{} - ipdomain.Name = devGroupConfig.IpDomainName - ipdomain.DnnName = devGroupConfig.IpDomainExpanded.Dnn - ipdomain.UePool = devGroupConfig.IpDomainExpanded.UeIpPool - ipdomain.DnsPrimary = devGroupConfig.IpDomainExpanded.DnsPrimary - ipdomain.Mtu = devGroupConfig.IpDomainExpanded.Mtu - if devGroupConfig.IpDomainExpanded.UeDnnQos != nil { - ipdomain.UeDnnQos = &protos.UeDnnQosInfo{} - ipdomain.UeDnnQos.DnnMbrUplink = devGroupConfig.IpDomainExpanded.UeDnnQos.DnnMbrUplink - ipdomain.UeDnnQos.DnnMbrDownlink = devGroupConfig.IpDomainExpanded.UeDnnQos.DnnMbrDownlink - if devGroupConfig.IpDomainExpanded.UeDnnQos.TrafficClass != nil { - ipdomain.UeDnnQos.TrafficClass = &protos.TrafficClassInfo{} - ipdomain.UeDnnQos.TrafficClass.Name = devGroupConfig.IpDomainExpanded.UeDnnQos.TrafficClass.Name - ipdomain.UeDnnQos.TrafficClass.Qci = devGroupConfig.IpDomainExpanded.UeDnnQos.TrafficClass.Qci - ipdomain.UeDnnQos.TrafficClass.Arp = devGroupConfig.IpDomainExpanded.UeDnnQos.TrafficClass.Arp - ipdomain.UeDnnQos.TrafficClass.Pdb = devGroupConfig.IpDomainExpanded.UeDnnQos.TrafficClass.Pdb - ipdomain.UeDnnQos.TrafficClass.Pelr = devGroupConfig.IpDomainExpanded.UeDnnQos.TrafficClass.Pelr + for _, ipDomainExpanded := range devGroupConfig.IpDomainExpanded { + ipdomain := &protos.IpDomain{} + ipdomain.Name = devGroupConfig.IpDomainName + ipdomain.DnnName = ipDomainExpanded.Dnn + ipdomain.UePool = ipDomainExpanded.UeIpPool + ipdomain.DnsPrimary = ipDomainExpanded.DnsPrimary + ipdomain.Mtu = ipDomainExpanded.Mtu + + if ipDomainExpanded.UeDnnQos != nil { + ipdomain.UeDnnQos = &protos.UeDnnQosInfo{} + ipdomain.UeDnnQos.DnnMbrUplink = ipDomainExpanded.UeDnnQos.DnnMbrUplink + ipdomain.UeDnnQos.DnnMbrDownlink = ipDomainExpanded.UeDnnQos.DnnMbrDownlink + + if ipDomainExpanded.UeDnnQos.TrafficClass != nil { + ipdomain.UeDnnQos.TrafficClass = &protos.TrafficClassInfo{} + ipdomain.UeDnnQos.TrafficClass.Name = ipDomainExpanded.UeDnnQos.TrafficClass.Name + ipdomain.UeDnnQos.TrafficClass.Qci = ipDomainExpanded.UeDnnQos.TrafficClass.Qci + ipdomain.UeDnnQos.TrafficClass.Arp = ipDomainExpanded.UeDnnQos.TrafficClass.Arp + ipdomain.UeDnnQos.TrafficClass.Pdb = ipDomainExpanded.UeDnnQos.TrafficClass.Pdb + ipdomain.UeDnnQos.TrafficClass.Pelr = ipDomainExpanded.UeDnnQos.TrafficClass.Pelr + } } - } - devGroupProto.IpDomainDetails = ipdomain + devGroupProto.IpDomainDetails = append(devGroupProto.IpDomainDetails, ipdomain) + } for i := 0; i < len(devGroupConfig.Imsis); i++ { devGroupProto.Imsi = append(devGroupProto.Imsi, devGroupConfig.Imsis[i]) @@ -286,7 +290,7 @@ func fillSlice(client *clientNF, sliceName string, sliceConf *configmodels.Slice nssai.Sd = sliceConf.SliceId.Sd sliceProto.Nssai = nssai - var defaultQos *configmodels.DeviceGroupsIpDomainExpandedUeDnnQos + var defaultQosList []*configmodels.DeviceGroupsIpDomainExpandedUeDnnQos for d := 0; d < len(sliceConf.SiteDeviceGroup); d++ { group := sliceConf.SiteDeviceGroup[d] client.clientLog.Debugf("group %v, len of devgroupsConfigClient %v ", group, len(client.devgroupsConfigClient)) @@ -296,12 +300,19 @@ func fillSlice(client *clientNF, sliceName string, sliceConf *configmodels.Slice return false } - if (defaultQos == nil) && (devGroupConfig.IpDomainExpanded.UeDnnQos != nil) && - (devGroupConfig.IpDomainExpanded.UeDnnQos.TrafficClass != nil) { - defaultQos = &configmodels.DeviceGroupsIpDomainExpandedUeDnnQos{} - defaultQos.TrafficClass = &configmodels.TrafficClassInfo{} - defaultQos.TrafficClass.Qci = devGroupConfig.IpDomainExpanded.UeDnnQos.TrafficClass.Qci - defaultQos.TrafficClass.Arp = devGroupConfig.IpDomainExpanded.UeDnnQos.TrafficClass.Arp + for _, ipDomainExpanded := range devGroupConfig.IpDomainExpanded { + if ipDomainExpanded.UeDnnQos != nil && ipDomainExpanded.UeDnnQos.TrafficClass != nil { + // Create a new QoS entry for each iteration + newQos := &configmodels.DeviceGroupsIpDomainExpandedUeDnnQos{ + TrafficClass: &configmodels.TrafficClassInfo{ + Qci: ipDomainExpanded.UeDnnQos.TrafficClass.Qci, + Arp: ipDomainExpanded.UeDnnQos.TrafficClass.Arp, + }, + } + + // Append to the list instead of overwriting + defaultQosList = append(defaultQosList, newQos) + } } devGroupProto := &protos.DeviceGroup{} @@ -318,86 +329,89 @@ func fillSlice(client *clientNF, sliceName string, sliceConf *configmodels.Slice } for _, ruleConfig := range sliceConf.ApplicationFilteringRules { client.clientLog.Debugf("Received Rule config = %v ", ruleConfig) - pccRule := protos.PccRule{} - - // RuleName - pccRule.RuleId = ruleConfig.RuleName - - // Rule Precedence - pccRule.Priority = ruleConfig.Priority + for _, defaultQos := range defaultQosList { // Iterate over defaultQosList + pccRule := protos.PccRule{} + + // RuleName + pccRule.RuleId = ruleConfig.RuleName + + // Rule Precedence + pccRule.Priority = ruleConfig.Priority + + // Qos Info + ruleQos := protos.PccRuleQos{} + ruleQos.MaxbrUl = ruleConfig.AppMbrUplink + ruleQos.MaxbrDl = ruleConfig.AppMbrDownlink + ruleQos.GbrUl = 0 + ruleQos.GbrDl = 0 + + var arpi, var5qi int32 + + if ruleConfig.TrafficClass != nil { + var5qi = ruleConfig.TrafficClass.Qci + arpi = ruleConfig.TrafficClass.Arp + } else if defaultQos != nil && defaultQos.TrafficClass != nil { + var5qi = defaultQos.TrafficClass.Qci + arpi = defaultQos.TrafficClass.Arp + } else { + var5qi = 9 + arpi = 1 + } - // Qos Info - ruleQos := protos.PccRuleQos{} - ruleQos.MaxbrUl = ruleConfig.AppMbrUplink - ruleQos.MaxbrDl = ruleConfig.AppMbrDownlink - ruleQos.GbrUl = 0 - ruleQos.GbrUl = 0 - - var arpi, var5qi int32 - - if ruleConfig.TrafficClass != nil { - var5qi = ruleConfig.TrafficClass.Qci - arpi = ruleConfig.TrafficClass.Arp - } else if defaultQos != nil { - var5qi = defaultQos.TrafficClass.Qci - arpi = defaultQos.TrafficClass.Arp - } else { - var5qi = 9 - arpi = 1 - } - if arpi > 15 { - arpi = 15 - } + if arpi > 15 { + arpi = 15 + } - ruleQos.Var5Qi = var5qi - arp := &protos.PccArp{} - arp.PL = arpi - arp.PC = protos.PccArpPc(1) - arp.PV = protos.PccArpPv(1) - ruleQos.Arp = arp - pccRule.Qos = &ruleQos + ruleQos.Var5Qi = var5qi + arp := &protos.PccArp{} + arp.PL = arpi + arp.PC = protos.PccArpPc(1) + arp.PV = protos.PccArpPv(1) + ruleQos.Arp = arp + pccRule.Qos = &ruleQos + + // Flow Info + pccRule.FlowInfos = make([]*protos.PccFlowInfo, 0) + var desc string + endp := ruleConfig.Endpoint + if strings.HasPrefix(endp, "0.0.0.0") { + endp = "any" + } - // Flow Info - // As of now config provides us only single flow - pccRule.FlowInfos = make([]*protos.PccFlowInfo, 0) - var desc string - endp := ruleConfig.Endpoint - if strings.HasPrefix(endp, "0.0.0.0") { - endp = "any" - } - if ruleConfig.Protocol == int32(protos.PccFlowTos_TCP.Number()) { - if ruleConfig.StartPort == 0 && ruleConfig.EndPort == 0 { - desc = "permit out tcp from " + endp + " to assigned" - } else if factory.WebUIConfig.Configuration.SdfComp { - desc = "permit out tcp from " + endp + " " + strconv.FormatInt(int64(ruleConfig.StartPort), 10) + "-" + strconv.FormatInt(int64(ruleConfig.EndPort), 10) + " to assigned" + if ruleConfig.Protocol == int32(protos.PccFlowTos_TCP.Number()) { + if ruleConfig.StartPort == 0 && ruleConfig.EndPort == 0 { + desc = "permit out tcp from " + endp + " to assigned" + } else if factory.WebUIConfig.Configuration.SdfComp { + desc = "permit out tcp from " + endp + " " + strconv.FormatInt(int64(ruleConfig.StartPort), 10) + "-" + strconv.FormatInt(int64(ruleConfig.EndPort), 10) + " to assigned" + } else { + desc = "permit out tcp from " + endp + " to assigned " + strconv.FormatInt(int64(ruleConfig.StartPort), 10) + "-" + strconv.FormatInt(int64(ruleConfig.EndPort), 10) + } + } else if ruleConfig.Protocol == int32(protos.PccFlowTos_UDP.Number()) { + if ruleConfig.StartPort == 0 && ruleConfig.EndPort == 0 { + desc = "permit out udp from " + endp + " to assigned" + } else if factory.WebUIConfig.Configuration.SdfComp { + desc = "permit out udp from " + endp + " " + strconv.FormatInt(int64(ruleConfig.StartPort), 10) + "-" + strconv.FormatInt(int64(ruleConfig.EndPort), 10) + " to assigned" + } else { + desc = "permit out udp from " + endp + " to assigned " + strconv.FormatInt(int64(ruleConfig.StartPort), 10) + "-" + strconv.FormatInt(int64(ruleConfig.EndPort), 10) + } } else { - desc = "permit out tcp from " + endp + " to assigned " + strconv.FormatInt(int64(ruleConfig.StartPort), 10) + "-" + strconv.FormatInt(int64(ruleConfig.EndPort), 10) + desc = "permit out ip from " + endp + " to assigned" } - } else if ruleConfig.Protocol == int32(protos.PccFlowTos_UDP.Number()) { - if ruleConfig.StartPort == 0 && ruleConfig.EndPort == 0 { - desc = "permit out udp from " + endp + " to assigned" - } else if factory.WebUIConfig.Configuration.SdfComp { - desc = "permit out udp from " + endp + " " + strconv.FormatInt(int64(ruleConfig.StartPort), 10) + "-" + strconv.FormatInt(int64(ruleConfig.EndPort), 10) + " to assigned" + + flowInfo := protos.PccFlowInfo{} + flowInfo.FlowDesc = desc + flowInfo.TosTrafficClass = "IPV4" + flowInfo.FlowDir = protos.PccFlowDirection_BIDIRECTIONAL + if ruleConfig.Action == "deny" { + flowInfo.FlowStatus = protos.PccFlowStatus_DISABLED } else { - desc = "permit out udp from " + endp + " to assigned " + strconv.FormatInt(int64(ruleConfig.StartPort), 10) + "-" + strconv.FormatInt(int64(ruleConfig.EndPort), 10) + flowInfo.FlowStatus = protos.PccFlowStatus_ENABLED } - } else { - desc = "permit out ip from " + endp + " to assigned" - } + pccRule.FlowInfos = append(pccRule.FlowInfos, &flowInfo) - flowInfo := protos.PccFlowInfo{} - flowInfo.FlowDesc = desc - flowInfo.TosTrafficClass = "IPV4" - flowInfo.FlowDir = protos.PccFlowDirection_BIDIRECTIONAL - if ruleConfig.Action == "deny" { - flowInfo.FlowStatus = protos.PccFlowStatus_DISABLED - } else { - flowInfo.FlowStatus = protos.PccFlowStatus_ENABLED + // Add PCC rule to Rulebase + appFilters.PccRuleBase = append(appFilters.PccRuleBase, &pccRule) } - pccRule.FlowInfos = append(pccRule.FlowInfos, &flowInfo) - - // Add PCC rule to Rulebase - appFilters.PccRuleBase = append(appFilters.PccRuleBase, &pccRule) } // AppFiltering rules not configured, so configuring default rule if len(sliceConf.ApplicationFilteringRules) == 0 { @@ -932,27 +946,38 @@ func postConfigHss(client *clientNF, lastDevGroup *configmodels.DeviceGroups, la // Traffic Class // override with device-group specific if available - if devGroup.IpDomainExpanded.UeDnnQos != nil && devGroup.IpDomainExpanded.UeDnnQos.TrafficClass != nil { - config.Qci = devGroup.IpDomainExpanded.UeDnnQos.TrafficClass.Qci - config.Arp = devGroup.IpDomainExpanded.UeDnnQos.TrafficClass.Arp - } + if len(devGroup.IpDomainExpanded) > 0 { + for _, ipDomain := range devGroup.IpDomainExpanded { + // Traffic Class + // Override with device-group specific if available + if ipDomain.UeDnnQos != nil && ipDomain.UeDnnQos.TrafficClass != nil { + config.Qci = ipDomain.UeDnnQos.TrafficClass.Qci + config.Arp = ipDomain.UeDnnQos.TrafficClass.Arp + } - // UL AMBR - // override with device-group specific if available - if devGroup.IpDomainExpanded.UeDnnQos != nil && devGroup.IpDomainExpanded.UeDnnQos.DnnMbrUplink != 0 { - config.AmbrUl = int32(devGroup.IpDomainExpanded.UeDnnQos.DnnMbrUplink) - } + // UL AMBR + // Override with device-group specific if available + if ipDomain.UeDnnQos != nil && ipDomain.UeDnnQos.DnnMbrUplink != 0 { + config.AmbrUl = int32(ipDomain.UeDnnQos.DnnMbrUplink) + } - // DL AMBR - // override with device-group specific if available - if devGroup.IpDomainExpanded.UeDnnQos != nil && devGroup.IpDomainExpanded.UeDnnQos.DnnMbrDownlink != 0 { - config.AmbrDl = int32(devGroup.IpDomainExpanded.UeDnnQos.DnnMbrDownlink) - } + // DL AMBR + // Override with device-group specific if available + if ipDomain.UeDnnQos != nil && ipDomain.UeDnnQos.DnnMbrDownlink != 0 { + config.AmbrDl = int32(ipDomain.UeDnnQos.DnnMbrDownlink) + } - var apnProf apnProfile - apnProf.ApnName = devGroup.IpDomainExpanded.Dnn - apnProfName := sliceName + "-" + apnProf.ApnName + "-apn" - config.ApnProfiles[apnProfName] = &apnProf + // Handle APN Profile + if ipDomain.Dnn != "" { + var apnProf apnProfile + apnProf.ApnName = ipDomain.Dnn + apnProfName := sliceName + "-" + apnProf.ApnName + "-apn" + config.ApnProfiles[apnProfName] = &apnProf + } + } + } else { + client.clientLog.Warn("No IP Domain Expanded data found in the device group.") + } var newImsis []string if lastDevGroup != nil && lastDevGroup.DeviceGroupName == devGroup.DeviceGroupName { @@ -1050,21 +1075,27 @@ func postConfigPcrf(client *clientNF) { sgroup := &pcrfServiceGroup{} pcrfServiceName := d + "-service" sgroup.Def_service = append(sgroup.Def_service, pcrfServiceName) - config.Policies.ServiceGroups[devGroup.IpDomainExpanded.Dnn] = sgroup + // config.Policies.ServiceGroups[devGroup.IpDomainExpanded.Dnn] = sgroup pcrfService := &pcrfServices{} // Traffic Class - if devGroup.IpDomainExpanded.UeDnnQos != nil && devGroup.IpDomainExpanded.UeDnnQos.TrafficClass != nil { - pcrfService.Qci = devGroup.IpDomainExpanded.UeDnnQos.TrafficClass.Qci - pcrfService.Arp = devGroup.IpDomainExpanded.UeDnnQos.TrafficClass.Arp - } + if len(devGroup.IpDomainExpanded) > 0 { + for _, ipDomain := range devGroup.IpDomainExpanded { + // Traffic Class + if ipDomain.UeDnnQos != nil && ipDomain.UeDnnQos.TrafficClass != nil { + pcrfService.Qci = ipDomain.UeDnnQos.TrafficClass.Qci + pcrfService.Arp = ipDomain.UeDnnQos.TrafficClass.Arp + } - // AMBR UL - if devGroup.IpDomainExpanded.UeDnnQos != nil && devGroup.IpDomainExpanded.UeDnnQos.DnnMbrUplink != 0 { - pcrfService.Ambr_ul = int32(devGroup.IpDomainExpanded.UeDnnQos.DnnMbrUplink) - } - // AMBR DL - if devGroup.IpDomainExpanded.UeDnnQos != nil && devGroup.IpDomainExpanded.UeDnnQos.DnnMbrDownlink != 0 { - pcrfService.Ambr_dl = int32(devGroup.IpDomainExpanded.UeDnnQos.DnnMbrDownlink) + // AMBR UL + if ipDomain.UeDnnQos != nil && ipDomain.UeDnnQos.DnnMbrUplink != 0 { + pcrfService.Ambr_ul = int32(ipDomain.UeDnnQos.DnnMbrUplink) + } + + // AMBR DL + if ipDomain.UeDnnQos != nil && ipDomain.UeDnnQos.DnnMbrDownlink != 0 { + pcrfService.Ambr_dl = int32(ipDomain.UeDnnQos.DnnMbrDownlink) + } + } } if len(sliceConfig.ApplicationFilteringRules) == 0 { @@ -1089,39 +1120,42 @@ func postConfigPcrf(client *clientNF) { ruleQInfo := &ruleQosInfo{} ruledef.QosInfo = ruleQInfo var arpi int32 - if app.TrafficClass != nil { - ruleQInfo.Qci = app.TrafficClass.Qci - arpi = app.TrafficClass.Arp - } else if devGroup.IpDomainExpanded.UeDnnQos != nil && - devGroup.IpDomainExpanded.UeDnnQos.TrafficClass != nil { - ruleQInfo.Qci = devGroup.IpDomainExpanded.UeDnnQos.TrafficClass.Qci - arpi = devGroup.IpDomainExpanded.UeDnnQos.TrafficClass.Arp - } else { - ruleQInfo.Qci = 9 - arpi = 1 - } - if arpi > 15 { - arpi = 15 - } - ruleQInfo.Mbr_ul = app.AppMbrUplink - ruleQInfo.Mbr_dl = app.AppMbrDownlink - ruleQInfo.Gbr_ul = 0 - ruleQInfo.Gbr_dl = 0 - - // override with device-group specific if available - if devGroup.IpDomainExpanded.UeDnnQos != nil && devGroup.IpDomainExpanded.UeDnnQos.DnnMbrUplink != 0 { - ruleQInfo.ApnAmbrUl = int32(devGroup.IpDomainExpanded.UeDnnQos.DnnMbrUplink) - } - if ruleQInfo.Mbr_ul == 0 { - ruleQInfo.Mbr_ul = ruleQInfo.ApnAmbrUl - } + if len(devGroup.IpDomainExpanded) > 0 { + for _, ipDomain := range devGroup.IpDomainExpanded { + if app.TrafficClass != nil { + ruleQInfo.Qci = app.TrafficClass.Qci + arpi = app.TrafficClass.Arp + } else if ipDomain.UeDnnQos != nil && ipDomain.UeDnnQos.TrafficClass != nil { + ruleQInfo.Qci = ipDomain.UeDnnQos.TrafficClass.Qci + arpi = ipDomain.UeDnnQos.TrafficClass.Arp + } else { + ruleQInfo.Qci = 9 + arpi = 1 + } + if arpi > 15 { + arpi = 15 + } + ruleQInfo.Mbr_ul = app.AppMbrUplink + ruleQInfo.Mbr_dl = app.AppMbrDownlink + ruleQInfo.Gbr_ul = 0 + ruleQInfo.Gbr_dl = 0 + + // override with device-group specific if available + if ipDomain.UeDnnQos != nil && ipDomain.UeDnnQos.DnnMbrUplink != 0 { + ruleQInfo.ApnAmbrUl = int32(ipDomain.UeDnnQos.DnnMbrUplink) + } + if ruleQInfo.Mbr_ul == 0 { + ruleQInfo.Mbr_ul = ruleQInfo.ApnAmbrUl + } - // override with device-group specific if available - if devGroup.IpDomainExpanded.UeDnnQos != nil && devGroup.IpDomainExpanded.UeDnnQos.DnnMbrDownlink != 0 { - ruleQInfo.ApnAmbrDl = int32(devGroup.IpDomainExpanded.UeDnnQos.DnnMbrDownlink) - } - if ruleQInfo.Mbr_dl == 0 { - ruleQInfo.Mbr_dl = ruleQInfo.ApnAmbrDl + // override with device-group specific if available + if ipDomain.UeDnnQos != nil && ipDomain.UeDnnQos.DnnMbrDownlink != 0 { + ruleQInfo.ApnAmbrDl = int32(ipDomain.UeDnnQos.DnnMbrDownlink) + } + if ruleQInfo.Mbr_dl == 0 { + ruleQInfo.Mbr_dl = ruleQInfo.ApnAmbrDl + } + } } arp := &arpInfo{} arp.Priority = arpi @@ -1211,44 +1245,46 @@ func postConfigSpgw(client *clientNF) { client.clientLog.Errorln("Device Group is not exist: ", d) continue } - var rule subSelectionRule - rule.Priority = 1 - var apnProf apnProfile - apnProf.DnsPrimary = devGroup.IpDomainExpanded.DnsPrimary - apnProf.DnsSecondary = devGroup.IpDomainExpanded.DnsSecondary - apnProf.ApnName = devGroup.IpDomainExpanded.Dnn - apnProf.Mtu = devGroup.IpDomainExpanded.Mtu - apnProf.GxEnabled = false - apnProfName := sliceName + "-" + apnProf.ApnName + "-apn" - config.ApnProfiles[apnProfName] = &apnProf - rule.SelectedApnProfile = apnProfName - - // user plane profile - var upProf userPlaneProfile - userProfName := sliceName + "_up" - upProf.UserPlane = siteInfo.Upf["upf-name"].(string) - upProf.GlobalAddress = true - config.UserPlaneProfiles[userProfName] = &upProf - rule.SelectedUserPlaneProfile = userProfName - - // qos profile - qosProfName := sliceName + "_qos" - var qosProf qosProfile - if (devGroup.IpDomainExpanded.UeDnnQos != nil) && - (devGroup.IpDomainExpanded.UeDnnQos.TrafficClass != nil) { - qosProf.Qci = devGroup.IpDomainExpanded.UeDnnQos.TrafficClass.Qci - qosProf.Arp = devGroup.IpDomainExpanded.UeDnnQos.TrafficClass.Arp - qosProf.Ambr = append(qosProf.Ambr, int32(devGroup.IpDomainExpanded.UeDnnQos.DnnMbrUplink)) - qosProf.Ambr = append(qosProf.Ambr, int32(devGroup.IpDomainExpanded.UeDnnQos.DnnMbrDownlink)) - } + for _, ipDomain := range devGroup.IpDomainExpanded { + var rule subSelectionRule + rule.Priority = 1 + var apnProf apnProfile + apnProf.DnsPrimary = ipDomain.DnsPrimary + apnProf.DnsSecondary = ipDomain.DnsSecondary + apnProf.ApnName = ipDomain.Dnn + apnProf.Mtu = ipDomain.Mtu + apnProf.GxEnabled = false + apnProfName := sliceName + "-" + apnProf.ApnName + "-apn" + config.ApnProfiles[apnProfName] = &apnProf + rule.SelectedApnProfile = apnProfName + + // User plane profile + var upProf userPlaneProfile + userProfName := sliceName + "_up" + upProf.UserPlane = siteInfo.Upf["upf-name"].(string) + upProf.GlobalAddress = true + config.UserPlaneProfiles[userProfName] = &upProf + rule.SelectedUserPlaneProfile = userProfName + + // QoS profile + qosProfName := sliceName + "_qos" + var qosProf qosProfile + if ipDomain.UeDnnQos != nil && ipDomain.UeDnnQos.TrafficClass != nil { + qosProf.Qci = ipDomain.UeDnnQos.TrafficClass.Qci + qosProf.Arp = ipDomain.UeDnnQos.TrafficClass.Arp + qosProf.Ambr = append(qosProf.Ambr, int32(ipDomain.UeDnnQos.DnnMbrUplink)) + qosProf.Ambr = append(qosProf.Ambr, int32(ipDomain.UeDnnQos.DnnMbrDownlink)) + } - config.QosProfiles[qosProfName] = &qosProf - rule.SelectedQoSProfile = qosProfName + config.QosProfiles[qosProfName] = &qosProf + rule.SelectedQoSProfile = qosProfName - var key selectionKeys - key.RequestedApn = devGroup.IpDomainExpanded.Dnn - rule.Keys = key - config.SubSelectRules = append(config.SubSelectRules, &rule) + // Selection Keys + var key selectionKeys + key.RequestedApn = ipDomain.Dnn + rule.Keys = key + config.SubSelectRules = append(config.SubSelectRules, &rule) + } } } client.clientLog.Infoln("spgw Config after filling details ", config) diff --git a/proto/server/configEvtHandler_test.go b/proto/server/configEvtHandler_test.go index f90db9df..f8245fc4 100644 --- a/proto/server/configEvtHandler_test.go +++ b/proto/server/configEvtHandler_test.go @@ -48,7 +48,7 @@ func deviceGroup(name string) configmodels.DeviceGroups { Imsis: []string{"1234", "5678"}, SiteInfo: "demo", IpDomainName: "pool1", - IpDomainExpanded: ipdomain, + IpDomainExpanded: []configmodels.DeviceGroupsIpDomainExpanded{ipdomain}, } return deviceGroup } diff --git a/proto/server/gServer.go b/proto/server/gServer.go index 18299d26..7ca61a48 100644 --- a/proto/server/gServer.go +++ b/proto/server/gServer.go @@ -11,7 +11,7 @@ import ( "os" "time" - protos "github.com/omec-project/config5g/proto/sdcoreConfig" + protos "github.com/5GC-DEV/config5g-cdac/proto/sdcoreConfig" "github.com/omec-project/openapi/models" "github.com/omec-project/webconsole/backend/factory" "github.com/omec-project/webconsole/backend/logger"