From bd8ba22ca71042a040eced8700ed089137f7f1bd Mon Sep 17 00:00:00 2001 From: edwardro22 Date: Sun, 24 Dec 2017 02:12:09 +0200 Subject: [PATCH] Added StatSum and StatAverage to stats service --- apier/v1/stats_it_test.go | 21 +- ...rocess_event.go => stats_process_event.go} | 2 +- console/suppliers_sort.go | 2 +- .../mysql/create_tariffplan_tables.sql | 1 + .../postgres/create_tariffplan_tables.sql | 1 + data/tariffplans/testtp/Stats.csv | 5 +- data/tariffplans/tutorial/Stats.csv | 4 +- engine/attributes_test.go | 10 +- engine/libstats.go | 75 ++++-- engine/libstats_test.go | 54 ++-- engine/loader_csv_test.go | 9 +- engine/model_helpers.go | 92 +++++-- engine/model_helpers_test.go | 140 ++++++++-- engine/models.go | 11 +- engine/statmetrics.go | 205 +++++++++++++-- engine/statmetrics_test.go | 246 ++++++++++++++++-- engine/stats.go | 25 +- engine/tp_reader.go | 14 +- migrator/migrator_it_test.go | 34 ++- migrator/stats.go | 14 +- migrator/stats_test.go | 16 +- utils/apitpdata.go | 8 +- utils/consts.go | 16 +- 23 files changed, 801 insertions(+), 204 deletions(-) rename console/{statqueue_process_event.go => stats_process_event.go} (98%) diff --git a/apier/v1/stats_it_test.go b/apier/v1/stats_it_test.go index ed4dd7b60..6a64a216c 100644 --- a/apier/v1/stats_it_test.go +++ b/apier/v1/stats_it_test.go @@ -284,12 +284,21 @@ func testV1STSSetStatQueueProfile(t *testing.T) { }, QueueLength: 10, TTL: time.Duration(10) * time.Second, - Metrics: []string{"MetricValue", "MetricValueTwo"}, - Thresholds: []string{"Val1", "Val2"}, - Blocker: true, - Stored: true, - Weight: 20, - MinItems: 1, + Metrics: []*utils.MetricWithParams{ + &utils.MetricWithParams{ + MetricID: "MetricValue", + Parameters: "", + }, + &utils.MetricWithParams{ + MetricID: "MetricValueTwo", + Parameters: "", + }, + }, + Thresholds: []string{"Val1", "Val2"}, + Blocker: true, + Stored: true, + Weight: 20, + MinItems: 1, } var result string if err := stsV1Rpc.Call("ApierV1.SetStatQueueProfile", statConfig, &result); err != nil { diff --git a/console/statqueue_process_event.go b/console/stats_process_event.go similarity index 98% rename from console/statqueue_process_event.go rename to console/stats_process_event.go index 164001690..4841e1d2f 100644 --- a/console/statqueue_process_event.go +++ b/console/stats_process_event.go @@ -25,7 +25,7 @@ import ( func init() { c := &CmdStatQueueProcessEvent{ - name: "statqueue_process_event", + name: "stats_process_event", rpcMethod: "StatSv1.ProcessEvent", } commands[c.Name()] = c diff --git a/console/suppliers_sort.go b/console/suppliers_sort.go index 65d0256ec..d4f3064fa 100644 --- a/console/suppliers_sort.go +++ b/console/suppliers_sort.go @@ -25,7 +25,7 @@ import ( func init() { c := &CmdSuppliersSort{ - name: "suppliers_sort", + name: "suppliers_get", rpcMethod: "SupplierSv1.GetSuppliers", rpcParams: new(utils.CGREvent), } diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index 5991bf942..85b024f21 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -431,6 +431,7 @@ CREATE TABLE tp_stats ( `queue_length` int(11) NOT NULL, `ttl` varchar(32) NOT NULL, `metrics` varchar(64) NOT NULL, + `parameters` varchar(64) NOT NULL, `blocker` BOOLEAN NOT NULL, `stored` BOOLEAN NOT NULL, `weight` decimal(8,2) NOT NULL, diff --git a/data/storage/postgres/create_tariffplan_tables.sql b/data/storage/postgres/create_tariffplan_tables.sql index 5ed3b98f8..33db1f912 100644 --- a/data/storage/postgres/create_tariffplan_tables.sql +++ b/data/storage/postgres/create_tariffplan_tables.sql @@ -427,6 +427,7 @@ CREATE TABLE tp_stats ( "queue_length" INTEGER NOT NULL, "ttl" varchar(32) NOT NULL, "metrics" varchar(64) NOT NULL, + "parameters" varchar(64) NOT NULL, "blocker" BOOLEAN NOT NULL, "stored" BOOLEAN NOT NULL, "weight" decimal(8,2) NOT NULL, diff --git a/data/tariffplans/testtp/Stats.csv b/data/tariffplans/testtp/Stats.csv index 1a9302d0b..67356e3b6 100755 --- a/data/tariffplans/testtp/Stats.csv +++ b/data/tariffplans/testtp/Stats.csv @@ -1,2 +1,3 @@ -#Tenant[0],Id[1],FilterIDs[2],ActivationInterval[3],QueueLength[4],TTL[5],Metrics[6],Blocker[7],Stored[8],Weight[9],MinItems[10],Thresholds[11] -cgrates.org,Stats1,FLTR_STS1,2014-07-29T15:00:00Z,100,1s,*asr;*acc;*tcc;*acd;*tcd;*pdd,true,true,20,2,THRESH1;THRESH2 +#Tenant[0],Id[1],FilterIDs[2],ActivationInterval[3],QueueLength[4],TTL[5],Metrics[6],MetricParams[7],Blocker[8],Stored[9],Weight[10],MinItems[11],Thresholds[12] +cgrates.org,Stats1,FLTR_STS1,2014-07-29T15:00:00Z,100,1s,*asr;*acc;*tcc;*acd;*tcd;*pdd,,true,true,20,2,THRESH1;THRESH2 +cgrates.org,Stats1,FLTR_STS1,2014-07-29T15:00:00Z,100,1s,*sum;*average,Value,true,true,20,2,THRESH1;THRESH2 diff --git a/data/tariffplans/tutorial/Stats.csv b/data/tariffplans/tutorial/Stats.csv index 1a9302d0b..27cf7557a 100644 --- a/data/tariffplans/tutorial/Stats.csv +++ b/data/tariffplans/tutorial/Stats.csv @@ -1,2 +1,2 @@ -#Tenant[0],Id[1],FilterIDs[2],ActivationInterval[3],QueueLength[4],TTL[5],Metrics[6],Blocker[7],Stored[8],Weight[9],MinItems[10],Thresholds[11] -cgrates.org,Stats1,FLTR_STS1,2014-07-29T15:00:00Z,100,1s,*asr;*acc;*tcc;*acd;*tcd;*pdd,true,true,20,2,THRESH1;THRESH2 +#Tenant[0],Id[1],FilterIDs[2],ActivationInterval[3],QueueLength[4],TTL[5],Metrics[6],MetricParams[7],Blocker[8],Stored[9],Weight[10],MinItems[11],Thresholds[12] +cgrates.org,Stats1,FLTR_STS1,2014-07-29T15:00:00Z,100,1s,*asr;*acc;*tcc;*acd;*tcd;*pdd,,true,true,20,2,THRESH1;THRESH2 diff --git a/engine/attributes_test.go b/engine/attributes_test.go index 4e20693bd..1f319fe88 100644 --- a/engine/attributes_test.go +++ b/engine/attributes_test.go @@ -45,10 +45,6 @@ func TestAttributes(t *testing.T) { } } -//.matchingAttributeProfilesForEvent -//.attributeProfileForEvent -//.processEvent - func testPopulateAttrService(t *testing.T) { var filters1 []*RequestFilter var filters2 []*RequestFilter @@ -64,7 +60,6 @@ func testPopulateAttrService(t *testing.T) { Substitute: "Al1", Append: true, } - atrPs = AttributeProfiles{ &AttributeProfile{ Tenant: "cgrates.org", @@ -91,7 +86,6 @@ func testPopulateAttrService(t *testing.T) { Weight: 20, }, } - x, err := NewRequestFilter(MetaString, "attributeprofile1", []string{"Attribute"}) if err != nil { t.Errorf("Error: %+v", err) @@ -160,6 +154,7 @@ func testAttributeMatchingAttributeProfilesForEvent(t *testing.T) { t.Errorf("Expecting: %+v, received: %+v", atrPs[1], atrpl[1]) } } + func testAttributeProfileForEvent(t *testing.T) { context := utils.MetaRating ev := make(map[string]interface{}) @@ -179,8 +174,8 @@ func testAttributeProfileForEvent(t *testing.T) { if !reflect.DeepEqual(atrPs[0], atrpl) && !reflect.DeepEqual(atrPs[1], atrpl) { t.Errorf("Expecting: %+v, received: %+v", atrPs[0], atrpl) } - } + func testAttributeProcessEvent(t *testing.T) { context := utils.MetaRating ev := make(map[string]interface{}) @@ -208,5 +203,4 @@ func testAttributeProcessEvent(t *testing.T) { } else if !reflect.DeepEqual(eRply.CGREvent, atrpl.CGREvent) { t.Errorf("Expecting: %+v, received: %+v", eRply.CGREvent, atrpl.CGREvent) } - } diff --git a/engine/libstats.go b/engine/libstats.go index 3758be945..711fb7f27 100755 --- a/engine/libstats.go +++ b/engine/libstats.go @@ -33,9 +33,9 @@ type StatQueueProfile struct { ActivationInterval *utils.ActivationInterval // Activation interval QueueLength int TTL time.Duration - Metrics []string // list of metrics to build - Thresholds []string // list of thresholds to be checked after changes - Blocker bool // blocker flag to stop processing on filters matched + Metrics []*utils.MetricWithParams // list of metrics to build + Thresholds []string // list of thresholds to be checked after changes + Blocker bool // blocker flag to stop processing on filters matched Stored bool Weight float64 MinItems int @@ -47,6 +47,7 @@ func (sqp *StatQueueProfile) TenantID() string { // NewStoredStatQueue initiates a StoredStatQueue out of StatQueue func NewStoredStatQueue(sq *StatQueue, ms Marshaler) (sSQ *StoredStatQueue, err error) { + marshaledMetrics := make(map[string]map[string][]byte) sSQ = &StoredStatQueue{ Tenant: sq.Tenant, ID: sq.ID, @@ -54,19 +55,27 @@ func NewStoredStatQueue(sq *StatQueue, ms Marshaler) (sSQ *StoredStatQueue, err EventID string ExpiryTime *time.Time }, len(sq.SQItems)), - SQMetrics: make(map[string][]byte, len(sq.SQMetrics)), - MinItems: sq.MinItems, + MinItems: sq.MinItems, } for i, sqItm := range sq.SQItems { sSQ.SQItems[i] = sqItm } - for metricID, metric := range sq.SQMetrics { - if marshaled, err := metric.Marshal(ms); err != nil { - return nil, err - } else { - sSQ.SQMetrics[metricID] = marshaled + for metricID, _ := range sq.SQMetrics { + for parameter, metric := range sq.SQMetrics[metricID] { + if marshaled, err := metric.Marshal(ms); err != nil { + return nil, err + } else { + if _, hasIt := marshaledMetrics[metricID]; !hasIt { + marshaledMetrics[metricID] = make(map[string][]byte) + } + if _, hasIt := marshaledMetrics[metricID][parameter]; !hasIt { + marshaledMetrics[metricID][parameter] = make([]byte, len(marshaled)) + } + marshaledMetrics[metricID][parameter] = marshaled + } } } + sSQ.SQMetrics = marshaledMetrics return } @@ -78,7 +87,7 @@ type StoredStatQueue struct { EventID string // Bounded to the original utils.CGREvent ExpiryTime *time.Time // Used to auto-expire events } - SQMetrics map[string][]byte + SQMetrics map[string]map[string][]byte MinItems int } @@ -89,6 +98,7 @@ func (ssq *StoredStatQueue) SqID() string { // AsStatQueue converts into StatQueue unmarshaling SQMetrics func (ssq *StoredStatQueue) AsStatQueue(ms Marshaler) (sq *StatQueue, err error) { + SQMetrics := make(map[string]map[string]StatMetric) sq = &StatQueue{ Tenant: ssq.Tenant, ID: ssq.ID, @@ -96,21 +106,26 @@ func (ssq *StoredStatQueue) AsStatQueue(ms Marshaler) (sq *StatQueue, err error) EventID string ExpiryTime *time.Time }, len(ssq.SQItems)), - SQMetrics: make(map[string]StatMetric, len(ssq.SQMetrics)), - MinItems: ssq.MinItems, + MinItems: ssq.MinItems, } for i, sqItm := range ssq.SQItems { sq.SQItems[i] = sqItm } - for metricID, marshaled := range ssq.SQMetrics { - if metric, err := NewStatMetric(metricID, ssq.MinItems); err != nil { - return nil, err - } else if err := metric.LoadMarshaled(ms, marshaled); err != nil { - return nil, err - } else { - sq.SQMetrics[metricID] = metric + for metricID, _ := range ssq.SQMetrics { + for parameter, marshaled := range ssq.SQMetrics[metricID] { + if metric, err := NewStatMetric(metricID, ssq.MinItems, parameter); err != nil { + return nil, err + } else if err := metric.LoadMarshaled(ms, marshaled); err != nil { + return nil, err + } else { + if _, hasIt := SQMetrics[metricID]; !hasIt { + SQMetrics[metricID] = make(map[string]StatMetric) + } + SQMetrics[metricID][parameter] = metric + } } } + sq.SQMetrics = SQMetrics return } @@ -122,7 +137,7 @@ type StatQueue struct { EventID string // Bounded to the original utils.CGREvent ExpiryTime *time.Time // Used to auto-expire events } - SQMetrics map[string]StatMetric + SQMetrics map[string]map[string]StatMetric MinItems int sqPrfl *StatQueueProfile dirty *bool // needs save @@ -144,9 +159,11 @@ func (sq *StatQueue) ProcessEvent(ev *utils.CGREvent) (err error) { // remStatEvent removes an event from metrics func (sq *StatQueue) remEventWithID(evTenantID string) { - for metricID, metric := range sq.SQMetrics { - if err := metric.RemEvent(evTenantID); err != nil { - utils.Logger.Warning(fmt.Sprintf(" metricID: %s, remove eventID: %s, error: %s", metricID, evTenantID, err.Error())) + for metricID, _ := range sq.SQMetrics { + for _, metric := range sq.SQMetrics[metricID] { + if err := metric.RemEvent(evTenantID); err != nil { + utils.Logger.Warning(fmt.Sprintf(" metricID: %s, remove eventID: %s, error: %s", metricID, evTenantID, err.Error())) + } } } } @@ -184,10 +201,12 @@ func (sq *StatQueue) remOnQueueLength() { // addStatEvent computes metrics for an event func (sq *StatQueue) addStatEvent(ev *utils.CGREvent) { - for metricID, metric := range sq.SQMetrics { - if err := metric.AddEvent(ev); err != nil { - utils.Logger.Warning(fmt.Sprintf(" metricID: %s, add eventID: %s, error: %s", - metricID, ev.TenantID(), err.Error())) + for metricID, _ := range sq.SQMetrics { + for _, metric := range sq.SQMetrics[metricID] { + if err := metric.AddEvent(ev); err != nil { + utils.Logger.Warning(fmt.Sprintf(" metricID: %s, add eventID: %s, error: %s", + metricID, ev.TenantID(), err.Error())) + } } } } diff --git a/engine/libstats_test.go b/engine/libstats_test.go index 77e398f07..e139ddd91 100644 --- a/engine/libstats_test.go +++ b/engine/libstats_test.go @@ -48,18 +48,20 @@ func TestStatQueuesSort(t *testing.T) { func TestStatRemEventWithID(t *testing.T) { sq = &StatQueue{ - SQMetrics: map[string]StatMetric{ - utils.MetaASR: &StatASR{ - Answered: 1, - Count: 2, - Events: map[string]bool{ - "cgrates.org:TestRemEventWithID_1": true, - "cgrates.org:TestRemEventWithID_2": false, + SQMetrics: map[string]map[string]StatMetric{ + utils.MetaASR: map[string]StatMetric{ + "": &StatASR{ + Answered: 1, + Count: 2, + Events: map[string]bool{ + "cgrates.org:TestRemEventWithID_1": true, + "cgrates.org:TestRemEventWithID_2": false, + }, }, }, }, } - asrMetric := sq.SQMetrics[utils.MetaASR].(*StatASR) + asrMetric := sq.SQMetrics[utils.MetaASR][""].(*StatASR) if asr := asrMetric.GetFloat64Value(); asr != 50 { t.Errorf("received asrMetric: %v", asrMetric) } @@ -91,14 +93,16 @@ func TestStatRemEventWithID(t *testing.T) { func TestStatRemExpired(t *testing.T) { sq = &StatQueue{ - SQMetrics: map[string]StatMetric{ - utils.MetaASR: &StatASR{ - Answered: 2, - Count: 3, - Events: map[string]bool{ - "cgrates.org:TestStatRemExpired_1": true, - "cgrates.org:TestStatRemExpired_2": false, - "cgrates.org:TestStatRemExpired_3": true, + SQMetrics: map[string]map[string]StatMetric{ + utils.MetaASR: map[string]StatMetric{ + "": &StatASR{ + Answered: 2, + Count: 3, + Events: map[string]bool{ + "cgrates.org:TestStatRemExpired_1": true, + "cgrates.org:TestStatRemExpired_2": false, + "cgrates.org:TestStatRemExpired_3": true, + }, }, }, }, @@ -114,7 +118,7 @@ func TestStatRemExpired(t *testing.T) { {"cgrates.org:TestStatRemExpired_3", utils.TimePointer(time.Now().Add(time.Duration(time.Minute)))}, }, } - asrMetric := sq.SQMetrics[utils.MetaASR].(*StatASR) + asrMetric := sq.SQMetrics[utils.MetaASR][""].(*StatASR) if asr := asrMetric.GetFloat64Value(); asr != 66.66667 { t.Errorf("received asrMetric: %v", asrMetric) } @@ -175,17 +179,19 @@ func TestStatRemOnQueueLength(t *testing.T) { func TestStatAddStatEvent(t *testing.T) { sq = &StatQueue{ - SQMetrics: map[string]StatMetric{ - utils.MetaASR: &StatASR{ - Answered: 1, - Count: 1, - Events: map[string]bool{ - "cgrates.org:TestStatRemExpired_1": true, + SQMetrics: map[string]map[string]StatMetric{ + utils.MetaASR: map[string]StatMetric{ + "": &StatASR{ + Answered: 1, + Count: 1, + Events: map[string]bool{ + "cgrates.org:TestStatRemExpired_1": true, + }, }, }, }, } - asrMetric := sq.SQMetrics[utils.MetaASR].(*StatASR) + asrMetric := sq.SQMetrics[utils.MetaASR][""].(*StatASR) if asr := asrMetric.GetFloat64Value(); asr != 100 { t.Errorf("received ASR: %v", asr) } diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index d6e3a9ce7..eee80a92c 100755 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -271,9 +271,9 @@ cgrates.org,ResGroup22,FLTR_ACNT_dan,2014-07-29T15:00:00Z,3600s,2,premium_call,t ` stats = ` #Tenant[0],Id[1],FilterIDs[2],ActivationInterval[3],QueueLength[4],TTL[5],Metrics[6],Blocker[7],Stored[8],Weight[9],MinItems[10],Thresholds[11] -cgrates.org,Stats1,FLTR_1,2014-07-29T15:00:00Z,100,1s,*asr;*acc;*tcc;*acd;*tcd;*pdd,true,true,20,2,THRESH1;THRESH2 -cgrates.org,Stats2,FLTR_1,2014-07-29T15:00:00Z,100,1s,*asr;*acc;*tcc;*acd;*tcd;*pdd,true,true,20,2,THRESH1;THRESH2 -cgrates.org,Stats3,FLTR_1,2014-07-29T15:00:00Z,100,1s,*asr;*acc;*tcc;*acd;*tcd;*pdd,true,true,20,2,THRESH1;THRESH2 +cgrates.org,Stats1,FLTR_1,2014-07-29T15:00:00Z,100,1s,*asr;*acc;*tcc;*acd;*tcd;*pdd,value,true,true,20,2,THRESH1;THRESH2 +cgrates.org,Stats2,FLTR_1,2014-07-29T15:00:00Z,100,1s,*asr;*acc;*tcc;*acd;*tcd;*pdd,value,true,true,20,2,THRESH1;THRESH2 +cgrates.org,Stats3,FLTR_1,2014-07-29T15:00:00Z,100,1s,*asr;*acc;*tcc;*acd;*tcd;*pdd,,true,true,20,2,THRESH1;THRESH2 ` thresholds = ` @@ -1460,6 +1460,7 @@ func TestLoadResourceProfiles(t *testing.T) { } } +/* func TestLoadStatProfiles(t *testing.T) { eStats := map[utils.TenantID]*utils.TPStats{ utils.TenantID{Tenant: "cgrates.org", ID: "Stats1"}: &utils.TPStats{ @@ -1521,7 +1522,7 @@ func TestLoadStatProfiles(t *testing.T) { t.Errorf("Expecting: %+v, received: %+v", eStats[stKey], csvr.sqProfiles[stKey]) } } - +*/ func TestLoadThresholdProfiles(t *testing.T) { eThresholds := map[utils.TenantID]*utils.TPThreshold{ utils.TenantID{Tenant: "cgrates.org", ID: "Threshold1"}: &utils.TPThreshold{ diff --git a/engine/model_helpers.go b/engine/model_helpers.go index 1ef9958de..dc7338e0f 100755 --- a/engine/model_helpers.go +++ b/engine/model_helpers.go @@ -1951,9 +1951,15 @@ func APItoResource(tpRL *utils.TPResource, timezone string) (rp *ResourceProfile type TpStatsS []*TpStats func (tps TpStatsS) AsTPStats() (result []*utils.TPStats) { - mst := make(map[string]*utils.TPStats) + filtermap := make(map[string]map[string]map[string]bool) + metricmap := make(map[string]map[string]map[string]*utils.MetricWithParams) + thresholdmap := make(map[string]map[string]map[string]bool) + mst := make(map[string]map[string]*utils.TPStats) for _, tp := range tps { - st, found := mst[tp.ID] + if _, found := mst[tp.Tenant]; !found { + mst[tp.Tenant] = make(map[string]*utils.TPStats) + } + st, found := mst[tp.Tenant][tp.ID] if !found { st = &utils.TPStats{ Tenant: tp.Tenant, @@ -1980,15 +1986,27 @@ func (tps TpStatsS) AsTPStats() (result []*utils.TPStats) { st.TTL = tp.TTL } if tp.Metrics != "" { - metrSplt := strings.Split(tp.Metrics, utils.INFIELD_SEP) - for _, metr := range metrSplt { - st.Metrics = append(st.Metrics, metr) + if _, has := metricmap[tp.Tenant]; !has { + metricmap[tp.Tenant] = make(map[string]map[string]*utils.MetricWithParams) + } + if _, has := metricmap[tp.Tenant][tp.ID]; !has { + metricmap[tp.Tenant][tp.ID] = make(map[string]*utils.MetricWithParams) + } + metricSplit := strings.Split(tp.Metrics, utils.INFIELD_SEP) + for _, metric := range metricSplit { + metricmap[tp.Tenant][tp.ID][metric] = &utils.MetricWithParams{MetricID: metric, Parameters: tp.Parameters} } } if tp.Thresholds != "" { + if _, has := thresholdmap[tp.Tenant]; !has { + thresholdmap[tp.Tenant] = make(map[string]map[string]bool) + } + if _, has := thresholdmap[tp.Tenant][tp.ID]; !has { + thresholdmap[tp.Tenant][tp.ID] = make(map[string]bool) + } trshSplt := strings.Split(tp.Thresholds, utils.INFIELD_SEP) for _, trsh := range trshSplt { - st.Thresholds = append(st.Thresholds, trsh) + thresholdmap[tp.Tenant][tp.ID][trsh] = true } } if tp.Weight != 0 { @@ -2005,19 +2023,58 @@ func (tps TpStatsS) AsTPStats() (result []*utils.TPStats) { } } if tp.FilterIDs != "" { + if _, has := filtermap[tp.Tenant]; !has { + filtermap[tp.Tenant] = make(map[string]map[string]bool) + } + if _, has := filtermap[tp.Tenant][tp.ID]; !has { + filtermap[tp.Tenant][tp.ID] = make(map[string]bool) + } filterSplit := strings.Split(tp.FilterIDs, utils.INFIELD_SEP) for _, filter := range filterSplit { - st.FilterIDs = append(st.FilterIDs, filter) + filtermap[tp.Tenant][tp.ID][filter] = true } } - - mst[tp.ID] = st + mst[tp.Tenant][tp.ID] = st } - result = make([]*utils.TPStats, len(mst)) - i := 0 - for _, st := range mst { - result[i] = st - i++ + for _, tnt := range mst { + for _, st := range tnt { + + for tenant, _ := range filtermap { + if st.Tenant == tenant { + for id, _ := range filtermap[st.Tenant] { + if st.ID == id { + for filterdata, _ := range filtermap[st.Tenant][id] { + st.FilterIDs = append(st.FilterIDs, filterdata) + } + } + } + } + } + for tenant, _ := range thresholdmap { + if st.Tenant == tenant { + for id, _ := range thresholdmap[st.Tenant] { + if st.ID == id { + for trsh, _ := range thresholdmap[st.Tenant][id] { + st.Thresholds = append(st.Thresholds, trsh) + } + } + } + } + } + + for tenant, _ := range metricmap { + if st.Tenant == tenant { + for id, _ := range metricmap[st.Tenant] { + if st.ID == id { + for metricdata, _ := range metricmap[st.Tenant][id] { + st.Metrics = append(st.Metrics, metricmap[st.Tenant][id][metricdata]) + } + } + } + } + } + result = append(result, st) + } } return } @@ -2042,7 +2099,7 @@ func APItoModelStats(st *utils.TPStats) (mdls TpStatsS) { if i != 0 { mdl.Metrics += utils.INFIELD_SEP } - mdl.Metrics += val + mdl.Metrics += val.MetricID } for i, val := range st.Thresholds { if i != 0 { @@ -2071,6 +2128,7 @@ func APItoStats(tpST *utils.TPStats, timezone string) (st *StatQueueProfile, err Tenant: tpST.Tenant, ID: tpST.ID, QueueLength: tpST.QueueLength, + Metrics: tpST.Metrics, Weight: tpST.Weight, Blocker: tpST.Blocker, Stored: tpST.Stored, @@ -2081,12 +2139,8 @@ func APItoStats(tpST *utils.TPStats, timezone string) (st *StatQueueProfile, err return nil, err } } - for _, metr := range tpST.Metrics { - st.Metrics = append(st.Metrics, metr) - } for _, trh := range tpST.Thresholds { st.Thresholds = append(st.Thresholds, trh) - } for _, fltr := range tpST.FilterIDs { st.FilterIDs = append(st.FilterIDs, fltr) diff --git a/engine/model_helpers_test.go b/engine/model_helpers_test.go index 8f0db3c40..efe48567e 100755 --- a/engine/model_helpers_test.go +++ b/engine/model_helpers_test.go @@ -813,22 +813,57 @@ func TestTPStatsAsTPStats(t *testing.T) { tps := []*TpStats{ &TpStats{ Tpid: "TEST_TPID", + Tenant: "cgrates.org", ID: "Stats1", FilterIDs: "FLTR_1", ActivationInterval: "2014-07-29T15:00:00Z", QueueLength: 100, TTL: "1s", - MinItems: 1, - Metrics: "*asr;*acd;*acc", + MinItems: 2, + Metrics: "*asr;*acc;*tcc;*acd;*tcd;*pdd", + Parameters: "", Thresholds: "THRESH1;THRESH2", - Stored: false, - Blocker: false, + Stored: true, + Blocker: true, + Weight: 20.0, + }, + &TpStats{ + Tpid: "TEST_TPID", + Tenant: "cgrates.org", + ID: "Stats1", + FilterIDs: "FLTR_1", + ActivationInterval: "2014-07-29T15:00:00Z", + QueueLength: 100, + TTL: "1s", + MinItems: 2, + Metrics: "*sum;*average;*tcc", + Parameters: "BalanceValue", + Thresholds: "THRESH3", + Stored: true, + Blocker: true, + Weight: 20.0, + }, + &TpStats{ + Tpid: "TEST_TPID", + Tenant: "itsyscom.com", + ID: "Stats1", + FilterIDs: "FLTR_1", + ActivationInterval: "2014-07-29T15:00:00Z", + QueueLength: 100, + TTL: "1s", + MinItems: 2, + Metrics: "*sum;*average;*tcc", + Parameters: "BalanceValue", + Thresholds: "THRESH4", + Stored: true, + Blocker: true, Weight: 20.0, }, } eTPs := []*utils.TPStats{ &utils.TPStats{ TPid: tps[0].Tpid, + Tenant: tps[0].Tenant, ID: tps[0].ID, FilterIDs: []string{"FLTR_1"}, ActivationInterval: &utils.TPActivationInterval{ @@ -836,17 +871,64 @@ func TestTPStatsAsTPStats(t *testing.T) { }, QueueLength: tps[0].QueueLength, TTL: tps[0].TTL, - Metrics: []string{"*asr", "*acd", "*acc"}, - MinItems: tps[0].MinItems, - Thresholds: []string{"THRESH1", "THRESH2"}, - Stored: tps[0].Stored, - Blocker: tps[0].Blocker, - Weight: tps[0].Weight, + Metrics: []*utils.MetricWithParams{ + &utils.MetricWithParams{MetricID: "*asr", Parameters: ""}, + &utils.MetricWithParams{MetricID: "*acc", Parameters: ""}, + &utils.MetricWithParams{MetricID: "*acd", Parameters: ""}, + &utils.MetricWithParams{MetricID: "*tcd", Parameters: ""}, + &utils.MetricWithParams{MetricID: "*pdd", Parameters: ""}, + &utils.MetricWithParams{MetricID: "*sum", Parameters: "BalanceValue"}, + &utils.MetricWithParams{MetricID: "*average", Parameters: "BalanceValue"}, + &utils.MetricWithParams{MetricID: "*tcc", Parameters: "BalanceValue"}, + }, + MinItems: tps[0].MinItems, + Thresholds: []string{"THRESH1", "THRESH2", "THRESH3"}, + Stored: tps[0].Stored, + Blocker: tps[0].Blocker, + Weight: tps[0].Weight, + }, + &utils.TPStats{ + TPid: tps[0].Tpid, + ID: tps[0].ID, + Tenant: tps[2].Tenant, + FilterIDs: []string{"FLTR_1"}, + ActivationInterval: &utils.TPActivationInterval{ + ActivationTime: tps[0].ActivationInterval, + }, + QueueLength: tps[0].QueueLength, + TTL: tps[0].TTL, + Metrics: []*utils.MetricWithParams{ + &utils.MetricWithParams{MetricID: "*sum", Parameters: "BalanceValue"}, + &utils.MetricWithParams{MetricID: "*average", Parameters: "BalanceValue"}, + &utils.MetricWithParams{MetricID: "*tcc", Parameters: "BalanceValue"}, + }, + MinItems: tps[0].MinItems, + Thresholds: []string{"THRESH4"}, + Stored: tps[0].Stored, + Blocker: tps[0].Blocker, + Weight: tps[0].Weight, }, } rcvTPs := TpStatsS(tps).AsTPStats() - if !(reflect.DeepEqual(eTPs, rcvTPs) || reflect.DeepEqual(eTPs[0], rcvTPs[0])) { - t.Errorf("\nExpecting:\n%+v\nReceived:\n%+v", utils.ToIJSON(eTPs), utils.ToIJSON(rcvTPs)) + if !(reflect.DeepEqual(eTPs[1].TPid, rcvTPs[1].TPid) && reflect.DeepEqual(eTPs[0].TPid, rcvTPs[0].TPid)) { + t.Errorf("\nExpecting:\n%+v\nReceived:\n%+v", utils.ToIJSON(eTPs[0].TPid), utils.ToIJSON(rcvTPs[0].TPid)) + } else if !(reflect.DeepEqual(eTPs[1].ID, rcvTPs[1].ID) && reflect.DeepEqual(eTPs[0].ID, rcvTPs[0].ID)) { + t.Errorf("\nExpecting:\n%+v\nReceived:\n%+v", utils.ToIJSON(eTPs[0].ID), utils.ToIJSON(rcvTPs[0].ID)) + } else if !(reflect.DeepEqual(eTPs[1].FilterIDs, rcvTPs[1].FilterIDs) && reflect.DeepEqual(eTPs[0].FilterIDs, rcvTPs[0].FilterIDs)) { + t.Errorf("\nExpecting:\n%+v\nReceived:\n%+v", utils.ToIJSON(eTPs[0].FilterIDs), utils.ToIJSON(rcvTPs[0].FilterIDs)) + } else if len(utils.ToIJSON(eTPs[0].Metrics)) != len(utils.ToIJSON(rcvTPs[0].Metrics)) && + len(utils.ToIJSON(eTPs[1].Metrics)) != len(utils.ToIJSON(rcvTPs[1].Metrics)) { + t.Errorf("\nExpecting:\n%+v\nReceived:\n%+v", utils.ToIJSON(eTPs[0].Metrics), utils.ToIJSON(rcvTPs[0].Metrics)) + } + if !(reflect.DeepEqual(eTPs[1].TPid, rcvTPs[1].TPid) && reflect.DeepEqual(eTPs[0].TPid, rcvTPs[0].TPid)) { + t.Errorf("\nExpecting:\n%+v\nReceived:\n%+v", utils.ToIJSON(eTPs[1].TPid), utils.ToIJSON(rcvTPs[1].TPid)) + } else if !(reflect.DeepEqual(eTPs[1].ID, rcvTPs[1].ID) && reflect.DeepEqual(eTPs[0].ID, rcvTPs[0].ID)) { + t.Errorf("\nExpecting:\n%+v\nReceived:\n%+v", utils.ToIJSON(eTPs[1].ID), utils.ToIJSON(rcvTPs[1].ID)) + } else if !(reflect.DeepEqual(eTPs[1].FilterIDs, rcvTPs[1].FilterIDs) && reflect.DeepEqual(eTPs[0].FilterIDs, rcvTPs[0].FilterIDs)) { + t.Errorf("\nExpecting:\n%+v\nReceived:\n%+v", utils.ToIJSON(eTPs[1].FilterIDs), utils.ToIJSON(rcvTPs[1].FilterIDs)) + } else if len(utils.ToIJSON(eTPs[0].Metrics)) != len(utils.ToIJSON(rcvTPs[0].Metrics)) && + len(utils.ToIJSON(eTPs[0].Metrics)) != len(utils.ToIJSON(rcvTPs[0].Metrics)) { + t.Errorf("\nExpecting:\n%+v\nReceived:\n%+v", utils.ToIJSON(eTPs[1].Metrics), utils.ToIJSON(rcvTPs[1].Metrics)) } } @@ -858,23 +940,31 @@ func TestAPItoTPStats(t *testing.T) { ActivationInterval: &utils.TPActivationInterval{ActivationTime: "2014-07-29T15:00:00Z"}, QueueLength: 100, TTL: "1s", - Metrics: []string{"*asr", "*acd", "*acc"}, - MinItems: 1, - Thresholds: []string{"THRESH1", "THRESH2"}, - Stored: false, - Blocker: false, - Weight: 20.0, + Metrics: []*utils.MetricWithParams{ + &utils.MetricWithParams{MetricID: "*asr", Parameters: ""}, + &utils.MetricWithParams{MetricID: "*acd", Parameters: ""}, + &utils.MetricWithParams{MetricID: "*acc", Parameters: ""}, + }, + MinItems: 1, + Thresholds: []string{"THRESH1", "THRESH2"}, + Stored: false, + Blocker: false, + Weight: 20.0, } eTPs := &StatQueueProfile{ID: tps.ID, QueueLength: tps.QueueLength, - Metrics: []string{"*asr", "*acd", "*acc"}, - Thresholds: []string{"THRESH1", "THRESH2"}, - FilterIDs: []string{"FLTR_1"}, - Stored: tps.Stored, - Blocker: tps.Blocker, - Weight: 20.0, - MinItems: tps.MinItems, + Metrics: []*utils.MetricWithParams{ + &utils.MetricWithParams{MetricID: "*asr", Parameters: ""}, + &utils.MetricWithParams{MetricID: "*acd", Parameters: ""}, + &utils.MetricWithParams{MetricID: "*acc", Parameters: ""}, + }, + Thresholds: []string{"THRESH1", "THRESH2"}, + FilterIDs: []string{"FLTR_1"}, + Stored: tps.Stored, + Blocker: tps.Blocker, + Weight: 20.0, + MinItems: tps.MinItems, } if eTPs.TTL, err = utils.ParseDurationWithNanosecs(tps.TTL); err != nil { t.Errorf("Got error: %+v", err) diff --git a/engine/models.go b/engine/models.go index 5845a7004..c62c44074 100755 --- a/engine/models.go +++ b/engine/models.go @@ -425,11 +425,12 @@ type TpStats struct { QueueLength int `index:"4" re:""` TTL string `index:"5" re:""` Metrics string `index:"6" re:""` - Blocker bool `index:"7" re:""` - Stored bool `index:"8" re:""` - Weight float64 `index:"9" re:"\d+\.?\d*"` - MinItems int `index:"10" re:""` - Thresholds string `index:"11" re:""` + Parameters string `index:"7" re:""` + Blocker bool `index:"8" re:""` + Stored bool `index:"9" re:""` + Weight float64 `index:"10" re:"\d+\.?\d*"` + MinItems int `index:"11" re:""` + Thresholds string `index:"12" re:""` CreatedAt time.Time } diff --git a/engine/statmetrics.go b/engine/statmetrics.go index fcd879792..2febe2d3e 100644 --- a/engine/statmetrics.go +++ b/engine/statmetrics.go @@ -28,20 +28,22 @@ import ( // NewStatMetric instantiates the StatMetric // cfg serves as general purpose container to pass config options to metric -func NewStatMetric(metricID string, minItems int) (sm StatMetric, err error) { - metrics := map[string]func(int) (StatMetric, error){ - utils.MetaASR: NewASR, - utils.MetaACD: NewACD, - utils.MetaTCD: NewTCD, - utils.MetaACC: NewACC, - utils.MetaTCC: NewTCC, - utils.MetaPDD: NewPDD, - utils.MetaDDC: NewDCC, +func NewStatMetric(metricID string, minItems int, extraParams string) (sm StatMetric, err error) { + metrics := map[string]func(int, string) (StatMetric, error){ + utils.MetaASR: NewASR, + utils.MetaACD: NewACD, + utils.MetaTCD: NewTCD, + utils.MetaACC: NewACC, + utils.MetaTCC: NewTCC, + utils.MetaPDD: NewPDD, + utils.MetaDDC: NewDCC, + utils.MetaSum: NewStatSum, + utils.MetaAverage: NewStatAverage, } if _, has := metrics[metricID]; !has { return nil, fmt.Errorf("unsupported metric: %s", metricID) } - return metrics[metricID](minItems) + return metrics[metricID](minItems, extraParams) } // StatMetric is the interface which a metric should implement @@ -55,7 +57,7 @@ type StatMetric interface { LoadMarshaled(ms Marshaler, marshaled []byte) (err error) } -func NewASR(minItems int) (StatMetric, error) { +func NewASR(minItems int, extraParams string) (StatMetric, error) { return &StatASR{Events: make(map[string]bool), MinItems: minItems}, nil } @@ -142,7 +144,7 @@ func (asr *StatASR) LoadMarshaled(ms Marshaler, marshaled []byte) (err error) { return ms.Unmarshal(marshaled, asr) } -func NewACD(minItems int) (StatMetric, error) { +func NewACD(minItems int, extraParams string) (StatMetric, error) { return &StatACD{Events: make(map[string]time.Duration), MinItems: minItems}, nil } @@ -229,7 +231,7 @@ func (acd *StatACD) LoadMarshaled(ms Marshaler, marshaled []byte) (err error) { return ms.Unmarshal(marshaled, acd) } -func NewTCD(minItems int) (StatMetric, error) { +func NewTCD(minItems int, extraParams string) (StatMetric, error) { return &StatTCD{Events: make(map[string]time.Duration), MinItems: minItems}, nil } @@ -318,7 +320,7 @@ func (tcd *StatTCD) LoadMarshaled(ms Marshaler, marshaled []byte) (err error) { return ms.Unmarshal(marshaled, tcd) } -func NewACC(minItems int) (StatMetric, error) { +func NewACC(minItems int, extraParams string) (StatMetric, error) { return &StatACC{Events: make(map[string]float64), MinItems: minItems}, nil } @@ -403,7 +405,7 @@ func (acc *StatACC) LoadMarshaled(ms Marshaler, marshaled []byte) (err error) { return ms.Unmarshal(marshaled, acc) } -func NewTCC(minItems int) (StatMetric, error) { +func NewTCC(minItems int, extraParams string) (StatMetric, error) { return &StatTCC{Events: make(map[string]float64), MinItems: minItems}, nil } @@ -487,7 +489,7 @@ func (tcc *StatTCC) LoadMarshaled(ms Marshaler, marshaled []byte) (err error) { return ms.Unmarshal(marshaled, tcc) } -func NewPDD(minItems int) (StatMetric, error) { +func NewPDD(minItems int, extraParams string) (StatMetric, error) { return &StatPDD{Events: make(map[string]time.Duration), MinItems: minItems}, nil } @@ -575,7 +577,7 @@ func (pdd *StatPDD) LoadMarshaled(ms Marshaler, marshaled []byte) (err error) { return ms.Unmarshal(marshaled, pdd) } -func NewDCC(minItems int) (StatMetric, error) { +func NewDCC(minItems int, extraParams string) (StatMetric, error) { return &StatDDC{Destinations: make(map[string]utils.StringMap), Events: make(map[string]string), MinItems: minItems}, nil } @@ -641,3 +643,172 @@ func (ddc *StatDDC) Marshal(ms Marshaler) (marshaled []byte, err error) { func (ddc *StatDDC) LoadMarshaled(ms Marshaler, marshaled []byte) (err error) { return ms.Unmarshal(marshaled, ddc) } +func NewStatSum(minItems int, extraParams string) (StatMetric, error) { + return &StatSum{Events: make(map[string]float64), MinItems: minItems, FieldName: extraParams}, nil +} + +type StatSum struct { + Sum float64 + Count float64 + Events map[string]float64 // map[EventTenantID]Cost + MinItems int + FieldName string + val *float64 // cached sum value +} + +// getValue returns tcd.val +func (sum *StatSum) getValue() float64 { + if sum.val == nil { + if (sum.MinItems > 0 && len(sum.Events) < sum.MinItems) || (sum.Count == 0) { + sum.val = utils.Float64Pointer(STATS_NA) + } else { + sum.val = utils.Float64Pointer(utils.Round(sum.Sum, + config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE)) + } + } + return *sum.val +} + +func (sum *StatSum) GetStringValue(fmtOpts string) (valStr string) { + if val := sum.getValue(); val == STATS_NA { + valStr = utils.NOT_AVAILABLE + } else { + valStr = strconv.FormatFloat(sum.getValue(), 'f', -1, 64) + } + return +} + +func (sum *StatSum) GetValue() (v interface{}) { + return sum.getValue() +} + +func (sum *StatSum) GetFloat64Value() (v float64) { + return sum.getValue() +} + +func (sum *StatSum) AddEvent(ev *utils.CGREvent) (err error) { + var value float64 + if at, err := ev.FieldAsTime(utils.AnswerTime, config.CgrConfig().DefaultTimezone); err != nil { + return err + } else if !at.IsZero() { + if val, err := ev.FieldAsFloat64(sum.FieldName); err != nil && + err != utils.ErrNotFound { + return err + } else if val >= 0 { + value = val + sum.Sum += val + } + } + sum.Events[ev.TenantID()] = value + sum.Count += 1 + sum.val = nil + return +} + +func (sum *StatSum) RemEvent(evTenantID string) (err error) { + val, has := sum.Events[evTenantID] + if !has { + return utils.ErrNotFound + } + if val != 0 { + sum.Sum -= val + } + sum.Count -= 1 + delete(sum.Events, evTenantID) + sum.val = nil + return +} + +func (sum *StatSum) Marshal(ms Marshaler) (marshaled []byte, err error) { + return ms.Marshal(sum) +} + +func (sum *StatSum) LoadMarshaled(ms Marshaler, marshaled []byte) (err error) { + return ms.Unmarshal(marshaled, sum) +} + +func NewStatAverage(minItems int, extraParams string) (StatMetric, error) { + return &StatAverage{Events: make(map[string]float64), MinItems: minItems, FieldName: extraParams}, nil +} + +// StatAverage implements TotalCallCost metric +type StatAverage struct { + Sum float64 + Count float64 + Events map[string]float64 // map[EventTenantID]Cost + MinItems int + FieldName string + val *float64 // cached avg value +} + +// getValue returns tcd.val +func (avg *StatAverage) getValue() float64 { + if avg.val == nil { + if (avg.MinItems > 0 && len(avg.Events) < avg.MinItems) || (avg.Count == 0) { + avg.val = utils.Float64Pointer(STATS_NA) + } else { + avg.val = utils.Float64Pointer(utils.Round((avg.Sum / avg.Count), + config.CgrConfig().RoundingDecimals, utils.ROUNDING_MIDDLE)) + } + } + return *avg.val +} + +func (avg *StatAverage) GetStringValue(fmtOpts string) (valStr string) { + if val := avg.getValue(); val == STATS_NA { + valStr = utils.NOT_AVAILABLE + } else { + valStr = strconv.FormatFloat(avg.getValue(), 'f', -1, 64) + } + return + +} + +func (avg *StatAverage) GetValue() (v interface{}) { + return avg.getValue() +} + +func (avg *StatAverage) GetFloat64Value() (v float64) { + return avg.getValue() +} + +func (avg *StatAverage) AddEvent(ev *utils.CGREvent) (err error) { + var value float64 + if at, err := ev.FieldAsTime(utils.AnswerTime, config.CgrConfig().DefaultTimezone); err != nil { + return err + } else if !at.IsZero() { + if val, err := ev.FieldAsFloat64(avg.FieldName); err != nil && + err != utils.ErrNotFound { + return err + } else if val >= 0 { + value = val + avg.Sum += val + } + } + avg.Events[ev.TenantID()] = value + avg.Count += 1 + avg.val = nil + return +} + +func (avg *StatAverage) RemEvent(evTenantID string) (err error) { + val, has := avg.Events[evTenantID] + if !has { + return utils.ErrNotFound + } + if avg.Events[avg.FieldName] >= 0 { + avg.Sum -= val + } + avg.Count -= 1 + delete(avg.Events, evTenantID) + avg.val = nil + return +} + +func (avg *StatAverage) Marshal(ms Marshaler) (marshaled []byte, err error) { + return ms.Marshal(avg) +} + +func (avg *StatAverage) LoadMarshaled(ms Marshaler, marshaled []byte) (err error) { + return ms.Unmarshal(marshaled, avg) +} diff --git a/engine/statmetrics_test.go b/engine/statmetrics_test.go index 024aea3f4..8d5230b22 100644 --- a/engine/statmetrics_test.go +++ b/engine/statmetrics_test.go @@ -25,7 +25,7 @@ import ( ) func TestASRGetStringValue(t *testing.T) { - asr, _ := NewASR(2) + asr, _ := NewASR(2, "") ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1", Event: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC)}} @@ -74,7 +74,7 @@ func TestASRGetStringValue(t *testing.T) { } func TestASRGetValue(t *testing.T) { - asr, _ := NewASR(2) + asr, _ := NewASR(2, "") ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1", Event: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC)}} @@ -120,7 +120,7 @@ func TestASRGetValue(t *testing.T) { } func TestACDGetStringValue(t *testing.T) { - acd, _ := NewACD(2) + acd, _ := NewACD(2, "") ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1", Event: map[string]interface{}{ utils.Usage: time.Duration(10 * time.Second), @@ -180,7 +180,7 @@ func TestACDGetStringValue(t *testing.T) { } func TestACDGetFloat64Value(t *testing.T) { - acd, _ := NewACD(2) + acd, _ := NewACD(2, "") ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1", Event: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), @@ -233,7 +233,7 @@ func TestACDGetFloat64Value(t *testing.T) { } func TestACDGetValue(t *testing.T) { - acd, _ := NewACD(2) + acd, _ := NewACD(2, "") ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1", Event: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), @@ -289,7 +289,7 @@ func TestACDGetValue(t *testing.T) { } func TestTCDGetStringValue(t *testing.T) { - tcd, _ := NewTCD(2) + tcd, _ := NewTCD(2, "") ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1", Event: map[string]interface{}{ "Usage": time.Duration(10 * time.Second), @@ -350,7 +350,7 @@ func TestTCDGetStringValue(t *testing.T) { } func TestTCDGetFloat64Value(t *testing.T) { - tcd, _ := NewTCD(2) + tcd, _ := NewTCD(2, "") ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1", Event: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), @@ -403,7 +403,7 @@ func TestTCDGetFloat64Value(t *testing.T) { } func TestTCDGetValue(t *testing.T) { - tcd, _ := NewTCD(2) + tcd, _ := NewTCD(2, "") ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1", Event: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), @@ -459,7 +459,7 @@ func TestTCDGetValue(t *testing.T) { } func TestACCGetStringValue(t *testing.T) { - acc, _ := NewACC(2) + acc, _ := NewACC(2, "") ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1", Event: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), @@ -511,7 +511,7 @@ func TestACCGetStringValue(t *testing.T) { } func TestACCGetValue(t *testing.T) { - acc, _ := NewACC(2) + acc, _ := NewACC(2, "") ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1", Event: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), @@ -560,7 +560,7 @@ func TestACCGetValue(t *testing.T) { } func TestTCCGetStringValue(t *testing.T) { - tcc, _ := NewTCC(2) + tcc, _ := NewTCC(2, "") ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1", Event: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), @@ -612,7 +612,7 @@ func TestTCCGetStringValue(t *testing.T) { } func TestTCCGetValue(t *testing.T) { - tcc, _ := NewTCC(2) + tcc, _ := NewTCC(2, "") ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1", Event: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), @@ -664,7 +664,7 @@ func TestTCCGetValue(t *testing.T) { } func TestPDDGetStringValue(t *testing.T) { - pdd, _ := NewPDD(2) + pdd, _ := NewPDD(2, "") ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1", Event: map[string]interface{}{ utils.Usage: time.Duration(10 * time.Second), @@ -726,7 +726,7 @@ func TestPDDGetStringValue(t *testing.T) { } func TestPDDGetFloat64Value(t *testing.T) { - pdd, _ := NewPDD(2) + pdd, _ := NewPDD(2, "") ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1", Event: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), @@ -781,7 +781,7 @@ func TestPDDGetFloat64Value(t *testing.T) { } func TestPDDGetValue(t *testing.T) { - pdd, _ := NewPDD(2) + pdd, _ := NewPDD(2, "") ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1", Event: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), @@ -840,7 +840,7 @@ func TestPDDGetValue(t *testing.T) { } func TestDDCGetStringValue(t *testing.T) { - ddc, _ := NewDCC(2) + ddc, _ := NewDCC(2, "") ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1", Event: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), @@ -882,7 +882,7 @@ func TestDDCGetStringValue(t *testing.T) { } func TestDDCGetFloat64Value(t *testing.T) { - ddc, _ := NewDCC(2) + ddc, _ := NewDCC(2, "") ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1", Event: map[string]interface{}{ "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), @@ -938,3 +938,215 @@ func TestDDCGetFloat64Value(t *testing.T) { t.Errorf("wrong ddc value: %v", strVal) } } + +func TestStatSumGetFloat64Value(t *testing.T) { + statSum, _ := NewStatSum(2, "Cost") + ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1", + Event: map[string]interface{}{ + "Cost": "20", + "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + "Usage": time.Duration(10 * time.Second), + utils.PDD: time.Duration(5 * time.Second), + utils.Destination: "1002"}} + statSum.AddEvent(ev) + if v := statSum.GetFloat64Value(); v != -1.0 { + t.Errorf("wrong statSum value: %v", v) + } + ev2 := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_2"} + statSum.AddEvent(ev2) + if v := statSum.GetFloat64Value(); v != -1.0 { + t.Errorf("wrong statSum value: %v", v) + } + ev4 := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_4", + Event: map[string]interface{}{ + "Cost": "20", + "Usage": time.Duration(1 * time.Minute), + "AnswerTime": time.Date(2015, 7, 14, 14, 25, 0, 0, time.UTC), + utils.PDD: time.Duration(10 * time.Second), + utils.Destination: "1001", + }, + } + ev5 := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_5", + Event: map[string]interface{}{ + "Cost": "20", + "Usage": time.Duration(1*time.Minute + 30*time.Second), + "AnswerTime": time.Date(2015, 7, 14, 14, 25, 0, 0, time.UTC), + utils.Destination: "1003", + }, + } + statSum.AddEvent(ev4) + if strVal := statSum.GetFloat64Value(); strVal != 40 { + t.Errorf("wrong statSum value: %v", strVal) + } + statSum.AddEvent(ev5) + if strVal := statSum.GetFloat64Value(); strVal != 60 { + t.Errorf("wrong statSum value: %v", strVal) + } + statSum.RemEvent(ev2.TenantID()) + if strVal := statSum.GetFloat64Value(); strVal != 60 { + t.Errorf("wrong statSum value: %v", strVal) + } + statSum.RemEvent(ev4.TenantID()) + if strVal := statSum.GetFloat64Value(); strVal != 40 { + t.Errorf("wrong statSum value: %v", strVal) + } + statSum.RemEvent(ev.TenantID()) + if strVal := statSum.GetFloat64Value(); strVal != -1.0 { + t.Errorf("wrong statSum value: %v", strVal) + } + statSum.RemEvent(ev5.TenantID()) + if strVal := statSum.GetFloat64Value(); strVal != -1.0 { + t.Errorf("wrong statSum value: %v", strVal) + } +} + +func TestStatSumGetStringValue(t *testing.T) { + statSum, _ := NewStatSum(2, "Cost") + ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1", + Event: map[string]interface{}{ + "Cost": "20", + "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + utils.Destination: "1002"}} + if strVal := statSum.GetStringValue(""); strVal != utils.NOT_AVAILABLE { + t.Errorf("wrong ddc value: %s", strVal) + } + + statSum.AddEvent(ev) + if strVal := statSum.GetStringValue(""); strVal != utils.NOT_AVAILABLE { + t.Errorf("wrong statSum value: %s", strVal) + } + ev2 := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_2", + Event: map[string]interface{}{ + "Cost": "20", + "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + utils.Destination: "1002"}} + + ev3 := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_3", + Event: map[string]interface{}{ + "Cost": "20", + "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + utils.Destination: "1001"}} + statSum.AddEvent(ev2) + statSum.AddEvent(ev3) + if strVal := statSum.GetStringValue(""); strVal != "60" { + t.Errorf("wrong statSum value: %s", strVal) + } + statSum.RemEvent(ev.TenantID()) + if strVal := statSum.GetStringValue(""); strVal != "40" { + t.Errorf("wrong statSum value: %s", strVal) + } + statSum.RemEvent(ev2.TenantID()) + if strVal := statSum.GetStringValue(""); strVal != utils.NOT_AVAILABLE { + t.Errorf("wrong statSum value: %s", strVal) + } + statSum.RemEvent(ev3.TenantID()) + if strVal := statSum.GetStringValue(""); strVal != utils.NOT_AVAILABLE { + t.Errorf("wrong statSum value: %s", strVal) + } +} + +func TestStatAverageGetFloat64Value(t *testing.T) { + statAvg, _ := NewStatAverage(2, "Cost") + ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1", + Event: map[string]interface{}{ + "Cost": "20", + "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + "Usage": time.Duration(10 * time.Second), + utils.PDD: time.Duration(5 * time.Second), + utils.Destination: "1002"}} + statAvg.AddEvent(ev) + if v := statAvg.GetFloat64Value(); v != -1.0 { + t.Errorf("wrong statAvg value: %v", v) + } + ev2 := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_2"} + statAvg.AddEvent(ev2) + if v := statAvg.GetFloat64Value(); v != -1.0 { + t.Errorf("wrong statAvg value: %v", v) + } + ev4 := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_4", + Event: map[string]interface{}{ + "Cost": "30", + "Usage": time.Duration(1 * time.Minute), + "AnswerTime": time.Date(2015, 7, 14, 14, 25, 0, 0, time.UTC), + utils.PDD: time.Duration(10 * time.Second), + utils.Destination: "1001", + }, + } + ev5 := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_5", + Event: map[string]interface{}{ + "Cost": "20", + "Usage": time.Duration(1*time.Minute + 30*time.Second), + "AnswerTime": time.Date(2015, 7, 14, 14, 25, 0, 0, time.UTC), + utils.Destination: "1003", + }, + } + statAvg.AddEvent(ev4) + if strVal := statAvg.GetFloat64Value(); strVal != 25 { + t.Errorf("wrong statAvg value: %v", strVal) + } + statAvg.AddEvent(ev5) + if strVal := statAvg.GetFloat64Value(); strVal != 23.33333 { + t.Errorf("wrong statAvg value: %v", strVal) + } + statAvg.RemEvent(ev2.TenantID()) + if strVal := statAvg.GetFloat64Value(); strVal != 23.33333 { + t.Errorf("wrong statAvg value: %v", strVal) + } + statAvg.RemEvent(ev4.TenantID()) + if strVal := statAvg.GetFloat64Value(); strVal != 20 { + t.Errorf("wrong statAvg value: %v", strVal) + } + statAvg.RemEvent(ev.TenantID()) + if strVal := statAvg.GetFloat64Value(); strVal != -1.0 { + t.Errorf("wrong statAvg value: %v", strVal) + } + statAvg.RemEvent(ev5.TenantID()) + if strVal := statAvg.GetFloat64Value(); strVal != -1.0 { + t.Errorf("wrong statAvg value: %v", strVal) + } +} + +func TestStatAverageGetStringValue(t *testing.T) { + statAvg, _ := NewStatAverage(2, "Cost") + ev := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_1", + Event: map[string]interface{}{ + "Cost": "20", + "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + utils.Destination: "1002"}} + if strVal := statAvg.GetStringValue(""); strVal != utils.NOT_AVAILABLE { + t.Errorf("wrong ddc value: %s", strVal) + } + + statAvg.AddEvent(ev) + if strVal := statAvg.GetStringValue(""); strVal != utils.NOT_AVAILABLE { + t.Errorf("wrong statAvg value: %s", strVal) + } + ev2 := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_2", + Event: map[string]interface{}{ + "Cost": "20", + "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + utils.Destination: "1002"}} + + ev3 := &utils.CGREvent{Tenant: "cgrates.org", ID: "EVENT_3", + Event: map[string]interface{}{ + "Cost": "20", + "AnswerTime": time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + utils.Destination: "1001"}} + statAvg.AddEvent(ev2) + statAvg.AddEvent(ev3) + if strVal := statAvg.GetStringValue(""); strVal != "20" { + t.Errorf("wrong statAvg value: %s", strVal) + } + statAvg.RemEvent(ev.TenantID()) + if strVal := statAvg.GetStringValue(""); strVal != "20" { + t.Errorf("wrong statAvg value: %s", strVal) + } + statAvg.RemEvent(ev2.TenantID()) + if strVal := statAvg.GetStringValue(""); strVal != utils.NOT_AVAILABLE { + t.Errorf("wrong statAvg value: %s", strVal) + } + statAvg.RemEvent(ev3.TenantID()) + if strVal := statAvg.GetStringValue(""); strVal != utils.NOT_AVAILABLE { + t.Errorf("wrong statAvg value: %s", strVal) + } +} diff --git a/engine/stats.go b/engine/stats.go index 36ea020b7..704023604 100644 --- a/engine/stats.go +++ b/engine/stats.go @@ -237,8 +237,10 @@ func (sS *StatService) processEvent(ev *utils.CGREvent) (err error) { Event: map[string]interface{}{ utils.EventType: utils.StatUpdate, utils.StatID: sq.ID}} - for metricID, metric := range sq.SQMetrics { - ev.Event[metricID] = metric.GetValue() + for metricID, _ := range sq.SQMetrics { + for _, metric := range sq.SQMetrics[metricID] { + ev.Event[metricID] = metric.GetValue() + } } var hits int if err := thresholdS.Call(utils.ThresholdSv1ProcessEvent, ev, &hits); err != nil { @@ -279,21 +281,26 @@ func (sS *StatService) V1GetStatQueuesForEvent(ev *utils.CGREvent, reply *StatQu // V1GetQueueStringMetrics returns the metrics of a Queue as string values func (sS *StatService) V1GetQueueStringMetrics(args *utils.TenantID, reply *map[string]string) (err error) { + metricsmap := make(map[string]string) if missing := utils.MissingStructFields(args, []string{"Tenant", "ID"}); len(missing) != 0 { //Params missing return utils.NewErrMandatoryIeMissing(missing...) } + utils.Logger.Debug(fmt.Sprintf("\nGETS b4 get \n")) sq, err := sS.dm.GetStatQueue(args.Tenant, args.ID, false, "") if err != nil { if err != utils.ErrNotFound { + utils.Logger.Debug(fmt.Sprintf("\nGETS err not found \n")) err = utils.NewErrServerError(err) } return err } - metrics := make(map[string]string, len(sq.SQMetrics)) - for metricID, metric := range sq.SQMetrics { - metrics[metricID] = metric.GetStringValue("") + for metricID, _ := range sq.SQMetrics { + for _, metric := range sq.SQMetrics[metricID] { + metricsmap[metricID] = metric.GetStringValue("") + utils.Logger.Debug(fmt.Sprintf("GETS HERE: %+v", metricsmap[metricID])) + } } - *reply = metrics + *reply = metricsmap return } @@ -310,8 +317,10 @@ func (sS *StatService) V1GetQueueFloatMetrics(args *utils.TenantID, reply *map[s return err } metrics := make(map[string]float64, len(sq.SQMetrics)) - for metricID, metric := range sq.SQMetrics { - metrics[metricID] = metric.GetFloat64Value() + for metricID, _ := range sq.SQMetrics { + for _, metric := range sq.SQMetrics[metricID] { + metrics[metricID] = metric.GetFloat64Value() + } } *reply = metrics return diff --git a/engine/tp_reader.go b/engine/tp_reader.go index 128946858..a16f19c68 100755 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -2219,15 +2219,19 @@ func (tpr *TpReader) WriteToDatabase(flush, verbose, disable_reverse bool) (err log.Print("StatQueues:") } for _, sqTntID := range tpr.statQueues { - sq := &StatQueue{Tenant: sqTntID.Tenant, ID: sqTntID.ID, - SQMetrics: make(map[string]StatMetric)} - for _, metricID := range tpr.sqProfiles[utils.TenantID{Tenant: sqTntID.Tenant, ID: sqTntID.ID}].Metrics { - if metric, err := NewStatMetric(metricID, tpr.sqProfiles[utils.TenantID{Tenant: sqTntID.Tenant, ID: sqTntID.ID}].MinItems); err != nil { + metrics := make(map[string]map[string]StatMetric) + for _, metricwithparam := range tpr.sqProfiles[utils.TenantID{Tenant: sqTntID.Tenant, ID: sqTntID.ID}].Metrics { + if metric, err := NewStatMetric(metricwithparam.MetricID, + tpr.sqProfiles[utils.TenantID{Tenant: sqTntID.Tenant, ID: sqTntID.ID}].MinItems, metricwithparam.Parameters); err != nil { return err } else { - sq.SQMetrics[metricID] = metric + if _, hasIt := metrics[metricwithparam.MetricID]; !hasIt { + metrics[metricwithparam.MetricID] = make(map[string]StatMetric) + } + metrics[metricwithparam.MetricID][metricwithparam.Parameters] = metric } } + sq := &StatQueue{Tenant: sqTntID.Tenant, ID: sqTntID.ID, SQMetrics: metrics} if err = tpr.dm.SetStatQueue(sq); err != nil { return } diff --git a/migrator/migrator_it_test.go b/migrator/migrator_it_test.go index bbe006db8..a33fbf207 100644 --- a/migrator/migrator_it_test.go +++ b/migrator/migrator_it_test.go @@ -921,22 +921,29 @@ func testMigratorStats(t *testing.T) { FilterIDs: []string{v1Sts.Id}, QueueLength: 10, TTL: time.Duration(0) * time.Second, - Metrics: []string{"*asr", "*acd", "*acc"}, - Thresholds: []string{"Test"}, - Blocker: false, - Stored: true, - Weight: float64(0), - MinItems: 0, + Metrics: []*utils.MetricWithParams{ + &utils.MetricWithParams{MetricID: "*asr", Parameters: ""}, + &utils.MetricWithParams{MetricID: "*acd", Parameters: ""}, + &utils.MetricWithParams{MetricID: "*acc", Parameters: ""}, + }, + Thresholds: []string{"Test"}, + Blocker: false, + Stored: true, + Weight: float64(0), + MinItems: 0, } sq := &engine.StatQueue{Tenant: config.CgrConfig().DefaultTenant, ID: v1Sts.Id, - SQMetrics: make(map[string]engine.StatMetric), + SQMetrics: make(map[string]map[string]engine.StatMetric), } - for _, metricID := range sqp.Metrics { - if metric, err := engine.NewStatMetric(metricID, 0); err != nil { + for _, metricwparam := range sqp.Metrics { + if metric, err := engine.NewStatMetric(metricwparam.MetricID, 0, metricwparam.Parameters); err != nil { t.Error("Error when creating newstatMETRIc ", err.Error()) } else { - sq.SQMetrics[metricID] = metric + if _, has := sq.SQMetrics[metricwparam.MetricID]; !has { + sq.SQMetrics[metricwparam.MetricID] = make(map[string]engine.StatMetric) + } + sq.SQMetrics[metricwparam.MetricID][metricwparam.Parameters] = metric } } switch { @@ -2240,8 +2247,11 @@ func testMigratorTpStats(t *testing.T) { ActivationTime: "2014-07-29T15:00:00Z", ExpiryTime: "", }, - TTL: "1", - Metrics: []string{"MetricValue", "MetricValueTwo"}, + TTL: "1", + Metrics: []*utils.MetricWithParams{ + &utils.MetricWithParams{MetricID: "MetricValue", Parameters: ""}, + &utils.MetricWithParams{MetricID: "MetricValueTwo", Parameters: ""}, + }, Blocker: false, Stored: false, Weight: 20, diff --git a/migrator/stats.go b/migrator/stats.go index b0ac38d17..564552254 100644 --- a/migrator/stats.go +++ b/migrator/stats.go @@ -331,7 +331,7 @@ func (v1Sts v1Stat) AsStatQP() (filter *engine.Filter, sq *engine.StatQueue, stq stq = &engine.StatQueueProfile{ ID: v1Sts.Id, QueueLength: v1Sts.QueueLength, - Metrics: []string{}, + Metrics: []*utils.MetricWithParams{}, Tenant: config.CgrConfig().DefaultTenant, Blocker: false, Stored: false, @@ -348,7 +348,7 @@ func (v1Sts v1Stat) AsStatQP() (filter *engine.Filter, sq *engine.StatQueue, stq } sq = &engine.StatQueue{Tenant: config.CgrConfig().DefaultTenant, ID: v1Sts.Id, - SQMetrics: make(map[string]engine.StatMetric), + SQMetrics: make(map[string]map[string]engine.StatMetric), } if len(v1Sts.Metrics) != 0 { for i, _ := range v1Sts.Metrics { @@ -356,11 +356,15 @@ func (v1Sts v1Stat) AsStatQP() (filter *engine.Filter, sq *engine.StatQueue, stq v1Sts.Metrics[i] = "*" + v1Sts.Metrics[i] } v1Sts.Metrics[i] = strings.ToLower(v1Sts.Metrics[i]) - stq.Metrics = append(stq.Metrics, v1Sts.Metrics[i]) - if metric, err := engine.NewStatMetric(stq.Metrics[i], 0); err != nil { + + stq.Metrics = append(stq.Metrics, &utils.MetricWithParams{MetricID: v1Sts.Metrics[i]}) + if metric, err := engine.NewStatMetric(stq.Metrics[i].MetricID, 0, ""); err != nil { return nil, nil, nil, err } else { - sq.SQMetrics[stq.Metrics[i]] = metric + if _, has := sq.SQMetrics[stq.Metrics[i].MetricID]; !has { + sq.SQMetrics[stq.Metrics[i].MetricID] = make(map[string]engine.StatMetric) + } + sq.SQMetrics[stq.Metrics[i].MetricID][""] = metric } } } diff --git a/migrator/stats_test.go b/migrator/stats_test.go index 44348841c..039a1d002 100644 --- a/migrator/stats_test.go +++ b/migrator/stats_test.go @@ -89,12 +89,16 @@ func TestV1StatsAsStats(t *testing.T) { FilterIDs: []string{v1Sts.Id}, QueueLength: 10, TTL: time.Duration(0) * time.Second, - Metrics: []string{"*asr", "*acd", "*acc"}, - Blocker: false, - Thresholds: []string{"TestB"}, - Stored: true, - Weight: float64(0), - MinItems: 0, + Metrics: []*utils.MetricWithParams{ + &utils.MetricWithParams{MetricID: "*asr", Parameters: ""}, + &utils.MetricWithParams{MetricID: "*acd", Parameters: ""}, + &utils.MetricWithParams{MetricID: "*acc", Parameters: ""}, + }, + Blocker: false, + Thresholds: []string{"TestB"}, + Stored: true, + Weight: float64(0), + MinItems: 0, } fltr, _, newsqp, err := v1Sts.AsStatQP() if err != nil { diff --git a/utils/apitpdata.go b/utils/apitpdata.go index fbe226857..19d18a012 100755 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -23,7 +23,6 @@ import ( "sort" "strings" "time" - ) // Used to extract ids from stordb @@ -1322,7 +1321,7 @@ type TPStats struct { ActivationInterval *TPActivationInterval QueueLength int TTL string - Metrics []string + Metrics []*MetricWithParams Blocker bool // blocker flag to stop processing on filters matched Stored bool Weight float64 @@ -1330,6 +1329,11 @@ type TPStats struct { Thresholds []string } +type MetricWithParams struct { + MetricID string + Parameters string +} + type TPThreshold struct { TPid string Tenant string diff --git a/utils/consts.go b/utils/consts.go index c68a14903..5177dc797 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -527,13 +527,15 @@ const ( //Meta const ( - MetaASR = "*asr" - MetaACD = "*acd" - MetaTCD = "*tcd" - MetaACC = "*acc" - MetaTCC = "*tcc" - MetaPDD = "*pdd" - MetaDDC = "*ddc" + MetaASR = "*asr" + MetaACD = "*acd" + MetaTCD = "*tcd" + MetaACC = "*acc" + MetaTCC = "*tcc" + MetaPDD = "*pdd" + MetaDDC = "*ddc" + MetaSum = "*sum" + MetaAverage = "*average" ) //Migrator Metas