diff --git a/apier/v1/apier.go b/apier/v1/apier.go index b735d2590..791d544cf 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -21,10 +21,13 @@ package apier import ( "errors" "fmt" + "strings" + "time" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/scheduler" "github.com/cgrates/cgrates/utils" + "github.com/cgrates/cgrates/cache2go" "path" ) @@ -415,7 +418,6 @@ func (self *ApierV1) AddAccount(attr AttrAddAccount, reply *string) error { } if attr.ActionTimingsId != "" { - engine.Logger.Debug(fmt.Sprintf("Querying for ActionTimingsId: %v", attr.ActionTimingsId)) if ats, err := self.AccountDb.GetActionTimings(attr.ActionTimingsId); err == nil { for _, at := range ats { engine.Logger.Debug(fmt.Sprintf("Found action timings: %v", at)) @@ -493,7 +495,7 @@ func (self *ApierV1) ReloadCache(attrs utils.ApiReloadCache, reply *string) erro rpfKeys[idx] = engine.RATING_PROFILE_PREFIX + rpfId } } - if len(attrs.RatingProfileIds) > 0 { + if len(attrs.ActionIds) > 0 { actKeys = make([]string, len(attrs.ActionIds)) for idx, actId := range attrs.ActionIds { actKeys[idx] = engine.ACTION_PREFIX + actId @@ -509,6 +511,47 @@ func (self *ApierV1) ReloadCache(attrs utils.ApiReloadCache, reply *string) erro return nil } +type AttrCacheStats struct { // Add in the future filters here maybe so we avoid counting complete cache +} + +func (self *ApierV1) GetCacheStats(attrs AttrCacheStats, reply *utils.CacheStats) error { + cs := new(utils.CacheStats) + cs.Destinations = cache2go.CountEntries(engine.DESTINATION_PREFIX) + cs.RatingPlans = cache2go.CountEntries(engine.RATING_PLAN_PREFIX) + cs.RatingProfiles = cache2go.CountEntries(engine.RATING_PROFILE_PREFIX) + cs.Actions = cache2go.CountEntries(engine.ACTION_PREFIX) + *reply = *cs + return nil +} + +func (self *ApierV1) GetCachedItemAge(attrs utils.AttrCachedItemAge, reply *time.Duration) error { + if missing := utils.MissingStructFields(&attrs, []string{"Category", "ItemId"}); len(missing) != 0 { + return fmt.Errorf("%s:%v", utils.ERR_MANDATORY_IE_MISSING, missing) + } + cacheKey := "" + switch attrs.Category { + case strings.TrimSuffix(utils.DESTINATIONS_CSV, ".csv"): + cacheKey = engine.DESTINATION_PREFIX + attrs.ItemId + case strings.TrimSuffix(utils.RATING_PLANS_CSV, ".csv"): + cacheKey = engine.RATING_PLAN_PREFIX + attrs.ItemId + case strings.TrimSuffix(utils.RATING_PROFILES_CSV, ".csv"): + cacheKey = engine.RATING_PROFILE_PREFIX + attrs.ItemId + case strings.TrimSuffix(utils.ACTIONS_CSV, ".csv"): + cacheKey = engine.ACTION_PREFIX + attrs.ItemId + } + if len(cacheKey) == 0 { + return fmt.Errorf("%s:Category", utils.ERR_MANDATORY_IE_MISSING) + } + //engine.Logger.Debug(fmt.Sprintf("Will query cache age with: %s", cacheKey)) + if age, err := cache2go.GetKeyAge(cacheKey); err != nil { + return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) + } else { + *reply = age + } + return nil +} + + type AttrLoadTPFromFolder struct { FolderPath string // Take files from folder absolute path DryRun bool // Do not write to database but parse only diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index e1ecb7d61..36fbfa4de 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -29,8 +29,9 @@ import ( "os/exec" "path" "reflect" - "testing" + "strings" "time" + "testing" ) // ToDo: Replace rpc.Client with internal rpc server and Apier using internal map as both data and stor so we can run the tests non-local @@ -797,6 +798,39 @@ func TestApierReloadCache(t *testing.T) { } } +func TestApierGetCacheStats(t *testing.T) { + if !*testLocal { + return + } + var rcvStats *utils.CacheStats + expectedStats := &utils.CacheStats{Destinations:4, RatingPlans: 1, RatingProfiles: 2, Actions: 1} + var args AttrCacheStats + if err := rater.Call("ApierV1.GetCacheStats", args, &rcvStats); err != nil { + t.Error("Got error on ApierV1.GetCacheStats: ", err.Error()) + } else if !reflect.DeepEqual(expectedStats, rcvStats) { + t.Errorf("Calling ApierV1.GetCacheStats expected: %v, received: %v", expectedStats, rcvStats) + } +} + +func TestApierGetCachedItemAge(t *testing.T) { + if !*testLocal { + return + } + var rcvAge *time.Duration + qryData := &utils.AttrCachedItemAge{Category: strings.TrimSuffix(utils.DESTINATIONS_CSV, ".csv"), ItemId: "+4917"} // Destinations are cached per prefix not id + if err := rater.Call("ApierV1.GetCachedItemAge", qryData, &rcvAge); err != nil { + t.Error("Got error on ApierV1.GetCachedItemAge: ", err.Error()) + } else if *rcvAge > time.Duration(2)*time.Second { + t.Errorf("Cache too old: %d", rcvAge) + } + qryData = &utils.AttrCachedItemAge{Category: strings.TrimSuffix(utils.RATING_PLANS_CSV, ".csv"), ItemId: "RETAIL1"} + if err := rater.Call("ApierV1.GetCachedItemAge", qryData, &rcvAge); err != nil { + t.Error("Got error on ApierV1.GetCachedItemAge: ", err.Error()) + } else if *rcvAge > time.Duration(2)*time.Second { + t.Errorf("Cache too old: %d", rcvAge) + } +} + // Test here GetDestination func TestApierGetDestination(t *testing.T) { if !*testLocal { diff --git a/utils/apitpdata.go b/utils/apitpdata.go index ca340ffb7..8e44e52cc 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -282,3 +282,16 @@ type ApiReloadCache struct { RatingProfileIds []string ActionIds []string } + +type CacheStats struct { + Destinations int + RatingPlans int + RatingProfiles int + Actions int +} + +type AttrCachedItemAge struct { + Category string // Item's category, same name as .csv files without extension + ItemId string // Item's identity tag +} +