Merge branch 'master' into hapool

This commit is contained in:
Radu Ioan Fericean
2015-07-31 14:30:48 +03:00
3 changed files with 232 additions and 10 deletions

View File

@@ -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",

View File

@@ -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 {

View File

@@ -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))
}
}