diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go
index c1a58d7aa..acb9968ac 100644
--- a/cmd/cgr-engine/cgr-engine.go
+++ b/cmd/cgr-engine/cgr-engine.go
@@ -853,6 +853,21 @@ func startSupplierService(internalSupplierSChan chan rpcclient.RpcClientConnecti
return
}
}
+ if len(cfg.SupplierSCfg().ResourceSConns) != 0 {
+ resourceSConn, err = engine.NewRPCPool(rpcclient.POOL_FIRST,
+ cfg.TlsCfg().ClientKey,
+ cfg.TlsCfg().ClientCerificate, cfg.TlsCfg().CaCertificate,
+ cfg.GeneralCfg().ConnectAttempts, cfg.GeneralCfg().Reconnects,
+ cfg.GeneralCfg().ConnectTimeout, cfg.GeneralCfg().ReplyTimeout,
+ cfg.SupplierSCfg().ResourceSConns, internalRsChan,
+ cfg.GeneralCfg().InternalTtl, false)
+ if err != nil {
+ utils.Logger.Crit(fmt.Sprintf("<%s> Could not connect to StatS: %s",
+ utils.SupplierS, err.Error()))
+ exitChan <- true
+ return
+ }
+ }
<-cacheS.GetPrecacheChannel(utils.CacheSupplierProfiles)
splS, err := engine.NewSupplierService(dm, cfg.GeneralCfg().DefaultTimezone,
diff --git a/data/conf/samples/tutmongo/cgrates.json b/data/conf/samples/tutmongo/cgrates.json
index 4aaa6ccb6..81ee3f368 100644
--- a/data/conf/samples/tutmongo/cgrates.json
+++ b/data/conf/samples/tutmongo/cgrates.json
@@ -133,7 +133,10 @@
"suppliers": {
"enabled": true,
"stats_conns": [
- {"address": "*internal"},
+ {"address": "127.0.0.1:2012", "transport":"*json"},
+ ],
+ "resources_conns": [
+ {"address": "127.0.0.1:2012", "transport":"*json"},
],
},
diff --git a/data/conf/samples/tutmysql/cgrates.json b/data/conf/samples/tutmysql/cgrates.json
index 0246f9db2..66896b054 100644
--- a/data/conf/samples/tutmysql/cgrates.json
+++ b/data/conf/samples/tutmysql/cgrates.json
@@ -256,10 +256,13 @@
"suppliers": {
"enabled": true,
- "prefix_indexed_fields":["Destination",],
+ "prefix_indexed_fields":["Destination"],
"stats_conns": [
{"address": "*internal"},
],
+ "resources_conns": [
+ {"address": "127.0.0.1:2012", "transport":"*json"},
+ ],
},
diff --git a/engine/libsuppliers.go b/engine/libsuppliers.go
index eb41e00ff..2c019273a 100644
--- a/engine/libsuppliers.go
+++ b/engine/libsuppliers.go
@@ -129,6 +129,28 @@ func (sSpls *SortedSuppliers) SortQOS(params []string) {
})
}
+// SortResourceAscendent is part of sort interface,
+// sort ascendent based on ResourceUsage with fallback on Weight
+func (sSpls *SortedSuppliers) SortResourceAscendent() {
+ sort.Slice(sSpls.SortedSuppliers, func(i, j int) bool {
+ if sSpls.SortedSuppliers[i].SortingData[utils.ResourceUsage].(float64) == sSpls.SortedSuppliers[j].SortingData[utils.ResourceUsage].(float64) {
+ return sSpls.SortedSuppliers[i].SortingData[utils.Weight].(float64) > sSpls.SortedSuppliers[j].SortingData[utils.Weight].(float64)
+ }
+ return sSpls.SortedSuppliers[i].SortingData[utils.ResourceUsage].(float64) < sSpls.SortedSuppliers[j].SortingData[utils.ResourceUsage].(float64)
+ })
+}
+
+// SortResourceDescendent is part of sort interface,
+// sort descendent based on ResourceUsage with fallback on Weight
+func (sSpls *SortedSuppliers) SortResourceDescendent() {
+ sort.Slice(sSpls.SortedSuppliers, func(i, j int) bool {
+ if sSpls.SortedSuppliers[i].SortingData[utils.ResourceUsage].(float64) == sSpls.SortedSuppliers[j].SortingData[utils.ResourceUsage].(float64) {
+ return sSpls.SortedSuppliers[i].SortingData[utils.Weight].(float64) > sSpls.SortedSuppliers[j].SortingData[utils.Weight].(float64)
+ }
+ return sSpls.SortedSuppliers[i].SortingData[utils.ResourceUsage].(float64) > sSpls.SortedSuppliers[j].SortingData[utils.ResourceUsage].(float64)
+ })
+}
+
// Digest returns list of supplierIDs + parameters for easier outside access
// format suppl1:suppl1params,suppl2:suppl2params
func (sSpls *SortedSuppliers) Digest() string {
@@ -152,6 +174,8 @@ func NewSupplierSortDispatcher(lcrS *SupplierService) (ssd SupplierSortDispatche
ssd[utils.MetaLeastCost] = NewLeastCostSorter(lcrS)
ssd[utils.MetaHighestCost] = NewHighestCostSorter(lcrS)
ssd[utils.MetaQOS] = NewQOSSupplierSorter(lcrS)
+ ssd[utils.MetaReas] = NewResourceAscendetSorter(lcrS)
+ ssd[utils.MetaReds] = NewResourceDescendentSorter(lcrS)
return
}
diff --git a/engine/spls_reas.go b/engine/spls_reas.go
new file mode 100644
index 000000000..a51acfe8d
--- /dev/null
+++ b/engine/spls_reas.go
@@ -0,0 +1,58 @@
+/*
+Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
+Copyright (C) ITsysCOM GmbH
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see
+*/
+
+package engine
+
+import (
+ "fmt"
+
+ "github.com/cgrates/cgrates/utils"
+)
+
+func NewResourceAscendetSorter(spS *SupplierService) *ResourceAscendentSorter {
+ return &ResourceAscendentSorter{spS: spS,
+ sorting: utils.MetaReas}
+}
+
+// ResourceAscendentSorter orders suppliers based on their Resource Usage
+type ResourceAscendentSorter struct {
+ sorting string
+ spS *SupplierService
+}
+
+func (ws *ResourceAscendentSorter) SortSuppliers(prflID string,
+ suppls []*Supplier, suplEv *utils.CGREvent, extraOpts *optsGetSuppliers) (sortedSuppls *SortedSuppliers, err error) {
+ sortedSuppls = &SortedSuppliers{ProfileID: prflID,
+ Sorting: ws.sorting,
+ SortedSuppliers: make([]*SortedSupplier, 0)}
+ for _, s := range suppls {
+ if len(s.ResourceIDs) == 0 {
+ utils.Logger.Warning(
+ fmt.Sprintf("<%s> supplier: <%s> - empty ResourceIDs",
+ utils.SupplierS, s.ID))
+ return nil, utils.NewErrMandatoryIeMissing("ResourceIDs")
+ }
+ if srtSpl, pass, err := ws.spS.populateSortingData(suplEv, s, extraOpts); err != nil {
+ return nil, err
+ } else if pass && srtSpl != nil {
+ sortedSuppls.SortedSuppliers = append(sortedSuppls.SortedSuppliers, srtSpl)
+ }
+ }
+ sortedSuppls.SortResourceAscendent()
+ return
+}
diff --git a/engine/spls_reds.go b/engine/spls_reds.go
new file mode 100644
index 000000000..726df7839
--- /dev/null
+++ b/engine/spls_reds.go
@@ -0,0 +1,58 @@
+/*
+Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
+Copyright (C) ITsysCOM GmbH
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see
+*/
+
+package engine
+
+import (
+ "fmt"
+
+ "github.com/cgrates/cgrates/utils"
+)
+
+func NewResourceDescendentSorter(spS *SupplierService) *ResourceDescendentSorter {
+ return &ResourceDescendentSorter{spS: spS,
+ sorting: utils.MetaReds}
+}
+
+// ResourceAscendentSorter orders suppliers based on their Resource Usage
+type ResourceDescendentSorter struct {
+ sorting string
+ spS *SupplierService
+}
+
+func (ws *ResourceDescendentSorter) SortSuppliers(prflID string,
+ suppls []*Supplier, suplEv *utils.CGREvent, extraOpts *optsGetSuppliers) (sortedSuppls *SortedSuppliers, err error) {
+ sortedSuppls = &SortedSuppliers{ProfileID: prflID,
+ Sorting: ws.sorting,
+ SortedSuppliers: make([]*SortedSupplier, 0)}
+ for _, s := range suppls {
+ if len(s.ResourceIDs) == 0 {
+ utils.Logger.Warning(
+ fmt.Sprintf("<%s> supplier: <%s> - empty ResourceIDs",
+ utils.SupplierS, s.ID))
+ return nil, utils.NewErrMandatoryIeMissing("ResourceIDs")
+ }
+ if srtSpl, pass, err := ws.spS.populateSortingData(suplEv, s, extraOpts); err != nil {
+ return nil, err
+ } else if pass && srtSpl != nil {
+ sortedSuppls.SortedSuppliers = append(sortedSuppls.SortedSuppliers, srtSpl)
+ }
+ }
+ sortedSuppls.SortResourceDescendent()
+ return
+}
diff --git a/engine/suppliers.go b/engine/suppliers.go
index b408bd900..2dce35ac4 100644
--- a/engine/suppliers.go
+++ b/engine/suppliers.go
@@ -258,7 +258,7 @@ func (spS *SupplierService) statMetrics(statIDs []string, tenant string) (stsMet
if spS.statS != nil {
for _, statID := range statIDs {
var metrics map[string]float64
- if err := spS.statS.Call(utils.StatSv1GetQueueFloatMetrics,
+ if err = spS.statS.Call(utils.StatSv1GetQueueFloatMetrics,
&utils.TenantID{Tenant: tenant, ID: statID}, &metrics); err != nil &&
err.Error() != utils.ErrNotFound.Error() {
utils.Logger.Warning(
@@ -273,7 +273,19 @@ func (spS *SupplierService) statMetrics(statIDs []string, tenant string) (stsMet
}
// resourceUsage returns sum of all resource usages out of list
-func (spS *SupplierService) resourceUsage(resIDs []string) (tUsage float64, err error) {
+func (spS *SupplierService) resourceUsage(resIDs []string, tenant string) (tUsage float64, err error) {
+ if spS.resourceS != nil {
+ for _, resID := range resIDs {
+ var res *Resource
+ if err = spS.resourceS.Call(utils.ApierV1GetResource,
+ &utils.TenantID{Tenant: tenant, ID: resID}, &res); err != nil &&
+ err.Error() != utils.ErrNotFound.Error() {
+ utils.Logger.Warning(
+ fmt.Sprintf(" error: %s getting resource for ID : %s", err.Error(), resID))
+ }
+ tUsage += res.totalUsage()
+ }
+ }
return
}
@@ -343,6 +355,21 @@ func (spS *SupplierService) populateSortingData(ev *utils.CGREvent, spl *Supplie
}
}
}
+ //calculate resourceUsage
+ if len(spl.ResourceIDs) != 0 {
+ resTotalUsage, err := spS.resourceUsage(spl.ResourceIDs, ev.Tenant)
+ if err != nil {
+ if extraOpts.ignoreErrors {
+ utils.Logger.Warning(
+ fmt.Sprintf("<%s> ignoring supplier with ID: %s, err: %s",
+ utils.SupplierS, spl.ID, err.Error()))
+ return nil, false, nil
+ } else {
+ return nil, false, err
+ }
+ }
+ sortedSpl.SortingData[utils.ResourceUsage] = resTotalUsage
+ }
//filter the supplier
if len(spl.FilterIDs) != 0 {
//construct the DP and pass it to filterS
diff --git a/general_tests/supplier_it_test.go b/general_tests/supplier_it_test.go
index cdf1bc1ea..0720f1a1f 100644
--- a/general_tests/supplier_it_test.go
+++ b/general_tests/supplier_it_test.go
@@ -49,6 +49,14 @@ var sTestsSupplierSV1 = []func(t *testing.T){
testV1SplSRpcConn,
testV1SplSFromFolder,
testV1SplSSetSupplierProfilesWithoutRatingPlanIDs,
+ //tests for *reas sorting strategy
+ testV1SplSAddNewSplPrf,
+ testV1SplSAddNewResPrf,
+ testV1SplSPopulateResUsage,
+ testV1SplSGetSortedSuppliers,
+ //tests for *reds sorting strategy
+ testV1SplSAddNewSplPrf2,
+ testV1SplSGetSortedSuppliers2,
testV1SplSStopEngine,
}
@@ -165,6 +173,351 @@ func testV1SplSSetSupplierProfilesWithoutRatingPlanIDs(t *testing.T) {
}
}
+func testV1SplSAddNewSplPrf(t *testing.T) {
+ var reply *engine.SupplierProfile
+ if err := splSv1Rpc.Call("ApierV1.GetSupplierProfile",
+ &utils.TenantID{Tenant: "cgrates.org", ID: "SPL_ResourceTest"}, &reply); err == nil ||
+ err.Error() != utils.ErrNotFound.Error() {
+ t.Error(err)
+ }
+ //create a new Supplier Profile to test *reas and *reds sorting strategy
+ splPrf = &engine.SupplierProfile{
+ Tenant: "cgrates.org",
+ ID: "SPL_ResourceTest",
+ Sorting: utils.MetaReas,
+ FilterIDs: []string{"*string:CustomField:ResourceTest"},
+ Suppliers: []*engine.Supplier{
+ //supplier1 will have ResourceUsage = 11
+ {
+ ID: "supplier1",
+ ResourceIDs: []string{"ResourceSupplier1", "Resource2Supplier1"},
+ Weight: 20,
+ Blocker: false,
+ },
+ //supplier2 and supplier3 will have the same ResourceUsage = 7
+ {
+ ID: "supplier2",
+ ResourceIDs: []string{"ResourceSupplier2"},
+ Weight: 20,
+ Blocker: false,
+ },
+ {
+ ID: "supplier3",
+ ResourceIDs: []string{"ResourceSupplier3"},
+ Weight: 35,
+ Blocker: false,
+ },
+ },
+ Weight: 10,
+ }
+ var result string
+ if err := splSv1Rpc.Call("ApierV1.SetSupplierProfile", splPrf, &result); err != nil {
+ t.Error(err)
+ } else if result != utils.OK {
+ t.Error("Unexpected reply returned", result)
+ }
+ if err := splSv1Rpc.Call("ApierV1.GetSupplierProfile",
+ &utils.TenantID{Tenant: "cgrates.org", ID: "SPL_ResourceTest"}, &reply); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(splPrf, reply) {
+ t.Errorf("Expecting: %+v, received: %+v", splPrf, reply)
+ }
+}
+
+func testV1SplSAddNewResPrf(t *testing.T) {
+ var result string
+ //add ResourceSupplier1
+ rPrf := &engine.ResourceProfile{
+ Tenant: "cgrates.org",
+ ID: "ResourceSupplier1",
+ FilterIDs: []string{"*string:Supplier:supplier1", "*string:ResID:ResourceSupplier1"},
+ ActivationInterval: &utils.ActivationInterval{
+ ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC),
+ ExpiryTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC),
+ },
+ UsageTTL: time.Duration(1) * time.Minute,
+ Limit: 10,
+ Stored: true,
+ Weight: 20,
+ ThresholdIDs: []string{utils.META_NONE},
+ }
+
+ if err := splSv1Rpc.Call("ApierV1.SetResourceProfile", rPrf, &result); err != nil {
+ t.Error(err)
+ } else if result != utils.OK {
+ t.Error("Unexpected reply returned", result)
+ }
+ //add Resource2Supplier1
+ rPrf2 := &engine.ResourceProfile{
+ Tenant: "cgrates.org",
+ ID: "Resource2Supplier1",
+ FilterIDs: []string{"*string:Supplier:supplier1", "*string:ResID:Resource2Supplier1"},
+ ActivationInterval: &utils.ActivationInterval{
+ ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC),
+ ExpiryTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC),
+ },
+ UsageTTL: time.Duration(1) * time.Minute,
+ Limit: 10,
+ Stored: true,
+ Weight: 30,
+ ThresholdIDs: []string{utils.META_NONE},
+ }
+
+ if err := splSv1Rpc.Call("ApierV1.SetResourceProfile", rPrf2, &result); err != nil {
+ t.Error(err)
+ } else if result != utils.OK {
+ t.Error("Unexpected reply returned", result)
+ }
+ //add ResourceSupplier2
+ rPrf3 := &engine.ResourceProfile{
+ Tenant: "cgrates.org",
+ ID: "ResourceSupplier2",
+ FilterIDs: []string{"*string:Supplier:supplier2", "*string:ResID:ResourceSupplier2"},
+ ActivationInterval: &utils.ActivationInterval{
+ ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC),
+ ExpiryTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC),
+ },
+ UsageTTL: time.Duration(1) * time.Minute,
+ Limit: 10,
+ Stored: true,
+ Weight: 20,
+ ThresholdIDs: []string{utils.META_NONE},
+ }
+
+ if err := splSv1Rpc.Call("ApierV1.SetResourceProfile", rPrf3, &result); err != nil {
+ t.Error(err)
+ } else if result != utils.OK {
+ t.Error("Unexpected reply returned", result)
+ }
+ //add ResourceSupplier2
+ rPrf4 := &engine.ResourceProfile{
+ Tenant: "cgrates.org",
+ ID: "ResourceSupplier3",
+ FilterIDs: []string{"*string:Supplier:supplier3", "*string:ResID:ResourceSupplier3"},
+ ActivationInterval: &utils.ActivationInterval{
+ ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC),
+ ExpiryTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC),
+ },
+ UsageTTL: time.Duration(1) * time.Minute,
+ Limit: 10,
+ Stored: true,
+ Weight: 20,
+ ThresholdIDs: []string{utils.META_NONE},
+ }
+
+ if err := splSv1Rpc.Call("ApierV1.SetResourceProfile", rPrf4, &result); err != nil {
+ t.Error(err)
+ } else if result != utils.OK {
+ t.Error("Unexpected reply returned", result)
+ }
+}
+
+func testV1SplSPopulateResUsage(t *testing.T) {
+ var reply string
+ argsRU := utils.ArgRSv1ResourceUsage{
+ UsageID: "RandomID",
+ CGREvent: utils.CGREvent{
+ Tenant: "cgrates.org",
+ ID: "Event1",
+ Event: map[string]interface{}{
+ "Account": "1002",
+ "Supplier": "supplier1",
+ "ResID": "ResourceSupplier1",
+ },
+ },
+ Units: 4,
+ }
+ if err := splSv1Rpc.Call(utils.ResourceSv1AllocateResources,
+ argsRU, &reply); err != nil {
+ t.Error(err)
+ }
+ eAllocationMsg := "ResourceSupplier1"
+ if reply != eAllocationMsg {
+ t.Errorf("Expecting: %+v, received: %+v", eAllocationMsg, reply)
+ }
+
+ argsRU = utils.ArgRSv1ResourceUsage{
+ UsageID: "RandomID2",
+
+ CGREvent: utils.CGREvent{
+ Tenant: "cgrates.org",
+ ID: "Event2",
+ Event: map[string]interface{}{
+ "Account": "1002",
+ "Supplier": "supplier1",
+ "ResID": "Resource2Supplier1",
+ },
+ },
+ Units: 7,
+ }
+ if err := splSv1Rpc.Call(utils.ResourceSv1AllocateResources,
+ argsRU, &reply); err != nil {
+ t.Error(err)
+ }
+ eAllocationMsg = "Resource2Supplier1"
+ if reply != eAllocationMsg {
+ t.Errorf("Expecting: %+v, received: %+v", eAllocationMsg, reply)
+ }
+
+ argsRU = utils.ArgRSv1ResourceUsage{
+ UsageID: "RandomID3",
+ CGREvent: utils.CGREvent{
+ Tenant: "cgrates.org",
+ ID: "Event3",
+ Event: map[string]interface{}{
+ "Account": "1002",
+ "Supplier": "supplier2",
+ "ResID": "ResourceSupplier2",
+ },
+ },
+ Units: 7,
+ }
+ if err := splSv1Rpc.Call(utils.ResourceSv1AllocateResources,
+ argsRU, &reply); err != nil {
+ t.Error(err)
+ }
+ eAllocationMsg = "ResourceSupplier2"
+ if reply != eAllocationMsg {
+ t.Errorf("Expecting: %+v, received: %+v", eAllocationMsg, reply)
+ }
+
+ argsRU = utils.ArgRSv1ResourceUsage{
+ UsageID: "RandomID4",
+ CGREvent: utils.CGREvent{
+ Tenant: "cgrates.org",
+ ID: "Event4",
+ Event: map[string]interface{}{
+ "Account": "1002",
+ "Supplier": "supplier3",
+ "ResID": "ResourceSupplier3",
+ },
+ },
+ Units: 7,
+ }
+ if err := splSv1Rpc.Call(utils.ResourceSv1AllocateResources,
+ argsRU, &reply); err != nil {
+ t.Error(err)
+ }
+ eAllocationMsg = "ResourceSupplier3"
+ if reply != eAllocationMsg {
+ t.Errorf("Expecting: %+v, received: %+v", eAllocationMsg, reply)
+ }
+
+}
+
+func testV1SplSGetSortedSuppliers(t *testing.T) {
+ ev := &engine.ArgsGetSuppliers{
+ CGREvent: utils.CGREvent{
+ Tenant: "cgrates.org",
+ ID: "testV1SplSGetSortedSuppliers",
+ Event: map[string]interface{}{
+ "CustomField": "ResourceTest",
+ },
+ },
+ }
+ expSupplierIDs := []string{"supplier3", "supplier2", "supplier1"}
+ var suplsReply engine.SortedSuppliers
+ if err := splSv1Rpc.Call(utils.SupplierSv1GetSuppliers,
+ ev, &suplsReply); err != nil {
+ t.Error(err)
+ } else {
+ rcvSupl := make([]string, len(suplsReply.SortedSuppliers))
+ for i, supl := range suplsReply.SortedSuppliers {
+ rcvSupl[i] = supl.SupplierID
+ }
+ if suplsReply.ProfileID != "SPL_ResourceTest" {
+ t.Errorf("Expecting: SPL_ResourceTest, received: %s",
+ suplsReply.ProfileID)
+ }
+ if !reflect.DeepEqual(rcvSupl, expSupplierIDs) {
+ t.Errorf("Expecting: %+v, \n received: %+v",
+ expSupplierIDs, utils.ToJSON(suplsReply))
+ }
+ }
+}
+
+func testV1SplSAddNewSplPrf2(t *testing.T) {
+ var reply *engine.SupplierProfile
+ if err := splSv1Rpc.Call("ApierV1.GetSupplierProfile",
+ &utils.TenantID{Tenant: "cgrates.org", ID: "SPL_ResourceDescendent"}, &reply); err == nil ||
+ err.Error() != utils.ErrNotFound.Error() {
+ t.Error(err)
+ }
+ //create a new Supplier Profile to test *reas and *reds sorting strategy
+ splPrf = &engine.SupplierProfile{
+ Tenant: "cgrates.org",
+ ID: "SPL_ResourceDescendent",
+ Sorting: utils.MetaReds,
+ FilterIDs: []string{"*string:CustomField:ResourceDescendent"},
+ Suppliers: []*engine.Supplier{
+ //supplier1 will have ResourceUsage = 11
+ {
+ ID: "supplier1",
+ ResourceIDs: []string{"ResourceSupplier1", "Resource2Supplier1"},
+ Weight: 20,
+ Blocker: false,
+ },
+ //supplier2 and supplier3 will have the same ResourceUsage = 7
+ {
+ ID: "supplier2",
+ ResourceIDs: []string{"ResourceSupplier2"},
+ Weight: 20,
+ Blocker: false,
+ },
+ {
+ ID: "supplier3",
+ ResourceIDs: []string{"ResourceSupplier3"},
+ Weight: 35,
+ Blocker: false,
+ },
+ },
+ Weight: 10,
+ }
+ var result string
+ if err := splSv1Rpc.Call("ApierV1.SetSupplierProfile", splPrf, &result); err != nil {
+ t.Error(err)
+ } else if result != utils.OK {
+ t.Error("Unexpected reply returned", result)
+ }
+ if err := splSv1Rpc.Call("ApierV1.GetSupplierProfile",
+ &utils.TenantID{Tenant: "cgrates.org", ID: "SPL_ResourceDescendent"}, &reply); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(splPrf, reply) {
+ t.Errorf("Expecting: %+v, received: %+v", splPrf, reply)
+ }
+}
+
+func testV1SplSGetSortedSuppliers2(t *testing.T) {
+ ev := &engine.ArgsGetSuppliers{
+ CGREvent: utils.CGREvent{
+ Tenant: "cgrates.org",
+ ID: "testV1SplSGetSortedSuppliers2",
+ Event: map[string]interface{}{
+ "CustomField": "ResourceDescendent",
+ },
+ },
+ }
+ expSupplierIDs := []string{"supplier1", "supplier3", "supplier2"}
+ var suplsReply engine.SortedSuppliers
+ if err := splSv1Rpc.Call(utils.SupplierSv1GetSuppliers,
+ ev, &suplsReply); err != nil {
+ t.Error(err)
+ } else {
+ rcvSupl := make([]string, len(suplsReply.SortedSuppliers))
+ for i, supl := range suplsReply.SortedSuppliers {
+ rcvSupl[i] = supl.SupplierID
+ }
+ if suplsReply.ProfileID != "SPL_ResourceDescendent" {
+ t.Errorf("Expecting: SPL_ResourceDescendent, received: %s",
+ suplsReply.ProfileID)
+ }
+ if !reflect.DeepEqual(rcvSupl, expSupplierIDs) {
+ t.Errorf("Expecting: %+v, \n received: %+v",
+ expSupplierIDs, utils.ToJSON(suplsReply))
+ }
+ }
+}
+
func testV1SplSStopEngine(t *testing.T) {
if err := engine.KillEngine(100); err != nil {
t.Error(err)
diff --git a/utils/consts.go b/utils/consts.go
index 2c1fa8eaa..0154b9178 100755
--- a/utils/consts.go
+++ b/utils/consts.go
@@ -548,6 +548,7 @@ const (
CGRDebitInterval = "CGRDebitInterval"
Version = "Version"
MetaTenant = "*tenant"
+ ResourceUsage = "ResourceUsage"
)
// Migrator Action