diff --git a/apier/v1/resourcesv1.go b/apier/v1/resourcesv1.go index 427f600d8..a20d037a1 100644 --- a/apier/v1/resourcesv1.go +++ b/apier/v1/resourcesv1.go @@ -57,7 +57,21 @@ func (rsv1 *ResourceSv1) ReleaseResources(args utils.ArgRSv1ResourceUsage, reply return rsv1.rls.V1ReleaseResource(args, reply) } -//func for getResource +// GetResource returns a resource configuration +func (apierV1 *ApierV1) GetResource(arg utils.TenantID, reply *engine.Resource) error { + if missing := utils.MissingStructFields(&arg, []string{"Tenant", "ID"}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + if res, err := apierV1.DataManager.GetResource(arg.Tenant, arg.ID, true, true, utils.NonTransactional); err != nil { + if err.Error() != utils.ErrNotFound.Error() { + err = utils.NewErrServerError(err) + } + return err + } else { + *reply = *res + } + return nil +} // GetResourceProfile returns a resource configuration func (apierV1 *ApierV1) GetResourceProfile(arg utils.TenantID, reply *engine.ResourceProfile) error { diff --git a/engine/filters.go b/engine/filters.go index 0efaabadc..737f4642f 100644 --- a/engine/filters.go +++ b/engine/filters.go @@ -481,7 +481,7 @@ func (fltr *FilterRule) passStatS(dP config.DataProvider, for key, val := range statValues { ifaceStatValues[key] = val } - //convert convert into a NavigableMap so we can send it to passGreaterThan + //convert ifaceStatValues into a NavigableMap so we can send it to passGreaterThan nM := config.NewNavigableMap(ifaceStatValues) //split the type in exact 2 parts //special cases like *gt#sum#Usage @@ -538,17 +538,30 @@ func (fltr *FilterRule) passResourceS(dP config.DataProvider, if resourceS == nil || reflect.ValueOf(resourceS).IsNil() { return false, errors.New("Missing ResourceS information") } - // for _, resItem := range fltr.resourceItems { - // //take total usage for resource - - // //compose the newFilter - - // //send it to passGreaterThan - // // if val, err := fltr.passGreaterThan(nM); err != nil || !val { - // // //in case of error return false and error - // // //and in case of not pass return false and nil - // // return false, err - // // } - // } + for _, resItem := range fltr.resourceItems { + //take total usage for resource + var reply *Resource + if err := resourceS.Call(utils.ApierV1GetResource, + &utils.TenantID{Tenant: tenant, ID: resItem.ItemID}, &reply); err != nil { + return false, err + } + data := map[string]interface{}{ + utils.Usage: reply.totalUsage(), + } + //convert data into a NavigableMap so we can send it to passGreaterThan + nM := config.NewNavigableMap(data) + //compose the newFilter + fltr, err := NewFilterRule(resItem.FilterType, + utils.Usage, []string{resItem.FilterValue}) + if err != nil { + return false, err + } + // send it to passGreaterThan + if val, err := fltr.passGreaterThan(nM); err != nil || !val { + //in case of error return false and error + //and in case of not pass return false and nil + return false, err + } + } return true, nil } diff --git a/general_tests/filters_it_test.go b/general_tests/filters_it_test.go index ec394e7b3..4916a1d6e 100644 --- a/general_tests/filters_it_test.go +++ b/general_tests/filters_it_test.go @@ -52,6 +52,7 @@ var sTestsFltr = []func(t *testing.T){ testV1FltrPupulateThreshold, testV1FltrGetThresholdForEvent, testV1FltrGetThresholdForEvent2, + testV1FltrPopulateResources, testV1FltrStopEngine, } @@ -276,7 +277,6 @@ func testV1FltrPupulateThreshold(t *testing.T) { }, MaxHits: -1, MinSleep: time.Duration(1 * time.Millisecond), - Blocker: false, Weight: 10.0, ActionIDs: []string{"LOG"}, Async: true, @@ -344,10 +344,8 @@ func testV1FltrGetThresholdForEvent2(t *testing.T) { }, MaxHits: -1, MinSleep: time.Duration(1 * time.Millisecond), - Blocker: false, Weight: 10.0, ActionIDs: []string{"LOG"}, - Async: true, } if err := fltrRpc.Call("ApierV1.SetThresholdProfile", tPrfl, &result); err != nil { t.Error(err) @@ -368,6 +366,149 @@ func testV1FltrGetThresholdForEvent2(t *testing.T) { } } +func testV1FltrPopulateResources(t *testing.T) { + //create a resourceProfile + rlsConfig := &engine.ResourceProfile{ + Tenant: "cgrates.org", + ID: "ResTest", + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + ExpiryTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), + }, + UsageTTL: time.Duration(1) * time.Minute, + Limit: 10, + AllocationMessage: "MessageAllocation", + Stored: true, + Weight: 20, + ThresholdIDs: []string{utils.META_NONE}, + } + + var result string + if err := fltrRpc.Call("ApierV1.SetResourceProfile", rlsConfig, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + + var reply *engine.ResourceProfile + if err := fltrRpc.Call("ApierV1.GetResourceProfile", + &utils.TenantID{Tenant: "cgrates.org", ID: rlsConfig.ID}, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(reply, rlsConfig) { + t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(rlsConfig), utils.ToJSON(reply)) + } + + // Allocate 3 units for resource ResTest + argsRU := utils.ArgRSv1ResourceUsage{ + CGREvent: utils.CGREvent{ + Tenant: "cgrates.org", + Event: map[string]interface{}{ + "Account": "3001", + "Destination": "3002"}, + }, + UsageID: "651a8db2-4f67-4cf8-b622-169e8a482e21", + Units: 3, + } + if err := fltrRpc.Call(utils.ResourceSv1AllocateResources, + argsRU, &result); err != nil { + t.Error(err) + } + + //we allocate 3 units to resource and add a filter for Usages > 2 + //should match (3>2) + filter := &engine.Filter{ + Tenant: "cgrates.org", + ID: "FLTR_TH_Resource", + Rules: []*engine.FilterRule{ + { + Type: "*resources", + Values: []string{"*gt:ResTest:2.0"}, + }, + }, + } + + if err := fltrRpc.Call("ApierV1.SetFilter", filter, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + + tPrfl := &engine.ThresholdProfile{ + Tenant: "cgrates.org", + ID: "TH_ResTest", + FilterIDs: []string{"FLTR_TH_Resource", "*string:Account:2020"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), + ExpiryTime: time.Date(2014, 7, 14, 14, 35, 0, 0, time.UTC), + }, + MaxHits: -1, + MinSleep: time.Duration(1 * time.Millisecond), + Weight: 10.0, + ActionIDs: []string{"LOG"}, + Async: true, + } + if err := fltrRpc.Call("ApierV1.SetThresholdProfile", tPrfl, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + var rcvTh *engine.ThresholdProfile + if err := fltrRpc.Call("ApierV1.GetThresholdProfile", + &utils.TenantID{Tenant: tPrfl.Tenant, ID: tPrfl.ID}, &rcvTh); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(tPrfl, rcvTh) { + t.Errorf("Expecting: %+v, received: %+v", tPrfl, rcvTh) + } + + // check the event + tEv := utils.CGREvent{ + Tenant: "cgrates.org", + ID: "event1", + Event: map[string]interface{}{ + utils.Account: "2020"}, + } + var ids []string + eIDs := []string{"TH_ResTest"} + if err := fltrRpc.Call(utils.ThresholdSv1ProcessEvent, tEv, &ids); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(ids, eIDs) { + t.Errorf("Expecting ids: %s, received: %s", eIDs, ids) + } + + //change the filter + //we allocate 3 units to resource and add a filter for Usages < 2 + //should fail (3<2) + filter = &engine.Filter{ + Tenant: "cgrates.org", + ID: "FLTR_TH_Resource", + Rules: []*engine.FilterRule{ + { + Type: "*resources", + Values: []string{"*lt:ResTest:2.0"}, + }, + }, + } + + if err := fltrRpc.Call("ApierV1.SetFilter", filter, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + + //Overwrite the threshold + if err := fltrRpc.Call("ApierV1.SetThresholdProfile", tPrfl, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } + + //expect NotFound error because filter doesn't match + if err := fltrRpc.Call(utils.ThresholdSv1ProcessEvent, tEv, &ids); err == nil || + err.Error() != utils.ErrNotFound.Error() { + t.Error(err) + } +} + func testV1FltrStopEngine(t *testing.T) { if err := engine.KillEngine(accDelay); err != nil { t.Error(err) diff --git a/utils/consts.go b/utils/consts.go index aa728cf77..d693d4cb1 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -695,6 +695,7 @@ const ( ApierV1SetDispatcherProfile = "ApierV1.SetDispatcherProfile" ApierV1GetDispatcherProfile = "ApierV1.GetDispatcherProfile" ApierV1RemoveDispatcherProfile = "ApierV1.RemoveDispatcherProfile" + ApierV1GetResource = "ApierV1.GetResource" ) const (