From 7ae16a20d588a2ae6fcf63859fc497336f240475 Mon Sep 17 00:00:00 2001 From: TeoV Date: Wed, 27 Feb 2019 13:41:40 +0200 Subject: [PATCH] Add *reas/*reds strategy for SupplierS (+tests) --- cmd/cgr-engine/cgr-engine.go | 15 + data/conf/samples/tutmongo/cgrates.json | 5 +- data/conf/samples/tutmysql/cgrates.json | 5 +- engine/libsuppliers.go | 24 ++ engine/spls_reas.go | 58 ++++ engine/spls_reds.go | 58 ++++ engine/suppliers.go | 31 ++- general_tests/supplier_it_test.go | 353 ++++++++++++++++++++++++ utils/consts.go | 1 + 9 files changed, 546 insertions(+), 4 deletions(-) create mode 100644 engine/spls_reas.go create mode 100644 engine/spls_reds.go 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