mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-16 13:49:53 +05:00
Merge branch 'master' into hapool
This commit is contained in:
13
docs/lcr.rst
13
docs/lcr.rst
@@ -18,7 +18,7 @@ Strategy indicates supplier selection algorithm and StrategyParams will be speci
|
||||
\*static (filter)
|
||||
Will use the suppliers provided as params.
|
||||
StrategyParams: suppier1;supplier2;etc
|
||||
|
||||
|
||||
\*lowest_cost (sorting)
|
||||
Matching suppliers will be sorted by ascending cost.
|
||||
StrategyParams: None
|
||||
@@ -35,6 +35,13 @@ Strategy indicates supplier selection algorithm and StrategyParams will be speci
|
||||
The system will sort by metrics in the order of appearance.
|
||||
StrategyParams: metric1;metric2;etc
|
||||
|
||||
\*load_distribution (sorting/filter)
|
||||
The system will sort the suppliers in order to achieve the specified load distribution.
|
||||
- if all have less than ponder return random order
|
||||
- if some have a cdr count not divisible by ponder return them first and all ordered by cdr times, oldest first
|
||||
- if all have a multiple of ponder return in the order of cdr times, oldest first
|
||||
StrategyParams: supplier1:ponder;supplier2:ponder;*default:ponder
|
||||
|
||||
ActivationTime is the date/time when the LCR entry starts to be active.
|
||||
|
||||
Weight is used to sort the rules with the same activation time.
|
||||
@@ -43,7 +50,7 @@ Example
|
||||
+++++++
|
||||
|
||||
::
|
||||
|
||||
|
||||
*in, cgrates.org,call,*any,*any,EU_LANDLINE,LCR_STANDARD,*static,ivo;dan;rif,2012-01-01T00:00:00Z,10
|
||||
|
||||
Code implementation
|
||||
@@ -63,7 +70,7 @@ For the QOS strategies the suppliers are searched using call descriptor paramete
|
||||
For the lowest/highest cost strategies the matched suppliers are sorted ascending/descending on cost.
|
||||
|
||||
::
|
||||
|
||||
|
||||
{
|
||||
"Entry": {
|
||||
"DestinationId": "*any",
|
||||
|
||||
@@ -342,8 +342,14 @@ func (lc *LCRCost) SortLoadDistribution() {
|
||||
// if all have a multiple of ponder return in the order of cdr times, oldest first
|
||||
|
||||
// first put them in one of the above categories
|
||||
havePonderlessSuppliers := false
|
||||
for supCost, sq := range supplierQueues {
|
||||
ponder := lc.GetSupplierPonder(supCost.Supplier)
|
||||
if ponder == -1 {
|
||||
supCost.Cost = -1
|
||||
havePonderlessSuppliers = true
|
||||
continue
|
||||
}
|
||||
cdrCount := len(sq.Cdrs)
|
||||
if cdrCount < ponder {
|
||||
supCost.Cost = float64(LOW_PRIORITY_LIMIT + rand.Intn(RAND_LIMIT))
|
||||
@@ -357,6 +363,15 @@ func (lc *LCRCost) SortLoadDistribution() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if havePonderlessSuppliers {
|
||||
var filteredSupplierCost []*LCRSupplierCost
|
||||
for _, supCost := range lc.SupplierCosts {
|
||||
if supCost.Cost != -1 {
|
||||
filteredSupplierCost = append(filteredSupplierCost, supCost)
|
||||
}
|
||||
}
|
||||
lc.SupplierCosts = filteredSupplierCost
|
||||
}
|
||||
}
|
||||
|
||||
// used in load distribution strategy only
|
||||
@@ -388,8 +403,10 @@ func (lc *LCRCost) GetSupplierPonder(supplier string) int {
|
||||
return ponder
|
||||
}
|
||||
}
|
||||
|
||||
return 1
|
||||
if len(ponders) == 0 {
|
||||
return 1 // use random/last cdr date sorting
|
||||
}
|
||||
return -1 // exclude missing suppliers
|
||||
}
|
||||
|
||||
func (lc *LCRCost) HasErrors() bool {
|
||||
|
||||
@@ -412,7 +412,7 @@ func TestLCRCostSuppliersLoadAllRounded(t *testing.T) {
|
||||
Supplier: "*out:tenant12:call:dan12",
|
||||
supplierQueues: []*StatsQueue{
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{}, &QCdr{}, &QCdr{}, &QCdr{}, &QCdr{SetupTime: setupTime.Add(60 * time.Minute)}},
|
||||
Cdrs: []*QCdr{&QCdr{}, &QCdr{}, &QCdr{}, &QCdr{}, &QCdr{SetupTime: setupTime.Add(100 * time.Minute)}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 10 * time.Minute,
|
||||
@@ -452,7 +452,7 @@ func TestLCRCostSuppliersLoadAllRounded(t *testing.T) {
|
||||
},
|
||||
},
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{}, &QCdr{SetupTime: setupTime.Add(200 * time.Minute)}},
|
||||
Cdrs: []*QCdr{&QCdr{}, &QCdr{SetupTime: setupTime.Add(300 * time.Minute)}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 10 * time.Minute,
|
||||
@@ -481,6 +481,206 @@ func TestLCRCostSuppliersLoadAllOver(t *testing.T) {
|
||||
setupTime := time.Date(2015, 7, 31, 6, 43, 0, 0, time.UTC)
|
||||
lcrCost := &LCRCost{
|
||||
Entry: &LCREntry{DestinationId: utils.ANY, RPCategory: "call", Strategy: LCR_STRATEGY_LOAD, StrategyParams: "ivo12:2;dan12:4;*default:2", Weight: 10.0},
|
||||
SupplierCosts: []*LCRSupplierCost{
|
||||
&LCRSupplierCost{
|
||||
Supplier: "*out:tenant12:call:ivo12",
|
||||
supplierQueues: []*StatsQueue{
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{}, &QCdr{}, &QCdr{SetupTime: setupTime}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 3 * time.Minute,
|
||||
},
|
||||
},
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{SetupTime: setupTime}, &QCdr{SetupTime: setupTime}, &QCdr{SetupTime: setupTime}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 1 * time.Minute,
|
||||
},
|
||||
},
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{SetupTime: setupTime}, &QCdr{SetupTime: setupTime}, &QCdr{SetupTime: setupTime}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 10 * time.Minute,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&LCRSupplierCost{
|
||||
Supplier: "*out:tenant12:call:dan12",
|
||||
supplierQueues: []*StatsQueue{
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{}, &QCdr{}, &QCdr{}, &QCdr{}, &QCdr{SetupTime: setupTime.Add(100 * time.Minute)}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 10 * time.Minute,
|
||||
},
|
||||
},
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{}, &QCdr{}, &QCdr{}, &QCdr{}, &QCdr{SetupTime: setupTime}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 7 * time.Minute,
|
||||
},
|
||||
},
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{}, &QCdr{}, &QCdr{}, &QCdr{}, &QCdr{SetupTime: setupTime}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 7 * time.Minute,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&LCRSupplierCost{
|
||||
Supplier: "*out:tenant12:call:rif12",
|
||||
supplierQueues: []*StatsQueue{
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{}, &QCdr{SetupTime: setupTime}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 7 * time.Minute,
|
||||
},
|
||||
},
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{}, &QCdr{SetupTime: setupTime}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 7 * time.Minute,
|
||||
},
|
||||
},
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{}, &QCdr{}, &QCdr{SetupTime: setupTime.Add(300 * time.Minute)}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 10 * time.Minute,
|
||||
},
|
||||
},
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{}, &QCdr{SetupTime: setupTime}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 1 * time.Minute,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
lcrCost.Sort()
|
||||
if lcrCost.SupplierCosts[0].Supplier != "*out:tenant12:call:ivo12" ||
|
||||
lcrCost.SupplierCosts[1].Supplier != "*out:tenant12:call:dan12" ||
|
||||
lcrCost.SupplierCosts[2].Supplier != "*out:tenant12:call:rif12" {
|
||||
t.Error("Error soring on load distribution: ", utils.ToIJSON(lcrCost))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLCRCostSuppliersLoadAllOverMisingDefault(t *testing.T) {
|
||||
setupTime := time.Date(2015, 7, 31, 6, 43, 0, 0, time.UTC)
|
||||
lcrCost := &LCRCost{
|
||||
Entry: &LCREntry{DestinationId: utils.ANY, RPCategory: "call", Strategy: LCR_STRATEGY_LOAD, StrategyParams: "ivo12:2;dan12:4", Weight: 10.0},
|
||||
SupplierCosts: []*LCRSupplierCost{
|
||||
&LCRSupplierCost{
|
||||
Supplier: "*out:tenant12:call:ivo12",
|
||||
supplierQueues: []*StatsQueue{
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{}, &QCdr{}, &QCdr{SetupTime: setupTime}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 3 * time.Minute,
|
||||
},
|
||||
},
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{SetupTime: setupTime}, &QCdr{SetupTime: setupTime}, &QCdr{SetupTime: setupTime}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 1 * time.Minute,
|
||||
},
|
||||
},
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{SetupTime: setupTime}, &QCdr{SetupTime: setupTime}, &QCdr{SetupTime: setupTime}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 10 * time.Minute,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&LCRSupplierCost{
|
||||
Supplier: "*out:tenant12:call:dan12",
|
||||
supplierQueues: []*StatsQueue{
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{}, &QCdr{}, &QCdr{}, &QCdr{}, &QCdr{SetupTime: setupTime.Add(100 * time.Minute)}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 10 * time.Minute,
|
||||
},
|
||||
},
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{}, &QCdr{}, &QCdr{}, &QCdr{}, &QCdr{SetupTime: setupTime}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 7 * time.Minute,
|
||||
},
|
||||
},
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{}, &QCdr{}, &QCdr{}, &QCdr{}, &QCdr{SetupTime: setupTime}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 7 * time.Minute,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&LCRSupplierCost{
|
||||
Supplier: "*out:tenant12:call:rif12",
|
||||
supplierQueues: []*StatsQueue{
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{}, &QCdr{SetupTime: setupTime}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 7 * time.Minute,
|
||||
},
|
||||
},
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{}, &QCdr{SetupTime: setupTime}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 7 * time.Minute,
|
||||
},
|
||||
},
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{}, &QCdr{}, &QCdr{SetupTime: setupTime.Add(300 * time.Minute)}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 10 * time.Minute,
|
||||
},
|
||||
},
|
||||
&StatsQueue{
|
||||
Cdrs: []*QCdr{&QCdr{}, &QCdr{SetupTime: setupTime}},
|
||||
conf: &CdrStats{
|
||||
QueueLength: 0,
|
||||
TimeWindow: 1 * time.Minute,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
lcrCost.Sort()
|
||||
if len(lcrCost.SupplierCosts) != 2 ||
|
||||
lcrCost.SupplierCosts[0].Supplier != "*out:tenant12:call:ivo12" ||
|
||||
lcrCost.SupplierCosts[1].Supplier != "*out:tenant12:call:dan12" {
|
||||
t.Error("Error soring on load distribution: ", utils.ToIJSON(lcrCost))
|
||||
}
|
||||
}
|
||||
|
||||
func TestLCRCostSuppliersLoadAllOverMisingParams(t *testing.T) {
|
||||
setupTime := time.Date(2015, 7, 31, 6, 43, 0, 0, time.UTC)
|
||||
lcrCost := &LCRCost{
|
||||
Entry: &LCREntry{DestinationId: utils.ANY, RPCategory: "call", Strategy: LCR_STRATEGY_LOAD, StrategyParams: "", Weight: 10.0},
|
||||
SupplierCosts: []*LCRSupplierCost{
|
||||
&LCRSupplierCost{
|
||||
Supplier: "*out:tenant12:call:ivo12",
|
||||
@@ -570,9 +770,7 @@ func TestLCRCostSuppliersLoadAllOver(t *testing.T) {
|
||||
},
|
||||
}
|
||||
lcrCost.Sort()
|
||||
if lcrCost.SupplierCosts[0].Supplier != "*out:tenant12:call:ivo12" ||
|
||||
lcrCost.SupplierCosts[1].Supplier != "*out:tenant12:call:dan12" ||
|
||||
lcrCost.SupplierCosts[2].Supplier != "*out:tenant12:call:rif12" {
|
||||
if len(lcrCost.SupplierCosts) != 3 {
|
||||
t.Error("Error soring on load distribution: ", utils.ToIJSON(lcrCost))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user