Update Suppliers stategy

This commit is contained in:
TeoV
2018-07-31 08:24:07 -04:00
committed by Dan Christian Bogos
parent 55bcc28169
commit 909358a124
13 changed files with 322 additions and 252 deletions

View File

@@ -58,6 +58,7 @@ var sTestsSupplierSV1 = []func(t *testing.T){
testV1SplSGetQOSSuppliers,
testV1SplSGetQOSSuppliers2,
testV1SplSGetQOSSuppliers3,
testV1SplSGetQOSSuppliersFiltred,
testV1SplSGetSupplierWithoutFilter,
testV1SplSSetSupplierProfiles,
testV1SplSUpdateSupplierProfiles,
@@ -673,6 +674,53 @@ func testV1SplSGetQOSSuppliers3(t *testing.T) {
}
}
func testV1SplSGetQOSSuppliersFiltred(t *testing.T) {
ev := &engine.ArgsGetSuppliers{
CGREvent: utils.CGREvent{
Tenant: "cgrates.org",
ID: "testV1SplSGetQOSSuppliers",
Event: map[string]interface{}{
"DistincMatch": "*qos_filtred",
},
},
}
eSpls := engine.SortedSuppliers{
ProfileID: "SPL_QOS_FILTRED",
Sorting: utils.MetaQOS,
SortedSuppliers: []*engine.SortedSupplier{
&engine.SortedSupplier{
SupplierID: "supplier1",
SortingData: map[string]interface{}{
"*acd:Stat_1": 11.0,
"*acd:Stat_1_1": 11.0,
"*asr:Stat_1": 100.0,
"*pdd:Stat_1_1": 12.0,
"*tcd:Stat_1": 22.0,
"*tcd:Stat_1_1": 11.0,
utils.Weight: 10.0,
},
},
&engine.SortedSupplier{
SupplierID: "supplier3",
SortingData: map[string]interface{}{
"*acd:Stat_3": 11.0,
"*asr:Stat_3": 100.0,
"*tcd:Stat_3": 11.0,
utils.Weight: 35.0,
},
},
},
}
var suplsReply engine.SortedSuppliers
if err := splSv1Rpc.Call(utils.SupplierSv1GetSuppliers,
ev, &suplsReply); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eSpls, suplsReply) {
t.Errorf("Expecting: %s, received: %s",
utils.ToJSON(eSpls), utils.ToJSON(suplsReply))
}
}
func testV1SplSGetSupplierWithoutFilter(t *testing.T) {
ev := &engine.ArgsGetSuppliers{
CGREvent: utils.CGREvent{

View File

@@ -6,6 +6,7 @@ cgrates.org,FLTR_1,*prefix,Destination,10;20,
cgrates.org,FLTR_1,*rsr,,Subject(~^1.*1$);Destination(1002),
cgrates.org,FLTR_ACNT_1007,*string,Account,1007,2014-07-29T15:00:00Z
cgrates.org,FLTR_ACNT_dan,*string,Account,dan,2014-07-29T15:00:00Z
cgrates.org,FLTR_SPP_ACNT_dan,*string,*req.Account,dan,2014-07-29T15:00:00Z
cgrates.org,FLTR_SPP_2,*string,Account,1003;1002,2014-07-29T15:00:00Z
cgrates.org,FLTR_SPP_2,*prefix,Destination,10;20,
cgrates.org,FLTR_SPP_2,*rsr,,Subject(~^1.*1$);Destination(1002),
@@ -16,4 +17,8 @@ cgrates.org,FLTR_STAT_2,*string,Account,1002,2014-07-29T15:00:00Z
cgrates.org,FLTR_STAT_3,*string,Account,1003,2014-07-29T15:00:00Z
cgrates.org,FLTR_SPP_4,*string,DistincMatch,*qos2,2014-07-29T15:00:00Z
cgrates.org,FLTR_SPP_5,*string,DistincMatch,*qos3,2014-07-29T15:00:00Z
cgrates.org,FLTR_STAT_1_1,*string,Stat,Stat1_1,2014-07-29T15:00:00Z
cgrates.org,FLTR_STAT_1_1,*string,Stat,Stat1_1,2014-07-29T15:00:00Z
cgrates.org,FLTR_SPP_6,*string,DistincMatch,*qos_filtred,2014-07-29T15:00:00Z
cgrates.org,FLTR_QOS_SP1,*gte,*gs.*acd,10.0,2014-07-29T15:00:00Z
cgrates.org,FLTR_QOS_SP2,*gte,*gs.*acd,10.0,2014-07-29T15:00:00Z
cgrates.org,FLTR_QOS_SP2,*gte,*gs.*tcd,11.0,
1 #Tenant[0] ID[1] FilterType[2] FilterFieldName[3] FilterFieldValues[4] ActivationInterval[5]
6 cgrates.org FLTR_1 *rsr Subject(~^1.*1$);Destination(1002)
7 cgrates.org FLTR_ACNT_1007 *string Account 1007 2014-07-29T15:00:00Z
8 cgrates.org FLTR_ACNT_dan *string Account dan 2014-07-29T15:00:00Z
9 cgrates.org FLTR_SPP_ACNT_dan *string *req.Account dan 2014-07-29T15:00:00Z
10 cgrates.org FLTR_SPP_2 *string Account 1003;1002 2014-07-29T15:00:00Z
11 cgrates.org FLTR_SPP_2 *prefix Destination 10;20
12 cgrates.org FLTR_SPP_2 *rsr Subject(~^1.*1$);Destination(1002)
17 cgrates.org FLTR_STAT_3 *string Account 1003 2014-07-29T15:00:00Z
18 cgrates.org FLTR_SPP_4 *string DistincMatch *qos2 2014-07-29T15:00:00Z
19 cgrates.org FLTR_SPP_5 *string DistincMatch *qos3 2014-07-29T15:00:00Z
20 cgrates.org FLTR_STAT_1_1 *string Stat Stat1_1 2014-07-29T15:00:00Z
21 cgrates.org FLTR_SPP_6 *string DistincMatch *qos_filtred 2014-07-29T15:00:00Z
22 cgrates.org FLTR_QOS_SP1 *gte *gs.*acd 10.0 2014-07-29T15:00:00Z
23 cgrates.org FLTR_QOS_SP2 *gte *gs.*acd 10.0 2014-07-29T15:00:00Z
24 cgrates.org FLTR_QOS_SP2 *gte *gs.*tcd 11.0

View File

@@ -4,7 +4,7 @@ cgrates.org,SPL_ACNT_1001,,,,,supplier2,,,,,,10,,,
cgrates.org,SPL_WEIGHT_2,,2017-11-27T00:00:00Z,*weight,,supplier1,,,,,,10,,,5
cgrates.org,SPL_WEIGHT_1,FLTR_DST_DE;FLTR_ACNT_1007,2017-11-27T00:00:00Z,*weight,,supplier1,,,,,,10,,,10
cgrates.org,SPL_WEIGHT_1,FLTR_DST_DE,,,,supplier2,,,,,,20,,,
cgrates.org,SPL_WEIGHT_1,FLTR_ACNT_1007,,,,supplier3,FLTR_ACNT_dan,,,,,15,,,
cgrates.org,SPL_WEIGHT_1,FLTR_ACNT_1007,,,,supplier3,FLTR_SPP_ACNT_dan,,,,,15,,,
cgrates.org,SPL_LEASTCOST_1,FLTR_1,2017-11-27T00:00:00Z,*least_cost,,supplier1,,,RP_SPECIAL_1002,,,10,false,,10
cgrates.org,SPL_LEASTCOST_1,,,,,supplier2,,,RP_RETAIL1,,,20,,,
cgrates.org,SPL_LEASTCOST_1,,,,,supplier3,,,RP_SPECIAL_1002,,,15,,,
@@ -20,3 +20,6 @@ cgrates.org,SPL_QOS_2,,,,,supplier3,,,,,Stat_3,35,,,
cgrates.org,SPL_QOS_3,FLTR_SPP_5,2017-11-27T00:00:00Z,*qos,*pdd,supplier1,,,,,Stat_1;Stat_1_1,10,false,,10
cgrates.org,SPL_QOS_3,,,,,supplier2,,,,,Stat_2,20,,,
cgrates.org,SPL_QOS_3,,,,,supplier3,,,,,Stat_3,35,,,
cgrates.org,SPL_QOS_FILTRED,FLTR_SPP_6,2017-11-27T00:00:00Z,*qos,*pdd,supplier1,FLTR_QOS_SP1,,,,Stat_1;Stat_1_1,10,false,,10
cgrates.org,SPL_QOS_FILTRED,,,,,supplier2,FLTR_QOS_SP2,,,,Stat_2,20,,,
cgrates.org,SPL_QOS_FILTRED,,,,,supplier3,,,,,Stat_3,35,,,
1 #Tenant ID FilterIDs ActivationInterval Sorting SortingParameters SupplierID SupplierFilterIDs SupplierAccountIDs SupplierRatingPlanIDs SupplierResourceIDs SupplierStatIDs SupplierWeight SupplierBlocker SupplierParameters Weight
4 cgrates.org SPL_WEIGHT_2 2017-11-27T00:00:00Z *weight supplier1 10 5
5 cgrates.org SPL_WEIGHT_1 FLTR_DST_DE;FLTR_ACNT_1007 2017-11-27T00:00:00Z *weight supplier1 10 10
6 cgrates.org SPL_WEIGHT_1 FLTR_DST_DE supplier2 20
7 cgrates.org SPL_WEIGHT_1 FLTR_ACNT_1007 supplier3 FLTR_ACNT_dan FLTR_SPP_ACNT_dan 15
8 cgrates.org SPL_LEASTCOST_1 FLTR_1 2017-11-27T00:00:00Z *least_cost supplier1 RP_SPECIAL_1002 10 false 10
9 cgrates.org SPL_LEASTCOST_1 supplier2 RP_RETAIL1 20
10 cgrates.org SPL_LEASTCOST_1 supplier3 RP_SPECIAL_1002 15
20 cgrates.org SPL_QOS_3 FLTR_SPP_5 2017-11-27T00:00:00Z *qos *pdd supplier1 Stat_1;Stat_1_1 10 false 10
21 cgrates.org SPL_QOS_3 supplier2 Stat_2 20
22 cgrates.org SPL_QOS_3 supplier3 Stat_3 35
23 cgrates.org SPL_QOS_FILTRED FLTR_SPP_6 2017-11-27T00:00:00Z *qos *pdd supplier1 FLTR_QOS_SP1 Stat_1;Stat_1_1 10 false 10
24 cgrates.org SPL_QOS_FILTRED supplier2 FLTR_QOS_SP2 Stat_2 20
25 cgrates.org SPL_QOS_FILTRED supplier3 Stat_3 35

View File

@@ -31,7 +31,7 @@ type SortedSupplier struct {
SupplierID string
SupplierParameters string
SortingData map[string]interface{} // store here extra info like cost or stats
worstStats map[string]float64
globalStats map[string]float64
}
// SuppliersReply is returned as part of GetSuppliers call
@@ -97,21 +97,21 @@ func (sSpls *SortedSuppliers) SortQOS(params []string) {
sort.Slice(sSpls.SortedSuppliers, func(i, j int) bool {
for _, param := range params {
// skip to next param
if sSpls.SortedSuppliers[i].worstStats[param] == sSpls.SortedSuppliers[j].worstStats[param] {
if sSpls.SortedSuppliers[i].globalStats[param] == sSpls.SortedSuppliers[j].globalStats[param] {
continue
}
if (param != utils.MetaPDD && sSpls.SortedSuppliers[i].worstStats[param] == -1) ||
(param == utils.MetaPDD && sSpls.SortedSuppliers[i].worstStats[param] == 1000000) {
if (param != utils.MetaPDD && sSpls.SortedSuppliers[i].globalStats[param] == -1) ||
(param == utils.MetaPDD && sSpls.SortedSuppliers[i].globalStats[param] == 1000000) {
return false
}
switch param {
default:
return sSpls.SortedSuppliers[i].worstStats[param] > sSpls.SortedSuppliers[j].worstStats[param]
return sSpls.SortedSuppliers[i].globalStats[param] > sSpls.SortedSuppliers[j].globalStats[param]
case utils.MetaPDD:
return sSpls.SortedSuppliers[i].worstStats[param] < sSpls.SortedSuppliers[j].worstStats[param]
return sSpls.SortedSuppliers[i].globalStats[param] < sSpls.SortedSuppliers[j].globalStats[param]
}
}
return sSpls.SortedSuppliers[i].worstStats[utils.Weight] > sSpls.SortedSuppliers[j].worstStats[utils.Weight]
return sSpls.SortedSuppliers[i].globalStats[utils.Weight] > sSpls.SortedSuppliers[j].globalStats[utils.Weight]
})
}
@@ -134,7 +134,7 @@ type SuppliersSorter interface {
// NewSupplierSortDispatcher constructs SupplierSortDispatcher
func NewSupplierSortDispatcher(lcrS *SupplierService) (ssd SupplierSortDispatcher, err error) {
ssd = make(map[string]SuppliersSorter)
ssd[utils.MetaWeight] = NewWeightSorter()
ssd[utils.MetaWeight] = NewWeightSorter(lcrS)
ssd[utils.MetaLeastCost] = NewLeastCostSorter(lcrS)
ssd[utils.MetaHighestCost] = NewHighestCostSorter(lcrS)
ssd[utils.MetaQOS] = NewQOSSupplierSorter(lcrS)
@@ -153,27 +153,3 @@ func (ssd SupplierSortDispatcher) SortSuppliers(prflID, strategy string,
}
return sd.SortSuppliers(prflID, suppls, suplEv, extraOpts)
}
func NewWeightSorter() *WeightSorter {
return &WeightSorter{sorting: utils.MetaWeight}
}
// WeightSorter orders suppliers based on their weight, no cost involved
type WeightSorter struct {
sorting string
}
func (ws *WeightSorter) SortSuppliers(prflID string,
suppls []*Supplier, suplEv *utils.CGREvent, extraOpts *optsGetSuppliers) (sortedSuppls *SortedSuppliers, err error) {
sortedSuppls = &SortedSuppliers{ProfileID: prflID,
Sorting: ws.sorting,
SortedSuppliers: make([]*SortedSupplier, len(suppls))}
for i, s := range suppls {
sortedSuppls.SortedSuppliers[i] = &SortedSupplier{
SupplierID: s.ID,
SortingData: map[string]interface{}{utils.Weight: s.Weight},
SupplierParameters: s.SupplierParameters}
}
sortedSuppls.SortWeight()
return
}

View File

@@ -89,64 +89,60 @@ func TestLibSuppliersSortCost(t *testing.T) {
}
func TestLibSuppliersSortWeight(t *testing.T) {
spl := []*Supplier{
&Supplier{
ID: "supplier1",
FilterIDs: []string{},
AccountIDs: []string{},
RatingPlanIDs: []string{},
ResourceIDs: []string{},
StatIDs: []string{},
Weight: 10.0,
SupplierParameters: "param1",
},
&Supplier{
ID: "supplier2",
FilterIDs: []string{},
AccountIDs: []string{},
RatingPlanIDs: []string{},
ResourceIDs: []string{},
StatIDs: []string{},
Weight: 20.0,
SupplierParameters: "param2",
},
}
eSpls := SortedSuppliers{
ProfileID: "SPL_WEIGHT_1",
Sorting: utils.MetaWeight,
sSpls := &SortedSuppliers{
SortedSuppliers: []*SortedSupplier{
&SortedSupplier{
SupplierID: "supplier1",
SortingData: map[string]interface{}{
utils.Weight: 10.0,
},
SupplierParameters: "param1",
},
&SortedSupplier{
SupplierID: "supplier2",
SortingData: map[string]interface{}{
"Weight": 20.0,
utils.Weight: 20.0,
},
SupplierParameters: "param2",
},
&SortedSupplier{
SupplierID: "supplier3",
SortingData: map[string]interface{}{
utils.Weight: 10.5,
},
SupplierParameters: "param3",
},
},
}
sSpls.SortWeight()
eOrderedSpls := &SortedSuppliers{
SortedSuppliers: []*SortedSupplier{
&SortedSupplier{
SupplierID: "supplier2",
SortingData: map[string]interface{}{
utils.Weight: 20.0,
},
SupplierParameters: "param2",
},
&SortedSupplier{
SupplierID: "supplier3",
SortingData: map[string]interface{}{
utils.Weight: 10.5,
},
SupplierParameters: "param3",
},
&SortedSupplier{
SupplierID: "supplier1",
SortingData: map[string]interface{}{
"Weight": 10.0,
utils.Weight: 10.0,
},
SupplierParameters: "param1",
},
},
}
se := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "supplierevent1",
Event: make(map[string]interface{}),
}
ws := NewWeightSorter()
result, err := ws.SortSuppliers("SPL_WEIGHT_1", spl, se, nil)
if err != nil {
t.Error(err)
}
if !reflect.DeepEqual(eSpls.ProfileID, result.ProfileID) {
t.Errorf("Expecting: %+v, received: %+v", eSpls.ProfileID, result.ProfileID)
} else if !reflect.DeepEqual(eSpls.SortedSuppliers, result.SortedSuppliers) {
t.Errorf("Expecting: %+v, received: %+v", eSpls.SortedSuppliers, result.SortedSuppliers)
} else if !reflect.DeepEqual(eSpls.Sorting, result.Sorting) {
t.Errorf("Expecting: %+v, received: %+v", eSpls.Sorting, result.Sorting)
if !reflect.DeepEqual(eOrderedSpls, sSpls) {
t.Errorf("Expecting: %s, received: %s",
utils.ToJSON(eOrderedSpls), utils.ToJSON(sSpls))
}
}
@@ -288,7 +284,7 @@ func TestLibSuppliersSortQOS(t *testing.T) {
SortedSuppliers: []*SortedSupplier{
&SortedSupplier{
SupplierID: "supplier1",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.1,
utils.MetaTCD: 15.0,
},
@@ -296,7 +292,7 @@ func TestLibSuppliersSortQOS(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier2",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.MetaTCD: 20.0,
},
@@ -304,7 +300,7 @@ func TestLibSuppliersSortQOS(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier3",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.05,
utils.MetaTCD: 10.0,
},
@@ -317,7 +313,7 @@ func TestLibSuppliersSortQOS(t *testing.T) {
SortedSuppliers: []*SortedSupplier{
&SortedSupplier{
SupplierID: "supplier2",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.MetaTCD: 20.0,
},
@@ -325,7 +321,7 @@ func TestLibSuppliersSortQOS(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier1",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.1,
utils.MetaTCD: 15.0,
},
@@ -333,7 +329,7 @@ func TestLibSuppliersSortQOS(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier3",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.05,
utils.MetaTCD: 10.0,
},
@@ -352,7 +348,7 @@ func TestLibSuppliersSortQOS2(t *testing.T) {
SortedSuppliers: []*SortedSupplier{
&SortedSupplier{
SupplierID: "supplier1",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.MetaTCD: 15.0,
},
@@ -360,7 +356,7 @@ func TestLibSuppliersSortQOS2(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier2",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.MetaTCD: 20.0,
},
@@ -368,7 +364,7 @@ func TestLibSuppliersSortQOS2(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier3",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.1,
utils.MetaTCD: 10.0,
},
@@ -381,7 +377,7 @@ func TestLibSuppliersSortQOS2(t *testing.T) {
SortedSuppliers: []*SortedSupplier{
&SortedSupplier{
SupplierID: "supplier2",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.MetaTCD: 20.0,
},
@@ -389,7 +385,7 @@ func TestLibSuppliersSortQOS2(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier1",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.MetaTCD: 15.0,
},
@@ -397,7 +393,7 @@ func TestLibSuppliersSortQOS2(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier3",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.1,
utils.MetaTCD: 10.0,
},
@@ -416,7 +412,7 @@ func TestLibSuppliersSortQOS3(t *testing.T) {
SortedSuppliers: []*SortedSupplier{
&SortedSupplier{
SupplierID: "supplier1",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.MetaTCD: 15.0,
utils.MetaASR: 1.2,
@@ -425,7 +421,7 @@ func TestLibSuppliersSortQOS3(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier2",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.MetaTCD: 20.0,
utils.MetaASR: -1.0,
@@ -434,7 +430,7 @@ func TestLibSuppliersSortQOS3(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier3",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.1,
utils.MetaTCD: 10.0,
utils.MetaASR: 1.2,
@@ -448,7 +444,7 @@ func TestLibSuppliersSortQOS3(t *testing.T) {
SortedSuppliers: []*SortedSupplier{
&SortedSupplier{
SupplierID: "supplier1",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.MetaTCD: 15.0,
utils.MetaASR: 1.2,
@@ -457,7 +453,7 @@ func TestLibSuppliersSortQOS3(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier3",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.1,
utils.MetaTCD: 10.0,
utils.MetaASR: 1.2,
@@ -466,7 +462,7 @@ func TestLibSuppliersSortQOS3(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier2",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.MetaTCD: 20.0,
utils.MetaASR: -1.0,
@@ -486,7 +482,7 @@ func TestLibSuppliersSortQOS4(t *testing.T) {
SortedSuppliers: []*SortedSupplier{
&SortedSupplier{
SupplierID: "supplier1",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.MetaTCD: 15.0,
utils.MetaASR: -1.0,
@@ -496,7 +492,7 @@ func TestLibSuppliersSortQOS4(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier2",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.MetaTCD: 20.0,
utils.MetaASR: 1.2,
@@ -506,7 +502,7 @@ func TestLibSuppliersSortQOS4(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier3",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.1,
utils.MetaTCD: 10.0,
utils.MetaASR: 1.2,
@@ -521,7 +517,7 @@ func TestLibSuppliersSortQOS4(t *testing.T) {
SortedSuppliers: []*SortedSupplier{
&SortedSupplier{
SupplierID: "supplier2",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.MetaTCD: 20.0,
utils.MetaASR: 1.2,
@@ -531,7 +527,7 @@ func TestLibSuppliersSortQOS4(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier3",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.1,
utils.MetaTCD: 10.0,
utils.MetaASR: 1.2,
@@ -541,7 +537,7 @@ func TestLibSuppliersSortQOS4(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier1",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.MetaTCD: 15.0,
utils.MetaASR: -1.0,
@@ -562,7 +558,7 @@ func TestLibSuppliersSortQOS5(t *testing.T) {
SortedSuppliers: []*SortedSupplier{
&SortedSupplier{
SupplierID: "supplier1",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.MetaPDD: 0.5,
},
@@ -570,7 +566,7 @@ func TestLibSuppliersSortQOS5(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier2",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.MetaPDD: 0.6,
},
@@ -578,7 +574,7 @@ func TestLibSuppliersSortQOS5(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier3",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.1,
utils.MetaPDD: 0.2,
},
@@ -591,7 +587,7 @@ func TestLibSuppliersSortQOS5(t *testing.T) {
SortedSuppliers: []*SortedSupplier{
&SortedSupplier{
SupplierID: "supplier3",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.1,
utils.MetaPDD: 0.2,
},
@@ -599,7 +595,7 @@ func TestLibSuppliersSortQOS5(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier1",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.MetaPDD: 0.5,
},
@@ -607,7 +603,7 @@ func TestLibSuppliersSortQOS5(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier2",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.MetaPDD: 0.6,
},
@@ -626,7 +622,7 @@ func TestLibSuppliersSortQOS6(t *testing.T) {
SortedSuppliers: []*SortedSupplier{
&SortedSupplier{
SupplierID: "supplier1",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.Weight: 15.0,
},
@@ -634,7 +630,7 @@ func TestLibSuppliersSortQOS6(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier2",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.Weight: 25.0,
},
@@ -642,7 +638,7 @@ func TestLibSuppliersSortQOS6(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier3",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.1,
utils.Weight: 20.0,
},
@@ -655,7 +651,7 @@ func TestLibSuppliersSortQOS6(t *testing.T) {
SortedSuppliers: []*SortedSupplier{
&SortedSupplier{
SupplierID: "supplier2",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.Weight: 25.0,
},
@@ -663,7 +659,7 @@ func TestLibSuppliersSortQOS6(t *testing.T) {
},
&SortedSupplier{
SupplierID: "supplier1",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.2,
utils.Weight: 15.0,
},
@@ -672,7 +668,7 @@ func TestLibSuppliersSortQOS6(t *testing.T) {
&SortedSupplier{
SupplierID: "supplier3",
worstStats: map[string]float64{
globalStats: map[string]float64{
utils.MetaACD: 0.1,
utils.Weight: 20.0,
},

View File

@@ -19,8 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package engine
import (
"fmt"
"github.com/cgrates/cgrates/utils"
)
@@ -35,42 +33,17 @@ type HightCostSorter struct {
spS *SupplierService
}
func (lcs *HightCostSorter) SortSuppliers(prflID string, suppls []*Supplier,
func (hcs *HightCostSorter) SortSuppliers(prflID string, suppls []*Supplier,
ev *utils.CGREvent, extraOpts *optsGetSuppliers) (sortedSuppls *SortedSuppliers, err error) {
sortedSuppls = &SortedSuppliers{ProfileID: prflID,
Sorting: lcs.sorting,
Sorting: hcs.sorting,
SortedSuppliers: make([]*SortedSupplier, 0)}
for _, s := range suppls {
costData, err := lcs.spS.costForEvent(ev, s.AccountIDs, s.RatingPlanIDs)
if err != nil {
if extraOpts.ignoreErrors {
utils.Logger.Warning(
fmt.Sprintf("<%s> profile: %s ignoring supplier with ID: %s, err: %s",
utils.SupplierS, prflID, s.ID, err.Error()))
continue
}
if srtSpl, pass, err := hcs.spS.populateSortingData(ev, s, extraOpts); err != nil {
return nil, err
} else if len(costData) == 0 {
utils.Logger.Warning(
fmt.Sprintf("<%s> profile: %s ignoring supplier with ID: %s, missing cost information",
utils.SupplierS, prflID, s.ID))
continue
} else if pass && srtSpl != nil {
sortedSuppls.SortedSuppliers = append(sortedSuppls.SortedSuppliers, srtSpl)
}
if extraOpts.maxCost != 0 &&
costData[utils.Cost].(float64) > extraOpts.maxCost {
continue
}
srtData := map[string]interface{}{
utils.Weight: s.Weight,
}
for k, v := range costData {
srtData[k] = v
}
sortedSuppls.SortedSuppliers = append(sortedSuppls.SortedSuppliers,
&SortedSupplier{
SupplierID: s.ID,
SortingData: srtData,
SupplierParameters: s.SupplierParameters})
}
if len(sortedSuppls.SortedSuppliers) == 0 {
return nil, utils.ErrNotFound

View File

@@ -19,8 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package engine
import (
"fmt"
"github.com/cgrates/cgrates/utils"
)
@@ -41,36 +39,11 @@ func (lcs *LeastCostSorter) SortSuppliers(prflID string, suppls []*Supplier,
Sorting: lcs.sorting,
SortedSuppliers: make([]*SortedSupplier, 0)}
for _, s := range suppls {
costData, err := lcs.spS.costForEvent(ev, s.AccountIDs, s.RatingPlanIDs)
if err != nil {
if extraOpts.ignoreErrors {
utils.Logger.Warning(
fmt.Sprintf("<%s> profile: %s ignoring supplier with ID: %s, err: %s",
utils.SupplierS, prflID, s.ID, err.Error()))
continue
}
if srtSpl, pass, err := lcs.spS.populateSortingData(ev, s, extraOpts); err != nil {
return nil, err
} else if len(costData) == 0 {
utils.Logger.Warning(
fmt.Sprintf("<%s> profile: %s ignoring supplier with ID: %s, missing cost information",
utils.SupplierS, prflID, s.ID))
continue
} else if pass && srtSpl != nil {
sortedSuppls.SortedSuppliers = append(sortedSuppls.SortedSuppliers, srtSpl)
}
if extraOpts.maxCost != 0 &&
costData[utils.Cost].(float64) > extraOpts.maxCost {
continue
}
srtData := map[string]interface{}{
utils.Weight: s.Weight,
}
for k, v := range costData {
srtData[k] = v
}
sortedSuppls.SortedSuppliers = append(sortedSuppls.SortedSuppliers,
&SortedSupplier{
SupplierID: s.ID,
SortingData: srtData,
SupplierParameters: s.SupplierParameters})
}
if len(sortedSuppls.SortedSuppliers) == 0 {
return nil, utils.ErrNotFound

View File

@@ -19,9 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package engine
import (
"fmt"
"strings"
"github.com/cgrates/cgrates/utils"
)
@@ -36,62 +33,17 @@ type QOSSupplierSorter struct {
spS *SupplierService
}
func (lcs *QOSSupplierSorter) SortSuppliers(prflID string, suppls []*Supplier,
func (qos *QOSSupplierSorter) SortSuppliers(prflID string, suppls []*Supplier,
ev *utils.CGREvent, extraOpts *optsGetSuppliers) (sortedSuppls *SortedSuppliers, err error) {
sortedSuppls = &SortedSuppliers{ProfileID: prflID,
Sorting: lcs.sorting,
Sorting: qos.sorting,
SortedSuppliers: make([]*SortedSupplier, 0)}
for _, s := range suppls {
metricSupp, err := lcs.spS.statMetrics(s.StatIDs, ev.Tenant) //create metric map for suppier s
if err != nil {
if extraOpts.ignoreErrors {
utils.Logger.Warning(
fmt.Sprintf("<%s> profile: %s ignoring supplier with ID: %s, err: %s",
utils.SupplierS, prflID, s.ID, err.Error()))
continue
}
if srtSpl, pass, err := qos.spS.populateSortingData(ev, s, extraOpts); err != nil {
return nil, err
} else if pass && srtSpl != nil {
sortedSuppls.SortedSuppliers = append(sortedSuppls.SortedSuppliers, srtSpl)
}
srtData := map[string]float64{
utils.Weight: s.Weight,
}
for _, metric := range extraOpts.sortingParameters {
hasMetric := false //check if metricSupp have sortingParameter
for keyWithID, value := range metricSupp { //transfer data from metric into srtData
if metric == strings.Split(keyWithID, utils.InInFieldSep)[0] {
if val, hasKey := srtData[metric]; !hasKey ||
(metric == utils.MetaPDD && val < value) || //worst values
(metric != utils.MetaPDD && val > value) {
srtData[metric] = value
hasMetric = true
}
}
}
if !hasMetric { //if not have populate with default value
switch metric {
default:
srtData[metric] = -1
case utils.MetaPDD:
srtData[metric] = 1000000
}
}
}
sortingData := map[string]interface{}{
utils.Weight: s.Weight,
}
for k, v := range metricSupp {
sortingData[k] = v
}
sortedSuppls.SortedSuppliers = append(sortedSuppls.SortedSuppliers,
&SortedSupplier{
SupplierID: s.ID,
SortingData: sortingData,
SupplierParameters: s.SupplierParameters,
worstStats: srtData,
},
)
}
if len(sortedSuppls.SortedSuppliers) == 0 {
return nil, utils.ErrNotFound

50
engine/spls_weight.go Executable file
View File

@@ -0,0 +1,50 @@
/*
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 <http://www.gnu.org/licenses/>
*/
package engine
import (
"github.com/cgrates/cgrates/utils"
)
func NewWeightSorter(spS *SupplierService) *WeightSorter {
return &WeightSorter{spS: spS,
sorting: utils.MetaWeight}
}
// WeightSorter orders suppliers based on their weight, no cost involved
type WeightSorter struct {
sorting string
spS *SupplierService
}
func (ws *WeightSorter) 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 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.SortWeight()
return
}

View File

@@ -23,6 +23,7 @@ import (
"reflect"
"sort"
"strconv"
"strings"
"time"
"github.com/cgrates/cgrates/config"
@@ -284,6 +285,100 @@ func (spS *SupplierService) resourceUsage(resIDs []string) (tUsage float64, err
return
}
func (spS *SupplierService) populateSortingData(ev *utils.CGREvent, spl *Supplier, extraOpts *optsGetSuppliers) (srtSpl *SortedSupplier, pass bool, err error) {
globalStats := map[string]float64{ //used for QOS strategy
utils.Weight: spl.Weight,
}
sortedSpl := &SortedSupplier{
SupplierID: spl.ID,
SortingData: map[string]interface{}{
utils.Weight: spl.Weight,
},
SupplierParameters: spl.SupplierParameters,
}
//calculate costData if we have fields
if len(spl.AccountIDs) != 0 || len(spl.RatingPlanIDs) != 0 {
costData, err := spS.costForEvent(ev, spl.AccountIDs, spl.RatingPlanIDs)
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()))
} else {
return nil, false, err
}
} else if len(costData) == 0 {
utils.Logger.Warning(
fmt.Sprintf("<%s> profile: %s ignoring supplier with ID: %s, missing cost information",
utils.SupplierS, spl.ID))
} else {
if extraOpts.maxCost != 0 &&
costData[utils.Cost].(float64) > extraOpts.maxCost {
return nil, false, nil
}
for k, v := range costData {
sortedSpl.SortingData[k] = v
}
}
}
metricForFilter := map[string]interface{}{
utils.Weight: spl.Weight,
}
//calculate metrics
if len(spl.StatIDs) != 0 {
metricSupp, err := spS.statMetrics(spl.StatIDs, ev.Tenant) //create metric map for suppier
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()))
} else {
return nil, false, err
}
}
for _, metric := range extraOpts.sortingParameters {
hasMetric := false //check if metricSupp have sortingParameter
for keyWithID, value := range metricSupp { //transfer data from metric into globalStats
if metric == strings.Split(keyWithID, utils.InInFieldSep)[0] {
if val, hasKey := globalStats[metric]; !hasKey ||
(metric == utils.MetaPDD && val < value) || //worst values
(metric != utils.MetaPDD && val > value) {
globalStats[metric] = value
hasMetric = true
}
}
}
if !hasMetric { //if not have populate with default value
switch metric {
default:
globalStats[metric] = -1
case utils.MetaPDD:
globalStats[metric] = 1000000
}
}
}
for k, v := range metricSupp {
sortedSpl.SortingData[k] = v
metricForFilter[strings.Split(k, utils.InInFieldSep)[0]] = v
}
sortedSpl.globalStats = globalStats
}
//filter the supplier
if len(spl.FilterIDs) != 0 {
nM := NewNavigableMap(nil)
nM.Set([]string{"*req"}, ev.Event, false)
nM.Set([]string{"*sd"}, sortedSpl.SortingData, false)
nM.Set([]string{"*gs"}, metricForFilter, false)
if pass, err = spS.filterS.Pass(ev.Tenant, spl.FilterIDs,
nM); err != nil {
return nil, false, err
} else if !pass {
return nil, false, nil
}
}
return sortedSpl, true, nil
}
// supliersForEvent will return the list of valid supplier IDs
// for event based on filters and sorting algorithms
func (spS *SupplierService) sortedSuppliersForEvent(args *ArgsGetSuppliers) (sortedSuppls *SortedSuppliers, err error) {
@@ -296,40 +391,36 @@ func (spS *SupplierService) sortedSuppliersForEvent(args *ArgsGetSuppliers) (sor
} else if len(suppPrfls) == 0 {
return nil, utils.ErrNotFound
}
splPrfl := suppPrfls[0] // pick up the first lcr profile as winner
var spls []*Supplier
for _, s := range splPrfl.Suppliers {
if len(s.FilterIDs) != 0 { // filters should be applied, check them here
if pass, err := spS.filterS.Pass(args.Tenant, s.FilterIDs,
NewNavigableMap(args.Event)); err != nil {
return nil, err
} else if !pass {
continue
}
}
spls = append(spls, s)
}
splPrfl := suppPrfls[0] // pick up the first lcr profile as winner
extraOpts, err := args.asOptsGetSuppliers() // convert suppliers arguments into internal options used to limit data
if err != nil {
return nil, err
}
extraOpts.sortingParameters = splPrfl.SortingParameters // populate sortingParameters in extraOpts
sortedSuppliers, err := spS.sorter.SortSuppliers(splPrfl.ID, splPrfl.Sorting,
spls, &args.CGREvent, extraOpts)
splPrfl.Suppliers, &args.CGREvent, extraOpts)
if err != nil {
return nil, err
}
srtTmp := &SortedSuppliers{
ProfileID: sortedSuppliers.ProfileID,
Sorting: sortedSuppliers.Sorting,
}
for _, s := range sortedSuppliers.SortedSuppliers {
srtTmp.SortedSuppliers = append(srtTmp.SortedSuppliers, s)
}
if args.Paginator.Offset != nil {
if *args.Paginator.Offset <= len(sortedSuppliers.SortedSuppliers) {
sortedSuppliers.SortedSuppliers = sortedSuppliers.SortedSuppliers[*args.Paginator.Offset:]
if *args.Paginator.Offset <= len(srtTmp.SortedSuppliers) {
srtTmp.SortedSuppliers = srtTmp.SortedSuppliers[*args.Paginator.Offset:]
}
}
if args.Paginator.Limit != nil {
if *args.Paginator.Limit <= len(sortedSuppliers.SortedSuppliers) {
sortedSuppliers.SortedSuppliers = sortedSuppliers.SortedSuppliers[:*args.Paginator.Limit]
if *args.Paginator.Limit <= len(srtTmp.SortedSuppliers) {
srtTmp.SortedSuppliers = srtTmp.SortedSuppliers[:*args.Paginator.Limit]
}
}
return sortedSuppliers, nil
return srtTmp, nil
}
type ArgsGetSuppliers struct {

View File

@@ -45,7 +45,7 @@ var (
Suppliers: []*Supplier{
&Supplier{
ID: "supplier1",
FilterIDs: []string{"FLTR_SUPP_1"},
FilterIDs: []string{},
AccountIDs: []string{},
RatingPlanIDs: []string{},
ResourceIDs: []string{},
@@ -70,7 +70,7 @@ var (
Suppliers: []*Supplier{
&Supplier{
ID: "supplier2",
FilterIDs: []string{"FLTR_SUPP_2"},
FilterIDs: []string{},
AccountIDs: []string{},
RatingPlanIDs: []string{},
ResourceIDs: []string{},
@@ -80,7 +80,7 @@ var (
},
&Supplier{
ID: "supplier3",
FilterIDs: []string{"FLTR_SUPP_2"},
FilterIDs: []string{},
AccountIDs: []string{},
RatingPlanIDs: []string{},
ResourceIDs: []string{},
@@ -90,7 +90,7 @@ var (
},
&Supplier{
ID: "supplier1",
FilterIDs: []string{"FLTR_SUPP_2"},
FilterIDs: []string{},
AccountIDs: []string{},
RatingPlanIDs: []string{},
ResourceIDs: []string{},
@@ -115,7 +115,7 @@ var (
Suppliers: []*Supplier{
&Supplier{
ID: "supplier1",
FilterIDs: []string{"FLTR_SUPP_3"},
FilterIDs: []string{},
AccountIDs: []string{},
RatingPlanIDs: []string{},
ResourceIDs: []string{},
@@ -430,6 +430,7 @@ func TestSuppliersSortedForEvent(t *testing.T) {
if !reflect.DeepEqual(eFirstSupplierProfile, sprf) {
t.Errorf("Expecting: %+v, received: %+v", eFirstSupplierProfile, sprf)
}
eFirstSupplierProfile = &SortedSuppliers{
ProfileID: "SupplierProfile2",
Sorting: utils.MetaWeight,

View File

@@ -1386,6 +1386,7 @@ type TPFilter struct {
type TPSupplier struct {
ID string // SupplierID
SortingFilter []string
FilterIDs []string
AccountIDs []string
RatingPlanIDs []string // used when computing price

View File

@@ -486,6 +486,7 @@ const (
MetaLeastCost = "*least_cost"
MetaHighestCost = "*highest_cost"
MetaQOS = "*qos"
MetaStatFiltered = "*stat_filtered"
Weight = "Weight"
Cost = "Cost"
RatingPlanID = "RatingPlanID"