diff --git a/apier/v1/api_interfaces.go b/apier/v1/api_interfaces.go index b76dce352..e0e6675ef 100644 --- a/apier/v1/api_interfaces.go +++ b/apier/v1/api_interfaces.go @@ -55,6 +55,14 @@ type ResourceSv1Interface interface { GetResourceWithConfig(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *engine.ResourceWithConfig) error } +type IPsV1Interface interface { + GetIPsForEvent(ctx *context.Context, args *utils.CGREvent, reply *engine.IPs) error + AuthorizeIPs(ctx *context.Context, args *utils.CGREvent, reply *string) error + AllocateIPs(ctx *context.Context, args *utils.CGREvent, reply *string) error + ReleaseIPs(ctx *context.Context, args *utils.CGREvent, reply *string) error + GetIP(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *engine.IP) error +} + type RouteSv1Interface interface { GetRoutes(ctx *context.Context, args *utils.CGREvent, reply *engine.SortedRoutesList) error GetRouteProfilesForEvent(ctx *context.Context, args *utils.CGREvent, reply *[]*engine.RouteProfile) error @@ -189,6 +197,8 @@ type ReplicatorSv1Interface interface { GetTiming(ctx *context.Context, id *utils.StringWithAPIOpts, reply *utils.TPTiming) error GetResource(ctx *context.Context, tntID *utils.TenantIDWithAPIOpts, reply *engine.Resource) error GetResourceProfile(ctx *context.Context, tntID *utils.TenantIDWithAPIOpts, reply *engine.ResourceProfile) error + GetIP(ctx *context.Context, tntID *utils.TenantIDWithAPIOpts, reply *engine.IP) error + GetIPProfile(ctx *context.Context, tntID *utils.TenantIDWithAPIOpts, reply *engine.IPProfile) error GetActionTriggers(ctx *context.Context, id *utils.StringWithAPIOpts, reply *engine.ActionTriggers) error GetSharedGroup(ctx *context.Context, id *utils.StringWithAPIOpts, reply *engine.SharedGroup) error GetActions(ctx *context.Context, id *utils.StringWithAPIOpts, reply *engine.Actions) error @@ -214,6 +224,8 @@ type ReplicatorSv1Interface interface { SetTiming(ctx *context.Context, tm *utils.TPTimingWithAPIOpts, reply *string) error SetResource(ctx *context.Context, rs *engine.ResourceWithAPIOpts, reply *string) error SetResourceProfile(ctx *context.Context, rs *engine.ResourceProfileWithAPIOpts, reply *string) error + SetIP(ctx *context.Context, rs *engine.IPWithAPIOpts, reply *string) error + SetIPProfile(ctx *context.Context, rs *engine.IPProfileWithAPIOpts, reply *string) error SetActionTriggers(ctx *context.Context, args *engine.SetActionTriggersArgWithAPIOpts, reply *string) error SetSharedGroup(ctx *context.Context, shg *engine.SharedGroupWithAPIOpts, reply *string) error SetActions(ctx *context.Context, args *engine.SetActionsArgsWithAPIOpts, reply *string) error @@ -237,6 +249,8 @@ type ReplicatorSv1Interface interface { RemoveTiming(ctx *context.Context, id *utils.StringWithAPIOpts, reply *string) error RemoveResource(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *string) error RemoveResourceProfile(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *string) error + RemoveIP(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *string) error + RemoveIPProfile(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *string) error RemoveActionTriggers(ctx *context.Context, id *utils.StringWithAPIOpts, reply *string) error RemoveSharedGroup(ctx *context.Context, id *utils.StringWithAPIOpts, reply *string) error RemoveActions(ctx *context.Context, id *utils.StringWithAPIOpts, reply *string) error diff --git a/apier/v1/api_interfaces_test.go b/apier/v1/api_interfaces_test.go index e46f4d277..87f1b2842 100644 --- a/apier/v1/api_interfaces_test.go +++ b/apier/v1/api_interfaces_test.go @@ -39,6 +39,11 @@ func TestResourceSv1Interface(t *testing.T) { _ = ResourceSv1Interface(NewResourceSv1(nil)) } +func TestIPsV1Interface(t *testing.T) { + _ = IPsV1Interface(NewDispatcherIPsV1(nil)) + _ = IPsV1Interface(NewIPsV1(nil)) +} + func TestRouteSv1Interface(t *testing.T) { _ = RouteSv1Interface(NewDispatcherRouteSv1(nil)) _ = RouteSv1Interface(NewRouteSv1(nil)) diff --git a/apier/v1/apier.go b/apier/v1/apier.go index 04845a8d8..f523722ef 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -1717,6 +1717,44 @@ func (apierSv1 *APIerSv1) ExportToFolder(ctx *context.Context, arg *utils.ArgExp } } csvWriter.Flush() + case utils.MetaIPs: + prfx := utils.IPProfilesPrefix + keys, err := apierSv1.DataManager.DataDB().GetKeysForPrefix(prfx) + if err != nil { + return err + } + if len(keys) == 0 { // if we don't find items we skip + continue + } + f, err := os.Create(path.Join(arg.Path, utils.IPsCsv)) + if err != nil { + return err + } + defer f.Close() + + csvWriter := csv.NewWriter(f) + csvWriter.Comma = utils.CSVSep + //write the header of the file + if err := csvWriter.Write(engine.IPMdls{}.CSVHeader()); err != nil { + return err + } + for _, key := range keys { + tntID := strings.SplitN(key[len(prfx):], utils.InInFieldSep, 2) + ipPrf, err := apierSv1.DataManager.GetIPProfile(tntID[0], tntID[1], + true, false, utils.NonTransactional) + if err != nil { + return err + } + for _, model := range engine.APItoModelIP( + engine.IPProfileToAPI(ipPrf)) { + if record, err := engine.CsvDump(model); err != nil { + return err + } else if err := csvWriter.Write(record); err != nil { + return err + } + } + } + csvWriter.Flush() case utils.MetaStats: prfx := utils.StatQueueProfilePrefix keys, err := apierSv1.DataManager.DataDB().GetKeysForPrefix(prfx) diff --git a/apier/v1/apier_it_test.go b/apier/v1/apier_it_test.go index 3ed8c540f..103acb335 100644 --- a/apier/v1/apier_it_test.go +++ b/apier/v1/apier_it_test.go @@ -1701,7 +1701,7 @@ func testApierResetDataAfterLoadFromFolder(t *testing.T) { expStats[utils.CacheRouteProfiles].Items = 2 expStats[utils.CacheThresholdProfiles].Items = 1 expStats[utils.CacheThresholds].Items = 1 - expStats[utils.CacheLoadIDs].Items = 35 + expStats[utils.CacheLoadIDs].Items = 38 expStats[utils.CacheTimings].Items = 12 expStats[utils.CacheThresholdFilterIndexes].Items = 5 expStats[utils.CacheThresholdFilterIndexes].Groups = 1 diff --git a/apier/v1/caches_it_test.go b/apier/v1/caches_it_test.go index 2ae1870f1..62378d323 100644 --- a/apier/v1/caches_it_test.go +++ b/apier/v1/caches_it_test.go @@ -163,7 +163,7 @@ func testCacheSAfterLoadFromFolder(t *testing.T) { expStats[utils.CacheRouteProfiles].Items = 2 expStats[utils.CacheThresholdProfiles].Items = 1 expStats[utils.CacheThresholds].Items = 1 - expStats[utils.CacheLoadIDs].Items = 35 + expStats[utils.CacheLoadIDs].Items = 38 expStats[utils.CacheTimings].Items = 12 expStats[utils.CacheThresholdFilterIndexes].Items = 5 expStats[utils.CacheThresholdFilterIndexes].Groups = 1 @@ -235,7 +235,7 @@ func testCacheSReload(t *testing.T) { expStats[utils.CacheRouteProfiles].Items = 2 expStats[utils.CacheThresholdProfiles].Items = 1 expStats[utils.CacheThresholds].Items = 1 - expStats[utils.CacheLoadIDs].Items = 35 + expStats[utils.CacheLoadIDs].Items = 38 expStats[utils.CacheTimings].Items = 12 expStats[utils.CacheThresholdFilterIndexes].Items = 5 expStats[utils.CacheThresholdFilterIndexes].Groups = 1 diff --git a/apier/v1/dispatcher.go b/apier/v1/dispatcher.go index 1fcd7b892..8d3624378 100644 --- a/apier/v1/dispatcher.go +++ b/apier/v1/dispatcher.go @@ -415,6 +415,45 @@ func (dRs *DispatcherResourceSv1) ReleaseResources(ctx *context.Context, args *u return dRs.dRs.ResourceSv1ReleaseResources(ctx, args, reply) } +func NewDispatcherIPsV1(dps *dispatchers.DispatcherService) *DispatcherIPsV1 { + return &DispatcherIPsV1{dRs: dps} +} + +// Exports RPC from RLs +type DispatcherIPsV1 struct { + dRs *dispatchers.DispatcherService +} + +// Ping implements IPsV1Ping +func (dRs *DispatcherIPsV1) Ping(ctx *context.Context, args *utils.CGREvent, reply *string) error { + return dRs.dRs.IPsV1Ping(ctx, args, reply) +} + +// GetIPsForEvent implements IPsV1GetIPsForEvent +func (dRs *DispatcherIPsV1) GetIPsForEvent(ctx *context.Context, args *utils.CGREvent, + reply *engine.IPs) error { + return dRs.dRs.IPsV1GetIPsForEvent(ctx, args, reply) +} + +func (dRs *DispatcherIPsV1) GetIP(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *engine.IP) error { + return dRs.dRs.IPsV1GetIP(ctx, args, reply) +} + +func (dRs *DispatcherIPsV1) AuthorizeIPs(ctx *context.Context, args *utils.CGREvent, + reply *string) error { + return dRs.dRs.IPsV1AuthorizeIPs(ctx, args, reply) +} + +func (dRs *DispatcherIPsV1) AllocateIPs(ctx *context.Context, args *utils.CGREvent, + reply *string) error { + return dRs.dRs.IPsV1AllocateIPs(ctx, args, reply) +} + +func (dRs *DispatcherIPsV1) ReleaseIPs(ctx *context.Context, args *utils.CGREvent, + reply *string) error { + return dRs.dRs.IPsV1ReleaseIPs(ctx, args, reply) +} + func NewDispatcherRouteSv1(dps *dispatchers.DispatcherService) *DispatcherRouteSv1 { return &DispatcherRouteSv1{dRoute: dps} } @@ -1132,6 +1171,16 @@ func (dS *DispatcherReplicatorSv1) GetResourceProfile(ctx *context.Context, tntI return dS.dS.ReplicatorSv1GetResourceProfile(ctx, tntID, reply) } +// GetIP +func (dS *DispatcherReplicatorSv1) GetIP(ctx *context.Context, tntID *utils.TenantIDWithAPIOpts, reply *engine.IP) error { + return dS.dS.ReplicatorSv1GetIP(ctx, tntID, reply) +} + +// GetIPProfile +func (dS *DispatcherReplicatorSv1) GetIPProfile(ctx *context.Context, tntID *utils.TenantIDWithAPIOpts, reply *engine.IPProfile) error { + return dS.dS.ReplicatorSv1GetIPProfile(ctx, tntID, reply) +} + // GetActionTriggers func (dS *DispatcherReplicatorSv1) GetActionTriggers(ctx *context.Context, id *utils.StringWithAPIOpts, reply *engine.ActionTriggers) error { return dS.dS.ReplicatorSv1GetActionTriggers(ctx, id, reply) @@ -1259,6 +1308,16 @@ func (dS *DispatcherReplicatorSv1) SetResourceProfile(ctx *context.Context, args return dS.dS.ReplicatorSv1SetResourceProfile(ctx, args, reply) } +// SetIP +func (dS *DispatcherReplicatorSv1) SetIP(ctx *context.Context, args *engine.IPWithAPIOpts, reply *string) error { + return dS.dS.ReplicatorSv1SetIP(ctx, args, reply) +} + +// SetIPProfile +func (dS *DispatcherReplicatorSv1) SetIPProfile(ctx *context.Context, args *engine.IPProfileWithAPIOpts, reply *string) error { + return dS.dS.ReplicatorSv1SetIPProfile(ctx, args, reply) +} + // SetActionTriggers func (dS *DispatcherReplicatorSv1) SetActionTriggers(ctx *context.Context, args *engine.SetActionTriggersArgWithAPIOpts, reply *string) error { return dS.dS.ReplicatorSv1SetActionTriggers(ctx, args, reply) @@ -1374,6 +1433,16 @@ func (dS *DispatcherReplicatorSv1) RemoveResourceProfile(ctx *context.Context, a return dS.dS.ReplicatorSv1RemoveResourceProfile(ctx, args, reply) } +// RemoveIP +func (dS *DispatcherReplicatorSv1) RemoveIP(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *string) error { + return dS.dS.ReplicatorSv1RemoveIP(ctx, args, reply) +} + +// RemoveIPProfile +func (dS *DispatcherReplicatorSv1) RemoveIPProfile(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *string) error { + return dS.dS.ReplicatorSv1RemoveIPProfile(ctx, args, reply) +} + // RemoveActionTriggers func (dS *DispatcherReplicatorSv1) RemoveActionTriggers(ctx *context.Context, args *utils.StringWithAPIOpts, reply *string) error { return dS.dS.ReplicatorSv1RemoveActionTriggers(ctx, args, reply) diff --git a/apier/v1/ips.go b/apier/v1/ips.go new file mode 100644 index 000000000..78e72b87c --- /dev/null +++ b/apier/v1/ips.go @@ -0,0 +1,165 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package v1 + +import ( + "fmt" + "time" + + "github.com/cgrates/birpc/context" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +func NewIPsV1(rls *engine.IPService) *IPsV1 { + return &IPsV1{rls: rls} +} + +type IPsV1 struct { + rls *engine.IPService +} + +// GetIPsForEvent returns IPs matching a specific event. +func (ip *IPsV1) GetIPsForEvent(ctx *context.Context, args *utils.CGREvent, reply *engine.IPs) error { + return ip.rls.V1GetIPsForEvent(ctx, args, reply) +} + +// AuthorizeIPs checks if there are limits imposed for event. +func (ip *IPsV1) AuthorizeIPs(ctx *context.Context, args *utils.CGREvent, reply *string) error { + return ip.rls.V1AuthorizeIPs(ctx, args, reply) +} + +// AllocateIPs records usage for an event. +func (ip *IPsV1) AllocateIPs(ctx *context.Context, args *utils.CGREvent, reply *string) error { + return ip.rls.V1AllocateIPs(ctx, args, reply) +} + +// V1TerminateIPUsage releases usage for an event +func (ip *IPsV1) ReleaseIPs(ctx *context.Context, args *utils.CGREvent, reply *string) error { + return ip.rls.V1ReleaseIPs(ctx, args, reply) +} + +// GetIP retrieves the specified IP from data_db. +func (ip *IPsV1) GetIP(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *engine.IP) error { + return ip.rls.V1GetIP(ctx, args, reply) +} + +// GetIPProfile retrieves the specificed IPProfile from data_db. +func (a *APIerSv1) GetIPProfile(ctx *context.Context, arg *utils.TenantID, reply *engine.IPProfile) error { + if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + tnt := arg.Tenant + if tnt == utils.EmptyString { + tnt = a.Config.GeneralCfg().DefaultTenant + } + if rcfg, err := a.DataManager.GetIPProfile(tnt, arg.ID, true, true, utils.NonTransactional); err != nil { + return utils.APIErrorHandler(err) + } else { + *reply = *rcfg + } + return nil +} + +// GetIPProfileIDs returns list of IPProfile IDs registered for a tenant. +func (a *APIerSv1) GetIPProfileIDs(ctx *context.Context, args *utils.PaginatorWithTenant, rsPrfIDs *[]string) error { + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = a.Config.GeneralCfg().DefaultTenant + } + prfx := utils.IPProfilesPrefix + tnt + utils.ConcatenatedKeySep + keys, err := a.DataManager.DataDB().GetKeysForPrefix(prfx) + if err != nil { + return err + } + if len(keys) == 0 { + return utils.ErrNotFound + } + retIDs := make([]string, len(keys)) + for i, key := range keys { + retIDs[i] = key[len(prfx):] + } + *rsPrfIDs = args.PaginateStringSlice(retIDs) + return nil +} + +// SetIPProfile persists the passed IPProfile to data_db. +func (a *APIerSv1) SetIPProfile(ctx *context.Context, arg *engine.IPProfileWithAPIOpts, reply *string) (err error) { + if missing := utils.MissingStructFields(arg.IPProfile, []string{utils.ID}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + if arg.Tenant == utils.EmptyString { + arg.Tenant = a.Config.GeneralCfg().DefaultTenant + } + if err = a.DataManager.SetIPProfile(arg.IPProfile, true); err != nil { + return utils.APIErrorHandler(err) + } + //generate a loadID for CacheIPProfiles and CacheIPs and store it in database + //make 1 insert for both IPProfile and IPs instead of 2 + loadID := time.Now().UnixNano() + if err = a.DataManager.SetLoadIDs( + map[string]int64{utils.CacheIPProfiles: loadID, + utils.CacheIPs: loadID}); err != nil { + return utils.APIErrorHandler(err) + } + // delay if needed before cache call + if a.Config.GeneralCfg().CachingDelay != 0 { + utils.Logger.Info(fmt.Sprintf(" Delaying cache call for %v", a.Config.GeneralCfg().CachingDelay)) + time.Sleep(a.Config.GeneralCfg().CachingDelay) + } + //handle caching for IPProfile + if err = a.CallCache(utils.IfaceAsString(arg.APIOpts[utils.CacheOpt]), arg.Tenant, utils.CacheIPProfiles, + arg.TenantID(), utils.EmptyString, &arg.FilterIDs, nil, arg.APIOpts); err != nil { + return utils.APIErrorHandler(err) + } + *reply = utils.OK + return nil +} + +// RemoveIPProfile removes the specified IPProfile from data_db. +func (a *APIerSv1) RemoveIPProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *string) error { + if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + tnt := arg.Tenant + if tnt == utils.EmptyString { + tnt = a.Config.GeneralCfg().DefaultTenant + } + if err := a.DataManager.RemoveIPProfile(tnt, arg.ID, true); err != nil { + return utils.APIErrorHandler(err) + } + // delay if needed before cache call + if a.Config.GeneralCfg().CachingDelay != 0 { + utils.Logger.Info(fmt.Sprintf(" Delaying cache call for %v", a.Config.GeneralCfg().CachingDelay)) + time.Sleep(a.Config.GeneralCfg().CachingDelay) + } + //handle caching for IPProfile + if err := a.CallCache(utils.IfaceAsString(arg.APIOpts[utils.CacheOpt]), tnt, utils.CacheIPProfiles, + utils.ConcatenatedKey(tnt, arg.ID), utils.EmptyString, nil, nil, arg.APIOpts); err != nil { + return utils.APIErrorHandler(err) + } + //generate a loadID for CacheIPProfiles and CacheIPs and store it in database + //make 1 insert for both IPProfile and IPs instead of 2 + loadID := time.Now().UnixNano() + if err := a.DataManager.SetLoadIDs(map[string]int64{utils.CacheIPProfiles: loadID, utils.CacheIPs: loadID}); err != nil { + return utils.APIErrorHandler(err) + } + *reply = utils.OK + return nil +} diff --git a/apier/v1/libapier.go b/apier/v1/libapier.go index e90711984..be2dc7f09 100644 --- a/apier/v1/libapier.go +++ b/apier/v1/libapier.go @@ -80,6 +80,8 @@ func (apierSv1 *APIerSv1) CallCache(cacheopt string, tnt, cacheID, itemID, group cacheIDs = append(cacheIDs, utils.CacheThresholds) case utils.CacheResourceProfiles: cacheIDs = append(cacheIDs, utils.CacheResources) + case utils.CacheIPProfiles: + cacheIDs = append(cacheIDs, utils.CacheIPs) case utils.CacheStatQueueProfiles: cacheIDs = append(cacheIDs, utils.CacheStatQueues) } @@ -104,6 +106,8 @@ func (apierSv1 *APIerSv1) composeArgsReload(tnt, cacheID, itemID string, filterI argCache[utils.CacheThresholds] = []string{itemID} case utils.CacheResourceProfiles: argCache[utils.CacheResources] = []string{itemID} + case utils.CacheIPProfiles: + argCache[utils.CacheIPs] = []string{itemID} case utils.CacheStatQueueProfiles: argCache[utils.CacheStatQueues] = []string{itemID} } diff --git a/apier/v1/precache_it_test.go b/apier/v1/precache_it_test.go index b04010482..d0767ea7a 100644 --- a/apier/v1/precache_it_test.go +++ b/apier/v1/precache_it_test.go @@ -183,6 +183,7 @@ func testPrecacheGetCacheStatsAfterRestart(t *testing.T) { utils.CacheDestinations: {Items: 5}, utils.CacheDispatchers: {}, utils.CacheEventResources: {}, + utils.CacheEventIPs: {}, utils.CacheFilters: {Items: 15}, utils.CacheRatingPlans: {Items: 4}, utils.CacheRatingProfiles: {Items: 5}, @@ -190,8 +191,11 @@ func testPrecacheGetCacheStatsAfterRestart(t *testing.T) { Items: 6, Groups: 1, }, + utils.CacheIPFilterIndexes: {}, utils.CacheResourceProfiles: {Items: 3}, utils.CacheResources: {Items: 3}, + utils.CacheIPProfiles: {}, + utils.CacheIPs: {}, utils.CacheReverseDestinations: {Items: 7}, utils.CacheRPCResponses: {}, utils.MetaSentryPeer: {}, diff --git a/apier/v1/replicator.go b/apier/v1/replicator.go index 3d1fa4300..06180a5fa 100644 --- a/apier/v1/replicator.go +++ b/apier/v1/replicator.go @@ -218,6 +218,28 @@ func (rplSv1 *ReplicatorSv1) GetResourceProfile(ctx *context.Context, tntID *uti return nil } +// GetIP is the remote method coresponding to the dataDb driver method +func (rplSv1 *ReplicatorSv1) GetIP(ctx *context.Context, tntID *utils.TenantIDWithAPIOpts, reply *engine.IP) error { + engine.UpdateReplicationFilters(utils.IPsPrefix, tntID.TenantID.TenantID(), utils.IfaceAsString(tntID.APIOpts[utils.RemoteHostOpt])) + rcv, err := rplSv1.dm.DataDB().GetIPDrv(tntID.Tenant, tntID.ID) + if err != nil { + return err + } + *reply = *rcv + return nil +} + +// GetIPProfile is the remote method coresponding to the dataDb driver method +func (rplSv1 *ReplicatorSv1) GetIPProfile(ctx *context.Context, tntID *utils.TenantIDWithAPIOpts, reply *engine.IPProfile) error { + engine.UpdateReplicationFilters(utils.IPProfilesPrefix, tntID.TenantID.TenantID(), utils.IfaceAsString(tntID.APIOpts[utils.RemoteHostOpt])) + rcv, err := rplSv1.dm.DataDB().GetIPProfileDrv(tntID.Tenant, tntID.ID) + if err != nil { + return err + } + *reply = *rcv + return nil +} + // GetActionTriggers is the remote method coresponding to the dataDb driver method func (rplSv1 *ReplicatorSv1) GetActionTriggers(ctx *context.Context, id *utils.StringWithAPIOpts, reply *engine.ActionTriggers) error { engine.UpdateReplicationFilters(utils.ActionTriggerPrefix, id.Arg, utils.IfaceAsString(id.APIOpts[utils.RemoteHostOpt])) @@ -588,6 +610,37 @@ func (rplSv1 *ReplicatorSv1) SetResource(ctx *context.Context, rs *engine.Resour return } +// SetIPProfile is the replication method coresponding to the dataDb driver method +func (rplSv1 *ReplicatorSv1) SetIPProfile(ctx *context.Context, ipp *engine.IPProfileWithAPIOpts, reply *string) (err error) { + if err = rplSv1.dm.DataDB().SetIPProfileDrv(ipp.IPProfile); err != nil { + return + } + // delay if needed before cache call + if rplSv1.v1.Config.GeneralCfg().CachingDelay != 0 { + utils.Logger.Info(fmt.Sprintf(" Delaying cache call for %v", rplSv1.v1.Config.GeneralCfg().CachingDelay)) + time.Sleep(rplSv1.v1.Config.GeneralCfg().CachingDelay) + } + if err = rplSv1.v1.CallCache(utils.IfaceAsString(ipp.APIOpts[utils.CacheOpt]), + ipp.Tenant, utils.CacheIPProfiles, ipp.TenantID(), utils.EmptyString, &ipp.FilterIDs, nil, ipp.APIOpts); err != nil { + return + } + *reply = utils.OK + return +} + +// SetIP is the replication method coresponding to the dataDb driver method +func (rplSv1 *ReplicatorSv1) SetIP(ctx *context.Context, ip *engine.IPWithAPIOpts, reply *string) (err error) { + if err = rplSv1.dm.DataDB().SetIPDrv(ip.IP); err != nil { + return + } + if err = rplSv1.v1.CallCache(utils.IfaceAsString(ip.APIOpts[utils.CacheOpt]), + ip.Tenant, utils.CacheIPs, ip.TenantID(), utils.EmptyString, nil, nil, ip.APIOpts); err != nil { + return + } + *reply = utils.OK + return +} + // SetActionTriggers is the replication method coresponding to the dataDb driver method func (rplSv1 *ReplicatorSv1) SetActionTriggers(ctx *context.Context, args *engine.SetActionTriggersArgWithAPIOpts, reply *string) (err error) { if err = rplSv1.dm.DataDB().SetActionTriggersDrv(args.Key, args.Attrs); err != nil { @@ -1013,6 +1066,37 @@ func (rplSv1 *ReplicatorSv1) RemoveResourceProfile(ctx *context.Context, args *u return } +// RemoveIP is the replication method coresponding to the dataDb driver method +func (rplSv1 *ReplicatorSv1) RemoveIP(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *string) (err error) { + if err = rplSv1.dm.DataDB().RemoveIPDrv(args.Tenant, args.ID); err != nil { + return + } + if err = rplSv1.v1.CallCache(utils.IfaceAsString(args.APIOpts[utils.CacheOpt]), + args.Tenant, utils.CacheIPs, args.TenantID.TenantID(), utils.EmptyString, nil, nil, args.APIOpts); err != nil { + return + } + *reply = utils.OK + return +} + +// RemoveIPProfile is the replication method coresponding to the dataDb driver method +func (rplSv1 *ReplicatorSv1) RemoveIPProfile(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *string) (err error) { + if err = rplSv1.dm.DataDB().RemoveIPProfileDrv(args.Tenant, args.ID); err != nil { + return + } + // delay if needed before cache call + if rplSv1.v1.Config.GeneralCfg().CachingDelay != 0 { + utils.Logger.Info(fmt.Sprintf(" Delaying cache call for %v", rplSv1.v1.Config.GeneralCfg().CachingDelay)) + time.Sleep(rplSv1.v1.Config.GeneralCfg().CachingDelay) + } + if err = rplSv1.v1.CallCache(utils.IfaceAsString(args.APIOpts[utils.CacheOpt]), + args.Tenant, utils.CacheIPProfiles, args.TenantID.TenantID(), utils.EmptyString, nil, nil, args.APIOpts); err != nil { + return + } + *reply = utils.OK + return +} + // RemoveActionTriggers is the replication method coresponding to the dataDb driver method func (rplSv1 *ReplicatorSv1) RemoveActionTriggers(ctx *context.Context, id *utils.StringWithAPIOpts, reply *string) (err error) { if err = rplSv1.dm.DataDB().RemoveActionTriggersDrv(id.Arg); err != nil { diff --git a/cmd/cgr-console/cgr-console_it_test.go b/cmd/cgr-console/cgr-console_it_test.go index a223e3310..814572d69 100644 --- a/cmd/cgr-console/cgr-console_it_test.go +++ b/cmd/cgr-console/cgr-console_it_test.go @@ -1200,6 +1200,10 @@ func testConsoleItCacheStats(t *testing.T) { "Items": 0., "Groups": 0., }, + "*tp_ips": map[string]any{ + "Items": 0., + "Groups": 0., + }, "*tp_routes": map[string]any{ "Items": 0., "Groups": 0., @@ -2263,6 +2267,7 @@ func testConsoleItCachePrecacheStatus(t *testing.T) { "*tp_rating_plans": "*ready", "*tp_rating_profiles": "*ready", "*tp_resources": "*ready", + "*tp_ips": "*ready", "*tp_routes": "*ready", "*tp_shared_groups": "*ready", "*tp_stats": "*ready", diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go index a2e300c62..f55c8ccb9 100644 --- a/cmd/cgr-engine/cgr-engine.go +++ b/cmd/cgr-engine/cgr-engine.go @@ -142,7 +142,7 @@ func initConfigSv1(internalConfigChan chan birpc.ClientConnector, } func startRPC(server *cores.Server, internalRaterChan, - internalCdrSChan, internalRsChan, internalStatSChan, + internalCdrSChan, internalRsChan, internalIPsChan, internalStatSChan, internalAttrSChan, internalChargerSChan, internalThdSChan, internalTrendSChan, internalSuplSChan, internalSMGChan, internalAnalyzerSChan, internalDispatcherSChan, internalLoaderSChan, internalRALsv1Chan, internalCacheSChan, @@ -158,6 +158,8 @@ func startRPC(server *cores.Server, internalRaterChan, internalSMGChan <- smg case rls := <-internalRsChan: internalRsChan <- rls + case ips := <-internalIPsChan: + internalIPsChan <- ips case statS := <-internalStatSChan: internalStatSChan <- statS case attrS := <-internalAttrSChan: @@ -434,6 +436,7 @@ func main() { internalTrendSChan := make(chan birpc.ClientConnector, 1) internalRankingSChan := make(chan birpc.ClientConnector, 1) internalResourceSChan := make(chan birpc.ClientConnector, 1) + internalIPsChan := make(chan birpc.ClientConnector, 1) internalRouteSChan := make(chan birpc.ClientConnector, 1) internalSchedulerSChan := make(chan birpc.ClientConnector, 1) internalRALsChan := make(chan birpc.ClientConnector, 1) @@ -456,6 +459,7 @@ func main() { utils.ConcatenatedKey(utils.MetaInternal, utils.MetaGuardian): internalGuardianSChan, utils.ConcatenatedKey(utils.MetaInternal, utils.MetaLoaders): internalLoaderSChan, utils.ConcatenatedKey(utils.MetaInternal, utils.MetaResources): internalResourceSChan, + utils.ConcatenatedKey(utils.MetaInternal, utils.MetaIPs): internalIPsChan, utils.ConcatenatedKey(utils.MetaInternal, utils.MetaResponder): internalResponderChan, utils.ConcatenatedKey(utils.MetaInternal, utils.MetaScheduler): internalSchedulerSChan, utils.ConcatenatedKey(utils.MetaInternal, utils.MetaSessionS): internalSessionSChan, @@ -498,6 +502,7 @@ func main() { utils.RadiusAgent: new(sync.WaitGroup), utils.RALService: new(sync.WaitGroup), utils.ResourceS: new(sync.WaitGroup), + utils.IPs: new(sync.WaitGroup), utils.ResponderS: new(sync.WaitGroup), utils.RouteS: new(sync.WaitGroup), utils.SchedulerS: new(sync.WaitGroup), @@ -590,6 +595,8 @@ func main() { internalRankingSChan, connManager, anz, srvDep) reS := services.NewResourceService(cfg, dmService, cacheS, filterSChan, server, internalResourceSChan, connManager, anz, srvDep) + ips := services.NewIPService(cfg, dmService, cacheS, filterSChan, server, + internalResourceSChan, connManager, anz, srvDep) routeS := services.NewRouteService(cfg, dmService, cacheS, filterSChan, server, internalRouteSChan, connManager, anz, srvDep) @@ -613,7 +620,7 @@ func main() { ldrs := services.NewLoaderService(cfg, dmService, filterSChan, server, internalLoaderSChan, connManager, anz, srvDep) - srvManager.AddServices(gvService, attrS, chrS, tS, stS, trS, rnS, reS, routeS, schS, rals, + srvManager.AddServices(gvService, attrS, chrS, tS, stS, trS, rnS, reS, ips, routeS, schS, rals, apiSv1, apiSv2, cdrS, smg, coreS, services.NewDNSAgent(cfg, filterSChan, shdChan, connManager, caps, srvDep), services.NewFreeswitchAgent(cfg, shdChan, connManager, srvDep), @@ -654,6 +661,7 @@ func main() { engine.IntRPC.AddInternalRPCClient(utils.GuardianSv1, internalGuardianSChan) engine.IntRPC.AddInternalRPCClient(utils.LoaderSv1, internalLoaderSChan) engine.IntRPC.AddInternalRPCClient(utils.ResourceSv1, internalResourceSChan) + engine.IntRPC.AddInternalRPCClient(utils.IPsV1, internalIPsChan) engine.IntRPC.AddInternalRPCClient(utils.Responder, internalResponderChan) engine.IntRPC.AddInternalRPCClient(utils.SchedulerSv1, internalSchedulerSChan) engine.IntRPC.AddInternalRPCClient(utils.SessionSv1, internalSessionSChan) @@ -681,7 +689,7 @@ func main() { // Serve rpc connections go startRPC(server, internalResponderChan, internalCDRServerChan, - internalResourceSChan, internalStatSChan, + internalResourceSChan, internalIPsChan, internalStatSChan, internalAttributeSChan, internalChargerSChan, internalThresholdSChan, internalTrendSChan, internalRouteSChan, internalSessionSChan, internalAnalyzerSChan, internalDispatcherSChan, internalLoaderSChan, internalRALsChan, diff --git a/config/config.go b/config/config.go index e1edd3ff7..590bbae82 100644 --- a/config/config.go +++ b/config/config.go @@ -182,6 +182,7 @@ func newCGRConfig(config []byte) (cfg *CGRConfig, err error) { cfg.apiBanCfg = new(APIBanCfg) cfg.sentryPeerCfg = new(SentryPeerCfg) cfg.coreSCfg = new(CoreSCfg) + cfg.ipsCfg = &IPsCfg{Opts: &IPsOpts{}} cfg.dfltEvExp = &EventExporterCfg{Opts: &EventExporterOpts{ Els: new(ElsOpts), SQL: new(SQLOpts), @@ -337,13 +338,14 @@ type CGRConfig struct { apiBanCfg *APIBanCfg // APIBan config sentryPeerCfg *SentryPeerCfg //SentryPeer config coreSCfg *CoreSCfg // CoreS config + ipsCfg *IPsCfg // IPs config cacheDP map[string]utils.MapStorage cacheDPMux sync.RWMutex } -var posibleLoaderTypes = utils.NewStringSet([]string{utils.MetaAttributes, - utils.MetaResources, utils.MetaFilters, utils.MetaStats, +var possibleLoaderTypes = utils.NewStringSet([]string{utils.MetaAttributes, + utils.MetaResources, utils.MetaIPs, utils.MetaFilters, utils.MetaStats, utils.MetaRoutes, utils.MetaThresholds, utils.MetaChargers, utils.MetaDispatchers, utils.MetaDispatcherHosts}) @@ -375,7 +377,9 @@ func (cfg *CGRConfig) loadFromJSONCfg(jsnCfg *CgrJsonCfg) (err error) { cfg.loadLoaderCgrCfg, cfg.loadMigratorCgrCfg, cfg.loadTLSCgrCfg, cfg.loadAnalyzerCgrCfg, cfg.loadApierCfg, cfg.loadErsCfg, cfg.loadEesCfg, cfg.loadSIPAgentCfg, cfg.loadRegistrarCCfg, cfg.loadJanusAgentCfg, - cfg.loadConfigSCfg, cfg.loadAPIBanCgrCfg, cfg.loadSentryPeerCgrCfg, cfg.loadCoreSCfg} { + cfg.loadConfigSCfg, cfg.loadAPIBanCgrCfg, cfg.loadSentryPeerCgrCfg, + cfg.loadCoreSCfg, cfg.loadIPsCfg, + } { if err = loadFunc(jsnCfg); err != nil { return } @@ -864,6 +868,15 @@ func (cfg *CGRConfig) loadConfigSCfg(jsnCfg *CgrJsonCfg) (err error) { return cfg.configSCfg.loadFromJSONCfg(jsnConfigSCfg) } +// loadIPsCfg loads the IPs section of the configuration. +func (cfg *CGRConfig) loadIPsCfg(jsnCfg *CgrJsonCfg) error { + jsnIPsCfg, err := jsnCfg.IPsJsonCfg() + if err != nil { + return err + } + return cfg.ipsCfg.loadFromJSONCfg(jsnIPsCfg) +} + // SureTaxCfg use locking to retrieve the configuration, possibility later for runtime reload func (cfg *CGRConfig) SureTaxCfg() *SureTaxCfg { cfg.lks[SURETAX_JSON].Lock() @@ -1196,6 +1209,13 @@ func (cfg *CGRConfig) CoreSCfg() *CoreSCfg { return cfg.coreSCfg } +// IPsCfg returns the IPs configuration. +func (cfg *CGRConfig) IPsCfg() *IPsCfg { + cfg.lks[IPsJSON].Lock() + defer cfg.lks[IPsJSON].Unlock() + return cfg.ipsCfg +} + // GetReloadChan returns the reload chanel for the given section func (cfg *CGRConfig) GetReloadChan(sectID string) chan struct{} { return cfg.rldChans[sectID] @@ -1306,6 +1326,7 @@ func (cfg *CGRConfig) getLoadFunctions() map[string]func(*CgrJsonCfg) error { APIBanCfgJson: cfg.loadAPIBanCgrCfg, SentryPeerCfgJson: cfg.loadSentryPeerCgrCfg, CoreSCfgJson: cfg.loadCoreSCfg, + IPsJSON: cfg.loadIPsCfg, } } @@ -1464,7 +1485,7 @@ func (cfg *CGRConfig) reloadSections(sections ...string) { subsystemsThatNeedDataDB := utils.NewStringSet([]string{DATADB_JSN, SCHEDULER_JSN, RALS_JSN, CDRS_JSN, SessionSJson, ATTRIBUTE_JSN, ChargerSCfgJson, RESOURCES_JSON, STATS_JSON, THRESHOLDS_JSON, - RouteSJson, LoaderJson, DispatcherSJson, ApierS, + RouteSJson, LoaderJson, DispatcherSJson, ApierS, IPsJSON, }) subsystemsThatNeedStorDB := utils.NewStringSet([]string{STORDB_JSN, RALS_JSN, CDRS_JSN, ApierS}) needsDataDB := false @@ -1563,6 +1584,8 @@ func (cfg *CGRConfig) reloadSections(sections ...string) { cfg.rldChans[SIPAgentJson] <- struct{}{} case RegistrarCJson: cfg.rldChans[RegistrarCJson] <- struct{}{} + case IPsJSON: + cfg.rldChans[IPsJSON] <- struct{}{} } } } @@ -1616,6 +1639,7 @@ func (cfg *CGRConfig) AsMapInterface(separator string) (mp map[string]any) { TemplatesJson: cfg.templates.AsMapInterface(separator), ConfigSJson: cfg.configSCfg.AsMapInterface(), CoreSCfgJson: cfg.coreSCfg.AsMapInterface(), + IPsJSON: cfg.ipsCfg.AsMapInterface(), } } @@ -1784,6 +1808,8 @@ func (cfg *CGRConfig) V1GetConfig(ctx *context.Context, args *SectionWithAPIOpts mp = cfg.AnalyzerSCfg().AsMapInterface() case CoreSCfgJson: mp = cfg.CoreSCfg().AsMapInterface() + case IPsJSON: + mp = cfg.IPsCfg().AsMapInterface() default: return errors.New("Invalid section") } @@ -1958,6 +1984,8 @@ func (cfg *CGRConfig) V1GetConfigAsJSON(ctx *context.Context, args *SectionWithA mp = cfg.AnalyzerSCfg().AsMapInterface() case CoreSCfgJson: mp = cfg.CoreSCfg().AsMapInterface() + case IPsJSON: + mp = cfg.IPsCfg().AsMapInterface() default: return errors.New("Invalid section") } @@ -2059,6 +2087,7 @@ func (cfg *CGRConfig) Clone() (cln *CGRConfig) { apiBanCfg: cfg.apiBanCfg.Clone(), sentryPeerCfg: cfg.sentryPeerCfg.Clone(), coreSCfg: cfg.coreSCfg.Clone(), + ipsCfg: cfg.ipsCfg.Clone(), cacheDP: make(map[string]utils.MapStorage), } diff --git a/config/config_defaults.go b/config/config_defaults.go index ef695e4fd..fcc55a377 100644 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -115,6 +115,8 @@ const CGRATES_CFG_JSON = ` "*timings": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, "*resource_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, "*resources": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, + "*ip_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, + "*ips": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, "*ranking_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, "*rankings": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, "*trend_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, @@ -132,6 +134,7 @@ const CGRATES_CFG_JSON = ` "*load_ids": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, "*versions": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, "*resource_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, + "*ip_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, "*stat_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, "*threshold_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, "*route_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, @@ -216,6 +219,7 @@ const CGRATES_CFG_JSON = ` "*tp_action_triggers": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, "*tp_account_actions": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, "*tp_resources": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, + "*tp_ips": {"limit": -1, "ttl": "", "static_ttl": false, "remote": false, "replicate": false}, "*tp_stats": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, "*tp_rankings": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, "*tp_trends": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, @@ -309,6 +313,9 @@ const CGRATES_CFG_JSON = ` "*resource_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control resource profiles caching "*resources": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control resources caching "*event_resources": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // matching resources to events + "*ip_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control ip profiles caching + "*ips": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control ips caching + "*event_ips": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // matching ips to events "*trend_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control trend profiles caching "*trends": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control trends caching "*ranking_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // ranking profiles @@ -324,6 +331,7 @@ const CGRATES_CFG_JSON = ` "*dispatcher_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control dispatcher profile caching "*dispatcher_hosts": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control dispatcher hosts caching "*resource_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // control resource filter indexes caching + "*ip_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // control ip filter indexes caching "*stat_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // control stat filter indexes caching "*threshold_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // control threshold filter indexes caching "*route_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // control route filter indexes caching @@ -1026,6 +1034,22 @@ const CGRATES_CFG_JSON = ` {"tag": "ThresholdIDs", "path": "ThresholdIDs", "type": "*variable", "value": "~*req.10"}, ], }, + { + "type": "*ips", // data source type + "file_name": "IPs.csv", // file name in the tp_in_dir + "fields": [ + {"tag": "Tenant", "path": "Tenant", "type": "*variable", "value": "~*req.0", "mandatory": true}, + {"tag": "ID", "path": "ID", "type": "*variable", "value": "~*req.1", "mandatory": true}, + {"tag": "FilterIDs", "path": "FilterIDs", "type": "*variable", "value": "~*req.2"}, + {"tag": "ActivationInterval", "path": "ActivationInterval", "type": "*variable", "value": "~*req.3"}, + {"tag": "TTL", "path": "TTL", "type": "*variable", "value": "~*req.4"}, + {"tag": "Type", "path": "Type", "type": "*variable", "value": "~*req.5"}, + {"tag": "AddressPool", "path": "AddressPool", "type": "*variable", "value": "~*req.6"}, + {"tag": "Allocation", "path": "Allocation", "type": "*variable", "value": "~*req.7"}, + {"tag": "Stored", "path": "Stored", "type": "*variable", "value": "~*req.8"}, + {"tag": "Weight", "path": "Weight", "type": "*variable", "value": "~*req.9"} + ] + }, { "type": "*stats", // data source type "file_name": "Stats.csv", // file name in the tp_in_dir @@ -1423,6 +1447,7 @@ const CGRATES_CFG_JSON = ` "keys": [] }, + "sentrypeer":{ "client_id":"", "client_secret":"", @@ -1431,6 +1456,23 @@ const CGRATES_CFG_JSON = ` "numbers_url":"https://sentrypeer.com/api/phone-numbers", "audience":"https://sentrypeer.com/api", "grant_type":"client_credentials" +}, + + +"ips": { + "enabled": false, // enables the IPs service: + "store_interval": "", // dump cache regularly to dataDB, 0 - dump at start/shutdown: <""|$dur> + "indexed_selects": true, // enable profile matching exclusively on indexes + //"string_indexed_fields": [], // query indexes based on these fields for faster processing + "prefix_indexed_fields": [], // query indexes based on these fields for faster processing + "suffix_indexed_fields": [], // query indexes based on these fields for faster processing + "exists_indexed_fields": [], // query indexes based on these fields for faster processing + "nested_fields": false, // determines which field is checked when matching indexed filters(true: all; false: only the one on the first level) + "opts": { + "*usageID": "", + // "*ttl": "72h", + "*units": 1 + } } }` diff --git a/config/config_json.go b/config/config_json.go index 81b4cd542..7dde13296 100644 --- a/config/config_json.go +++ b/config/config_json.go @@ -70,6 +70,7 @@ const ( APIBanCfgJson = "apiban" SentryPeerCfgJson = "sentrypeer" CoreSCfgJson = "cores" + IPsJSON = "ips" ) var ( @@ -77,7 +78,7 @@ var ( CACHE_JSN, FilterSjsn, RALS_JSN, CDRS_JSN, ERsJson, SessionSJson, AsteriskAgentJSN, FreeSWITCHAgentJSN, KamailioAgentJSN, DA_JSN, RA_JSN, HttpAgentJson, DNSAgentJson, PrometheusAgentJSON, ATTRIBUTE_JSN, ChargerSCfgJson, RESOURCES_JSON, STATS_JSON, TRENDS_JSON, RANKINGS_JSON, THRESHOLDS_JSON, RouteSJson, LoaderJson, MAILER_JSN, SURETAX_JSON, CgrLoaderCfgJson, CgrMigratorCfgJson, DispatcherSJson, JanusAgentJson, - AnalyzerCfgJson, ApierS, EEsJson, SIPAgentJson, RegistrarCJson, TemplatesJson, ConfigSJson, APIBanCfgJson, SentryPeerCfgJson, CoreSCfgJson} + AnalyzerCfgJson, ApierS, EEsJson, SIPAgentJson, RegistrarCJson, TemplatesJson, ConfigSJson, APIBanCfgJson, SentryPeerCfgJson, CoreSCfgJson, IPsJSON} ) // Loads the json config out of io.Reader, eg other sources than file, maybe over http @@ -635,3 +636,15 @@ func (jsnCfg CgrJsonCfg) CoreSCfgJson() (*CoreSJsonCfg, error) { } return cfg, nil } + +func (jc CgrJsonCfg) IPsJsonCfg() (*IPsJsonCfg, error) { + rawCfg, hasKey := jc[IPsJSON] + if !hasKey { + return nil, nil + } + var pa *IPsJsonCfg + if err := json.Unmarshal(*rawCfg, &pa); err != nil { + return nil, err + } + return pa, nil +} diff --git a/config/config_json_test.go b/config/config_json_test.go index bd77c70e6..08e686e22 100644 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -126,6 +126,15 @@ func TestCacheJsonCfg(t *testing.T) { utils.CacheEventResources: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + utils.CacheIPProfiles: {Limit: utils.IntPointer(-1), + Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + utils.CacheIPs: {Limit: utils.IntPointer(-1), + Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + utils.CacheEventIPs: {Limit: utils.IntPointer(-1), + Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheStatQueueProfiles: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, @@ -171,6 +180,9 @@ func TestCacheJsonCfg(t *testing.T) { utils.CacheResourceFilterIndexes: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + utils.CacheIPFilterIndexes: {Limit: utils.IntPointer(-1), + Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheStatFilterIndexes: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, @@ -405,6 +417,13 @@ func TestDfDataDbJsonCfg(t *testing.T) { Ttl: utils.StringPointer(utils.EmptyString), Static_ttl: utils.BoolPointer(false), }, + utils.MetaIPProfiles: { + Replicate: utils.BoolPointer(false), + Remote: utils.BoolPointer(false), + Limit: utils.IntPointer(-1), + Ttl: utils.StringPointer(utils.EmptyString), + Static_ttl: utils.BoolPointer(false), + }, utils.MetaStatQueues: { Replicate: utils.BoolPointer(false), Remote: utils.BoolPointer(false), @@ -419,6 +438,13 @@ func TestDfDataDbJsonCfg(t *testing.T) { Ttl: utils.StringPointer(utils.EmptyString), Static_ttl: utils.BoolPointer(false), }, + utils.MetaIPs: { + Replicate: utils.BoolPointer(false), + Remote: utils.BoolPointer(false), + Limit: utils.IntPointer(-1), + Ttl: utils.StringPointer(utils.EmptyString), + Static_ttl: utils.BoolPointer(false), + }, utils.MetaStatQueueProfiles: { Replicate: utils.BoolPointer(false), Remote: utils.BoolPointer(false), @@ -532,6 +558,13 @@ func TestDfDataDbJsonCfg(t *testing.T) { Ttl: utils.StringPointer(utils.EmptyString), Static_ttl: utils.BoolPointer(false), }, + utils.CacheIPFilterIndexes: { + Replicate: utils.BoolPointer(false), + Remote: utils.BoolPointer(false), + Limit: utils.IntPointer(-1), + Ttl: utils.StringPointer(utils.EmptyString), + Static_ttl: utils.BoolPointer(false), + }, utils.CacheStatFilterIndexes: { Replicate: utils.BoolPointer(false), Remote: utils.BoolPointer(false), @@ -707,6 +740,13 @@ func TestDfStorDBJsonCfg(t *testing.T) { Ttl: utils.StringPointer(utils.EmptyString), Static_ttl: utils.BoolPointer(false), }, + utils.CacheTBLTPIPs: { + Replicate: utils.BoolPointer(false), + Remote: utils.BoolPointer(false), + Limit: utils.IntPointer(-1), + Ttl: utils.StringPointer(utils.EmptyString), + Static_ttl: utils.BoolPointer(false), + }, utils.CacheTBLTPStats: { Replicate: utils.BoolPointer(false), Remote: utils.BoolPointer(false), @@ -1473,6 +1513,54 @@ func TestDfLoaderJsonCfg(t *testing.T) { Value: utils.StringPointer("~*req.10")}, }, }, + { + Type: utils.StringPointer(utils.MetaIPs), + File_name: utils.StringPointer(utils.IPsCsv), + Fields: &[]*FcTemplateJsonCfg{ + {Tag: utils.StringPointer(utils.Tenant), + Path: utils.StringPointer(utils.Tenant), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.0"), + Mandatory: utils.BoolPointer(true)}, + {Tag: utils.StringPointer(utils.ID), + Path: utils.StringPointer(utils.ID), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.1"), + Mandatory: utils.BoolPointer(true)}, + {Tag: utils.StringPointer("FilterIDs"), + Path: utils.StringPointer("FilterIDs"), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.2")}, + {Tag: utils.StringPointer("ActivationInterval"), + Path: utils.StringPointer("ActivationInterval"), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.3")}, + {Tag: utils.StringPointer("TTL"), + Path: utils.StringPointer("TTL"), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.4")}, + {Tag: utils.StringPointer("Type"), + Path: utils.StringPointer("Type"), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.5")}, + {Tag: utils.StringPointer(utils.AddressPool), + Path: utils.StringPointer(utils.AddressPool), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.6")}, + {Tag: utils.StringPointer(utils.Allocation), + Path: utils.StringPointer(utils.Allocation), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.7")}, + {Tag: utils.StringPointer("Stored"), + Path: utils.StringPointer("Stored"), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.8")}, + {Tag: utils.StringPointer("Weight"), + Path: utils.StringPointer("Weight"), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.9")}, + }, + }, { Type: utils.StringPointer(utils.MetaStats), File_name: utils.StringPointer(utils.StatsCsv), diff --git a/config/config_test.go b/config/config_test.go index 9776bee9f..67e2c87f8 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -550,6 +550,12 @@ func TestCgrCfgJSONDefaultsCacheCFG(t *testing.T) { TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheEventResources: {Limit: -1, TTL: 0, Remote: false, StaticTTL: false}, + utils.CacheIPProfiles: {Limit: -1, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, + utils.CacheIPs: {Limit: -1, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, + utils.CacheEventIPs: {Limit: -1, + TTL: 0, Remote: false, StaticTTL: false}, utils.CacheStatQueueProfiles: {Limit: -1, TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheStatQueues: {Limit: -1, @@ -580,6 +586,8 @@ func TestCgrCfgJSONDefaultsCacheCFG(t *testing.T) { TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheResourceFilterIndexes: {Limit: -1, TTL: 0, Remote: false, StaticTTL: false, Precache: false}, + utils.CacheIPFilterIndexes: {Limit: -1, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheStatFilterIndexes: {Limit: -1, TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheThresholdFilterIndexes: {Limit: -1, @@ -2678,6 +2686,64 @@ func TestCgrLoaderCfgITDefaults(t *testing.T) { Layout: time.RFC3339}, }, }, + { + Type: utils.MetaIPs, + Filename: utils.IPsCsv, + Fields: []*FCTemplate{ + {Tag: "Tenant", + Path: "Tenant", + Type: utils.MetaVariable, + Value: NewRSRParsersMustCompile("~*req.0", utils.InfieldSep), + Mandatory: true, + Layout: time.RFC3339}, + {Tag: "ID", + Path: "ID", + Type: utils.MetaVariable, + Value: NewRSRParsersMustCompile("~*req.1", utils.InfieldSep), + Mandatory: true, + Layout: time.RFC3339}, + {Tag: "FilterIDs", + Path: "FilterIDs", + Type: utils.MetaVariable, + Value: NewRSRParsersMustCompile("~*req.2", utils.InfieldSep), + Layout: time.RFC3339}, + {Tag: "ActivationInterval", + Path: "ActivationInterval", + Type: utils.MetaVariable, + Value: NewRSRParsersMustCompile("~*req.3", utils.InfieldSep), + Layout: time.RFC3339}, + {Tag: "TTL", + Path: "TTL", + Type: utils.MetaVariable, + Value: NewRSRParsersMustCompile("~*req.4", utils.InfieldSep), + Layout: time.RFC3339}, + {Tag: "Type", + Path: "Type", + Type: utils.MetaVariable, + Value: NewRSRParsersMustCompile("~*req.5", utils.InfieldSep), + Layout: time.RFC3339}, + {Tag: utils.AddressPool, + Path: utils.AddressPool, + Type: utils.MetaVariable, + Value: NewRSRParsersMustCompile("~*req.6", utils.InfieldSep), + Layout: time.RFC3339}, + {Tag: utils.Allocation, + Path: utils.Allocation, + Type: utils.MetaVariable, + Value: NewRSRParsersMustCompile("~*req.7", utils.InfieldSep), + Layout: time.RFC3339}, + {Tag: "Stored", + Path: "Stored", + Type: utils.MetaVariable, + Value: NewRSRParsersMustCompile("~*req.8", utils.InfieldSep), + Layout: time.RFC3339}, + {Tag: "Weight", + Path: "Weight", + Type: utils.MetaVariable, + Value: NewRSRParsersMustCompile("~*req.9", utils.InfieldSep), + Layout: time.RFC3339}, + }, + }, { Type: utils.MetaStats, Filename: utils.StatsCsv, @@ -4778,7 +4844,7 @@ func TestV1GetConfigAsJSONGeneral(t *testing.T) { func TestV1GetConfigAsJSONDataDB(t *testing.T) { var reply string - expected := `{"data_db":{"db_host":"127.0.0.1","db_name":"10","db_password":"","db_port":6379,"db_type":"*redis","db_user":"cgrates","items":{"*account_action_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*accounts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*action_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*action_triggers":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*actions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*attribute_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*attribute_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*charger_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*charger_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*destinations":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_hosts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*filters":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*load_ids":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*ranking_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rankings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rating_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rating_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*resource_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*resource_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*resources":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*reverse_destinations":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*reverse_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*route_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*route_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*sessions_backup":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*shared_groups":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*stat_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*statqueue_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*statqueues":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*threshold_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*threshold_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*thresholds":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*timings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*trend_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*trends":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*versions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false}},"opts":{"internalDBBackupPath":"/var/lib/cgrates/internal_db/backup/datadb","internalDBDumpInterval":"0s","internalDBDumpPath":"/var/lib/cgrates/internal_db/datadb","internalDBFileSizeLimit":1073741824,"internalDBRewriteInterval":"0s","internalDBStartTimeout":"5m0s","mongoConnScheme":"mongodb","mongoQueryTimeout":"10s","redisCACertificate":"","redisClientCertificate":"","redisClientKey":"","redisCluster":false,"redisClusterOndownDelay":"0s","redisClusterSync":"5s","redisConnectAttempts":20,"redisConnectTimeout":"0s","redisMaxConns":10,"redisPoolPipelineLimit":0,"redisPoolPipelineWindow":"150µs","redisReadTimeout":"0s","redisSentinel":"","redisTLS":false,"redisWriteTimeout":"0s"},"remote_conn_id":"","remote_conns":[],"replication_cache":"","replication_conns":[],"replication_failed_dir":"","replication_filtered":false,"replication_interval":"0s"}}` + expected := `{"data_db":{"db_host":"127.0.0.1","db_name":"10","db_password":"","db_port":6379,"db_type":"*redis","db_user":"cgrates","items":{"*account_action_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*accounts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*action_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*action_triggers":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*actions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*attribute_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*attribute_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*charger_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*charger_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*destinations":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_hosts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*filters":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*ip_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*ip_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*ips":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*load_ids":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*ranking_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rankings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rating_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rating_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*resource_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*resource_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*resources":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*reverse_destinations":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*reverse_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*route_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*route_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*sessions_backup":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*shared_groups":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*stat_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*statqueue_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*statqueues":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*threshold_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*threshold_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*thresholds":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*timings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*trend_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*trends":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*versions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false}},"opts":{"internalDBBackupPath":"/var/lib/cgrates/internal_db/backup/datadb","internalDBDumpInterval":"0s","internalDBDumpPath":"/var/lib/cgrates/internal_db/datadb","internalDBFileSizeLimit":1073741824,"internalDBRewriteInterval":"0s","internalDBStartTimeout":"5m0s","mongoConnScheme":"mongodb","mongoQueryTimeout":"10s","redisCACertificate":"","redisClientCertificate":"","redisClientKey":"","redisCluster":false,"redisClusterOndownDelay":"0s","redisClusterSync":"5s","redisConnectAttempts":20,"redisConnectTimeout":"0s","redisMaxConns":10,"redisPoolPipelineLimit":0,"redisPoolPipelineWindow":"150µs","redisReadTimeout":"0s","redisSentinel":"","redisTLS":false,"redisWriteTimeout":"0s"},"remote_conn_id":"","remote_conns":[],"replication_cache":"","replication_conns":[],"replication_failed_dir":"","replication_filtered":false,"replication_interval":"0s"}}` cfgCgr := NewDefaultCGRConfig() if err := cfgCgr.V1GetConfigAsJSON(context.Background(), &SectionWithAPIOpts{Section: DATADB_JSN}, &reply); err != nil { t.Error(err) @@ -4789,7 +4855,7 @@ func TestV1GetConfigAsJSONDataDB(t *testing.T) { func TestV1GetConfigAsJSONStorDB(t *testing.T) { var reply string - expected := `{"stor_db":{"db_host":"127.0.0.1","db_name":"cgrates","db_password":"CGRateS.org","db_port":3306,"db_type":"*mysql","db_user":"cgrates","items":{"*cdrs":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*session_costs":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_account_actions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_action_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_action_triggers":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_actions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_attributes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_chargers":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_destination_rates":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_destinations":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_dispatcher_hosts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_dispatcher_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_filters":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rankings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rates":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rating_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rating_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_resources":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_routes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_shared_groups":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_stats":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_thresholds":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_timings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_trends":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*versions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false}},"opts":{"internalDBBackupPath":"/var/lib/cgrates/internal_db/backup/stordb","internalDBDumpInterval":"0s","internalDBDumpPath":"/var/lib/cgrates/internal_db/stordb","internalDBFileSizeLimit":1073741824,"internalDBRewriteInterval":"0s","internalDBStartTimeout":"5m0s","mongoConnScheme":"mongodb","mongoQueryTimeout":"10s","mysqlDSNParams":{},"mysqlLocation":"Local","pgSSLMode":"disable","pgSchema":"","sqlConnMaxLifetime":"0s","sqlLogLevel":3,"sqlMaxIdleConns":10,"sqlMaxOpenConns":100},"prefix_indexed_fields":[],"remote_conns":null,"replication_conns":null,"string_indexed_fields":[]}}` + expected := `{"stor_db":{"db_host":"127.0.0.1","db_name":"cgrates","db_password":"CGRateS.org","db_port":3306,"db_type":"*mysql","db_user":"cgrates","items":{"*cdrs":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*session_costs":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_account_actions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_action_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_action_triggers":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_actions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_attributes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_chargers":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_destination_rates":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_destinations":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_dispatcher_hosts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_dispatcher_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_filters":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_ips":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rankings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rates":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rating_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rating_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_resources":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_routes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_shared_groups":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_stats":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_thresholds":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_timings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_trends":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*versions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false}},"opts":{"internalDBBackupPath":"/var/lib/cgrates/internal_db/backup/stordb","internalDBDumpInterval":"0s","internalDBDumpPath":"/var/lib/cgrates/internal_db/stordb","internalDBFileSizeLimit":1073741824,"internalDBRewriteInterval":"0s","internalDBStartTimeout":"5m0s","mongoConnScheme":"mongodb","mongoQueryTimeout":"10s","mysqlDSNParams":{},"mysqlLocation":"Local","pgSSLMode":"disable","pgSchema":"","sqlConnMaxLifetime":"0s","sqlLogLevel":3,"sqlMaxIdleConns":10,"sqlMaxOpenConns":100},"prefix_indexed_fields":[],"remote_conns":null,"replication_conns":null,"string_indexed_fields":[]}}` cfgCgr := NewDefaultCGRConfig() if err := cfgCgr.V1GetConfigAsJSON(context.Background(), &SectionWithAPIOpts{Section: STORDB_JSN}, &reply); err != nil { t.Error(err) @@ -4811,7 +4877,7 @@ func TestV1GetConfigAsJSONTls(t *testing.T) { func TestV1GetConfigAsJSONTCache(t *testing.T) { var reply string - expected := `{"caches":{"partitions":{"*account_action_plans":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*action_plans":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*action_triggers":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*actions":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*apiban":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"2m0s"},"*attribute_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*attribute_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*caps_events":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*cdr_ids":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"10m0s"},"*charger_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*charger_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*closed_sessions":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"10s"},"*destinations":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*diameter_messages":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*dispatcher_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_hosts":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_loads":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_routes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatchers":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*event_charges":{"limit":0,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"10s"},"*event_resources":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*filters":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*load_ids":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*radius_packets":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*ranking_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rankings":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rating_plans":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rating_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*replication_hosts":{"limit":0,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*resource_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*resource_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*resources":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*reverse_destinations":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*reverse_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*route_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*route_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rpc_connections":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rpc_responses":{"limit":0,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"2s"},"*sentrypeer":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":true,"ttl":"24h0m0s"},"*shared_groups":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*stat_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*statqueue_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*statqueues":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*stir":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*threshold_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*threshold_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*thresholds":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*timings":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*trend_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*trends":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*uch":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"}},"remote_conns":[],"replication_conns":[]}}` + expected := `{"caches":{"partitions":{"*account_action_plans":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*action_plans":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*action_triggers":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*actions":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*apiban":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"2m0s"},"*attribute_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*attribute_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*caps_events":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*cdr_ids":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"10m0s"},"*charger_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*charger_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*closed_sessions":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"10s"},"*destinations":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*diameter_messages":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*dispatcher_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_hosts":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_loads":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_routes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatchers":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*event_charges":{"limit":0,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"10s"},"*event_ips":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*event_resources":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*filters":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*ip_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*ip_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*ips":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*load_ids":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*radius_packets":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*ranking_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rankings":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rating_plans":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rating_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*replication_hosts":{"limit":0,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*resource_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*resource_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*resources":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*reverse_destinations":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*reverse_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*route_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*route_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rpc_connections":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rpc_responses":{"limit":0,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"2s"},"*sentrypeer":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":true,"ttl":"24h0m0s"},"*shared_groups":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*stat_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*statqueue_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*statqueues":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*stir":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*threshold_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*threshold_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*thresholds":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*timings":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*trend_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*trends":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*uch":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"}},"remote_conns":[],"replication_conns":[]}}` cfgCgr := NewDefaultCGRConfig() if err := cfgCgr.V1GetConfigAsJSON(context.Background(), &SectionWithAPIOpts{Section: CACHE_JSN}, &reply); err != nil { t.Error(err) @@ -5066,7 +5132,7 @@ func TestV1GetConfigAsJSONDispatcherH(t *testing.T) { func TestV1GetConfigAsJSONLoaders(t *testing.T) { var reply string - expected := `{"loaders":[{"caches_conns":["*internal"],"data":[{"fields":[{"mandatory":true,"path":"Tenant","tag":"TenantID","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ProfileID","type":"*variable","value":"~*req.1"},{"path":"Contexts","tag":"Contexts","type":"*variable","value":"~*req.2"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.3"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.4"},{"path":"AttributeFilterIDs","tag":"AttributeFilterIDs","type":"*variable","value":"~*req.5"},{"path":"Path","tag":"Path","type":"*variable","value":"~*req.6"},{"path":"Type","tag":"Type","type":"*variable","value":"~*req.7"},{"path":"Value","tag":"Value","type":"*variable","value":"~*req.8"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.9"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.10"}],"file_name":"Attributes.csv","flags":null,"type":"*attributes"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Type","tag":"Type","type":"*variable","value":"~*req.2"},{"path":"Element","tag":"Element","type":"*variable","value":"~*req.3"},{"path":"Values","tag":"Values","type":"*variable","value":"~*req.4"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.5"}],"file_name":"Filters.csv","flags":null,"type":"*filters"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"UsageTTL","tag":"TTL","type":"*variable","value":"~*req.4"},{"path":"Limit","tag":"Limit","type":"*variable","value":"~*req.5"},{"path":"AllocationMessage","tag":"AllocationMessage","type":"*variable","value":"~*req.6"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.7"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.8"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.9"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.10"}],"file_name":"Resources.csv","flags":null,"type":"*resources"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"QueueLength","tag":"QueueLength","type":"*variable","value":"~*req.4"},{"path":"TTL","tag":"TTL","type":"*variable","value":"~*req.5"},{"path":"MinItems","tag":"MinItems","type":"*variable","value":"~*req.6"},{"path":"MetricIDs","tag":"MetricIDs","type":"*variable","value":"~*req.7"},{"path":"MetricFilterIDs","tag":"MetricFilterIDs","type":"*variable","value":"~*req.8"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.9"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.10"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.11"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.12"}],"file_name":"Stats.csv","flags":null,"type":"*stats"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"MaxHits","tag":"MaxHits","type":"*variable","value":"~*req.4"},{"path":"MinHits","tag":"MinHits","type":"*variable","value":"~*req.5"},{"path":"MinSleep","tag":"MinSleep","type":"*variable","value":"~*req.6"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.7"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.8"},{"path":"ActionIDs","tag":"ActionIDs","type":"*variable","value":"~*req.9"},{"path":"Async","tag":"Async","type":"*variable","value":"~*req.10"}],"file_name":"Thresholds.csv","flags":null,"type":"*thresholds"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"Sorting","tag":"Sorting","type":"*variable","value":"~*req.4"},{"path":"SortingParameters","tag":"SortingParameters","type":"*variable","value":"~*req.5"},{"path":"RouteID","tag":"RouteID","type":"*variable","value":"~*req.6"},{"path":"RouteFilterIDs","tag":"RouteFilterIDs","type":"*variable","value":"~*req.7"},{"path":"RouteAccountIDs","tag":"RouteAccountIDs","type":"*variable","value":"~*req.8"},{"path":"RouteRatingPlanIDs","tag":"RouteRatingPlanIDs","type":"*variable","value":"~*req.9"},{"path":"RouteResourceIDs","tag":"RouteResourceIDs","type":"*variable","value":"~*req.10"},{"path":"RouteStatIDs","tag":"RouteStatIDs","type":"*variable","value":"~*req.11"},{"path":"RouteWeight","tag":"RouteWeight","type":"*variable","value":"~*req.12"},{"path":"RouteBlocker","tag":"RouteBlocker","type":"*variable","value":"~*req.13"},{"path":"RouteParameters","tag":"RouteParameters","type":"*variable","value":"~*req.14"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.15"}],"file_name":"Routes.csv","flags":null,"type":"*routes"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"RunID","tag":"RunID","type":"*variable","value":"~*req.4"},{"path":"AttributeIDs","tag":"AttributeIDs","type":"*variable","value":"~*req.5"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.6"}],"file_name":"Chargers.csv","flags":null,"type":"*chargers"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Contexts","tag":"Contexts","type":"*variable","value":"~*req.2"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.3"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.4"},{"path":"Strategy","tag":"Strategy","type":"*variable","value":"~*req.5"},{"path":"StrategyParameters","tag":"StrategyParameters","type":"*variable","value":"~*req.6"},{"path":"ConnID","tag":"ConnID","type":"*variable","value":"~*req.7"},{"path":"ConnFilterIDs","tag":"ConnFilterIDs","type":"*variable","value":"~*req.8"},{"path":"ConnWeight","tag":"ConnWeight","type":"*variable","value":"~*req.9"},{"path":"ConnBlocker","tag":"ConnBlocker","type":"*variable","value":"~*req.10"},{"path":"ConnParameters","tag":"ConnParameters","type":"*variable","value":"~*req.11"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.12"}],"file_name":"DispatcherProfiles.csv","flags":null,"type":"*dispatchers"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Address","tag":"Address","type":"*variable","value":"~*req.2"},{"path":"Transport","tag":"Transport","type":"*variable","value":"~*req.3"},{"path":"ConnectAttempts","tag":"ConnectAttempts","type":"*variable","value":"~*req.4"},{"path":"Reconnects","tag":"Reconnects","type":"*variable","value":"~*req.5"},{"path":"MaxReconnectInterval","tag":"MaxReconnectInterval","type":"*variable","value":"~*req.6"},{"path":"ConnectTimeout","tag":"ConnectTimeout","type":"*variable","value":"~*req.7"},{"path":"ReplyTimeout","tag":"ReplyTimeout","type":"*variable","value":"~*req.8"},{"path":"TLS","tag":"TLS","type":"*variable","value":"~*req.9"},{"path":"ClientKey","tag":"ClientKey","type":"*variable","value":"~*req.10"},{"path":"ClientCertificate","tag":"ClientCertificate","type":"*variable","value":"~*req.11"},{"path":"CaCertificate","tag":"CaCertificate","type":"*variable","value":"~*req.12"}],"file_name":"DispatcherHosts.csv","flags":null,"type":"*dispatcher_hosts"}],"dry_run":false,"enabled":false,"field_separator":",","id":"*default","lockfile_path":".cgr.lck","run_delay":"0","tenant":"","tp_in_dir":"/var/spool/cgrates/loader/in","tp_out_dir":"/var/spool/cgrates/loader/out"}]}` + expected := `{"loaders":[{"caches_conns":["*internal"],"data":[{"fields":[{"mandatory":true,"path":"Tenant","tag":"TenantID","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ProfileID","type":"*variable","value":"~*req.1"},{"path":"Contexts","tag":"Contexts","type":"*variable","value":"~*req.2"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.3"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.4"},{"path":"AttributeFilterIDs","tag":"AttributeFilterIDs","type":"*variable","value":"~*req.5"},{"path":"Path","tag":"Path","type":"*variable","value":"~*req.6"},{"path":"Type","tag":"Type","type":"*variable","value":"~*req.7"},{"path":"Value","tag":"Value","type":"*variable","value":"~*req.8"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.9"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.10"}],"file_name":"Attributes.csv","flags":null,"type":"*attributes"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Type","tag":"Type","type":"*variable","value":"~*req.2"},{"path":"Element","tag":"Element","type":"*variable","value":"~*req.3"},{"path":"Values","tag":"Values","type":"*variable","value":"~*req.4"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.5"}],"file_name":"Filters.csv","flags":null,"type":"*filters"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"UsageTTL","tag":"TTL","type":"*variable","value":"~*req.4"},{"path":"Limit","tag":"Limit","type":"*variable","value":"~*req.5"},{"path":"AllocationMessage","tag":"AllocationMessage","type":"*variable","value":"~*req.6"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.7"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.8"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.9"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.10"}],"file_name":"Resources.csv","flags":null,"type":"*resources"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"TTL","tag":"TTL","type":"*variable","value":"~*req.4"},{"path":"Type","tag":"Type","type":"*variable","value":"~*req.5"},{"path":"AddressPool","tag":"AddressPool","type":"*variable","value":"~*req.6"},{"path":"Allocation","tag":"Allocation","type":"*variable","value":"~*req.7"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.8"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.9"}],"file_name":"IPs.csv","flags":null,"type":"*ips"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"QueueLength","tag":"QueueLength","type":"*variable","value":"~*req.4"},{"path":"TTL","tag":"TTL","type":"*variable","value":"~*req.5"},{"path":"MinItems","tag":"MinItems","type":"*variable","value":"~*req.6"},{"path":"MetricIDs","tag":"MetricIDs","type":"*variable","value":"~*req.7"},{"path":"MetricFilterIDs","tag":"MetricFilterIDs","type":"*variable","value":"~*req.8"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.9"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.10"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.11"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.12"}],"file_name":"Stats.csv","flags":null,"type":"*stats"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"MaxHits","tag":"MaxHits","type":"*variable","value":"~*req.4"},{"path":"MinHits","tag":"MinHits","type":"*variable","value":"~*req.5"},{"path":"MinSleep","tag":"MinSleep","type":"*variable","value":"~*req.6"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.7"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.8"},{"path":"ActionIDs","tag":"ActionIDs","type":"*variable","value":"~*req.9"},{"path":"Async","tag":"Async","type":"*variable","value":"~*req.10"}],"file_name":"Thresholds.csv","flags":null,"type":"*thresholds"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"Sorting","tag":"Sorting","type":"*variable","value":"~*req.4"},{"path":"SortingParameters","tag":"SortingParameters","type":"*variable","value":"~*req.5"},{"path":"RouteID","tag":"RouteID","type":"*variable","value":"~*req.6"},{"path":"RouteFilterIDs","tag":"RouteFilterIDs","type":"*variable","value":"~*req.7"},{"path":"RouteAccountIDs","tag":"RouteAccountIDs","type":"*variable","value":"~*req.8"},{"path":"RouteRatingPlanIDs","tag":"RouteRatingPlanIDs","type":"*variable","value":"~*req.9"},{"path":"RouteResourceIDs","tag":"RouteResourceIDs","type":"*variable","value":"~*req.10"},{"path":"RouteStatIDs","tag":"RouteStatIDs","type":"*variable","value":"~*req.11"},{"path":"RouteWeight","tag":"RouteWeight","type":"*variable","value":"~*req.12"},{"path":"RouteBlocker","tag":"RouteBlocker","type":"*variable","value":"~*req.13"},{"path":"RouteParameters","tag":"RouteParameters","type":"*variable","value":"~*req.14"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.15"}],"file_name":"Routes.csv","flags":null,"type":"*routes"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"RunID","tag":"RunID","type":"*variable","value":"~*req.4"},{"path":"AttributeIDs","tag":"AttributeIDs","type":"*variable","value":"~*req.5"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.6"}],"file_name":"Chargers.csv","flags":null,"type":"*chargers"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Contexts","tag":"Contexts","type":"*variable","value":"~*req.2"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.3"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.4"},{"path":"Strategy","tag":"Strategy","type":"*variable","value":"~*req.5"},{"path":"StrategyParameters","tag":"StrategyParameters","type":"*variable","value":"~*req.6"},{"path":"ConnID","tag":"ConnID","type":"*variable","value":"~*req.7"},{"path":"ConnFilterIDs","tag":"ConnFilterIDs","type":"*variable","value":"~*req.8"},{"path":"ConnWeight","tag":"ConnWeight","type":"*variable","value":"~*req.9"},{"path":"ConnBlocker","tag":"ConnBlocker","type":"*variable","value":"~*req.10"},{"path":"ConnParameters","tag":"ConnParameters","type":"*variable","value":"~*req.11"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.12"}],"file_name":"DispatcherProfiles.csv","flags":null,"type":"*dispatchers"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Address","tag":"Address","type":"*variable","value":"~*req.2"},{"path":"Transport","tag":"Transport","type":"*variable","value":"~*req.3"},{"path":"ConnectAttempts","tag":"ConnectAttempts","type":"*variable","value":"~*req.4"},{"path":"Reconnects","tag":"Reconnects","type":"*variable","value":"~*req.5"},{"path":"MaxReconnectInterval","tag":"MaxReconnectInterval","type":"*variable","value":"~*req.6"},{"path":"ConnectTimeout","tag":"ConnectTimeout","type":"*variable","value":"~*req.7"},{"path":"ReplyTimeout","tag":"ReplyTimeout","type":"*variable","value":"~*req.8"},{"path":"TLS","tag":"TLS","type":"*variable","value":"~*req.9"},{"path":"ClientKey","tag":"ClientKey","type":"*variable","value":"~*req.10"},{"path":"ClientCertificate","tag":"ClientCertificate","type":"*variable","value":"~*req.11"},{"path":"CaCertificate","tag":"CaCertificate","type":"*variable","value":"~*req.12"}],"file_name":"DispatcherHosts.csv","flags":null,"type":"*dispatcher_hosts"}],"dry_run":false,"enabled":false,"field_separator":",","id":"*default","lockfile_path":".cgr.lck","run_delay":"0","tenant":"","tp_in_dir":"/var/spool/cgrates/loader/in","tp_out_dir":"/var/spool/cgrates/loader/out"}]}` cgrCfg := NewDefaultCGRConfig() if err := cgrCfg.V1GetConfigAsJSON(context.Background(), &SectionWithAPIOpts{Section: LoaderJson}, &reply); err != nil { t.Error(err) @@ -5301,7 +5367,7 @@ func TestV1GetConfigAsJSONAllConfig(t *testing.T) { }` var reply string cgrCfg, err := NewCGRConfigFromJSONStringWithDefaults(cfgJSON) - expected := `{"analyzers":{"cleanup_interval":"1h0m0s","db_path":"/var/spool/cgrates/analyzers","enabled":false,"index_type":"*scorch","ttl":"24h0m0s"},"apiban":{"keys":[]},"apiers":{"attributes_conns":[],"caches_conns":["*internal"],"ees_conns":[],"enabled":false,"scheduler_conns":[]},"asterisk_agent":{"asterisk_conns":[{"address":"127.0.0.1:8088","alias":"","connect_attempts":3,"max_reconnect_interval":"0s","password":"CGRateS.org","reconnects":5,"user":"cgrates"}],"create_cdr":false,"enabled":false,"sessions_conns":["*birpc_internal"]},"attributes":{"any_context":true,"apiers_conns":[],"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"opts":{"*processRuns":1,"*profileIDs":[],"*profileIgnoreFilters":false,"*profileRuns":0},"prefix_indexed_fields":[],"resources_conns":[],"stats_conns":[],"suffix_indexed_fields":[]},"caches":{"partitions":{"*account_action_plans":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*action_plans":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*action_triggers":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*actions":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*apiban":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"2m0s"},"*attribute_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*attribute_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*caps_events":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*cdr_ids":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"10m0s"},"*charger_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*charger_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*closed_sessions":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"10s"},"*destinations":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*diameter_messages":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*dispatcher_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_hosts":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_loads":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_routes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatchers":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*event_charges":{"limit":0,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"10s"},"*event_resources":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*filters":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*load_ids":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*radius_packets":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*ranking_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rankings":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rating_plans":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rating_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*replication_hosts":{"limit":0,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*resource_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*resource_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*resources":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*reverse_destinations":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*reverse_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*route_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*route_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rpc_connections":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rpc_responses":{"limit":0,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"2s"},"*sentrypeer":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":true,"ttl":"24h0m0s"},"*shared_groups":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*stat_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*statqueue_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*statqueues":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*stir":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*threshold_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*threshold_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*thresholds":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*timings":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*trend_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*trends":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*uch":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"}},"remote_conns":[],"replication_conns":[]},"cdrs":{"attributes_conns":[],"chargers_conns":[],"compress_stored_cost":false,"ees_conns":[],"enabled":false,"extra_fields":[],"online_cdr_exports":[],"rals_conns":[],"scheduler_conns":[],"session_cost_retries":5,"stats_conns":[],"store_cdrs":true,"thresholds_conns":[]},"chargers":{"attributes_conns":[],"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"prefix_indexed_fields":[],"suffix_indexed_fields":[]},"configs":{"enabled":false,"root_dir":"/var/spool/cgrates/configs","url":"/configs/"},"cores":{"caps":0,"caps_stats_interval":"0","caps_strategy":"*busy","shutdown_timeout":"1s"},"data_db":{"db_host":"127.0.0.1","db_name":"10","db_password":"","db_port":6379,"db_type":"*redis","db_user":"cgrates","items":{"*account_action_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*accounts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*action_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*action_triggers":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*actions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*attribute_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*attribute_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*charger_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*charger_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*destinations":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_hosts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*filters":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*load_ids":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*ranking_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rankings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rating_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rating_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*resource_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*resource_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*resources":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*reverse_destinations":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*reverse_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*route_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*route_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*sessions_backup":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*shared_groups":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*stat_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*statqueue_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*statqueues":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*threshold_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*threshold_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*thresholds":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*timings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*trend_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*trends":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*versions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false}},"opts":{"internalDBBackupPath":"/var/lib/cgrates/internal_db/backup/datadb","internalDBDumpInterval":"0s","internalDBDumpPath":"/var/lib/cgrates/internal_db/datadb","internalDBFileSizeLimit":1073741824,"internalDBRewriteInterval":"0s","internalDBStartTimeout":"5m0s","mongoConnScheme":"mongodb","mongoQueryTimeout":"10s","redisCACertificate":"","redisClientCertificate":"","redisClientKey":"","redisCluster":false,"redisClusterOndownDelay":"0s","redisClusterSync":"5s","redisConnectAttempts":20,"redisConnectTimeout":"0s","redisMaxConns":10,"redisPoolPipelineLimit":0,"redisPoolPipelineWindow":"150µs","redisReadTimeout":"0s","redisSentinel":"","redisTLS":false,"redisWriteTimeout":"0s"},"remote_conn_id":"","remote_conns":[],"replication_cache":"","replication_conns":[],"replication_failed_dir":"","replication_filtered":false,"replication_interval":"0s"},"diameter_agent":{"asr_template":"","dictionaries_path":"/usr/share/cgrates/diameter/dict/","enabled":false,"forced_disconnect":"*none","listen":"127.0.0.1:3868","listen_net":"tcp","origin_host":"CGR-DA","origin_realm":"cgrates.org","product_name":"CGRateS","rar_template":"","request_processors":[],"sessions_conns":["*birpc_internal"],"synced_conn_requests":false,"vendor_id":0},"dispatchers":{"any_subsystem":true,"attributes_conns":[],"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"prefix_indexed_fields":[],"prevent_loop":false,"suffix_indexed_fields":[]},"dns_agent":{"enabled":false,"listeners":[{"address":"127.0.0.1:53","network":"udp"}],"request_processors":[],"sessions_conns":["*internal"],"timezone":""},"ees":{"attributes_conns":[],"cache":{"*amqp_json_map":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*amqpv1_json_map":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*els":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*file_csv":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*kafka_json_map":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*nats_json_map":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*s3_json_map":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*sql":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*sqs_json_map":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false}},"enabled":false,"exporters":[{"attempts":1,"attribute_context":"","attribute_ids":[],"concurrent_requests":0,"export_path":"/var/spool/cgrates/ees","failed_posts_dir":"/var/spool/cgrates/failed_posts","fields":[],"filters":[],"flags":[],"id":"*default","metrics_reset_schedule":"","opts":{},"synchronous":false,"timezone":"","type":"*none"}]},"ers":{"concurrent_events":1,"ees_conns":[],"enabled":false,"partial_cache_ttl":"1s","readers":[{"cache_dump_fields":[],"concurrent_requests":1024,"fields":[{"mandatory":true,"path":"*cgreq.ToR","tag":"ToR","type":"*variable","value":"~*req.2"},{"mandatory":true,"path":"*cgreq.OriginID","tag":"OriginID","type":"*variable","value":"~*req.3"},{"mandatory":true,"path":"*cgreq.RequestType","tag":"RequestType","type":"*variable","value":"~*req.4"},{"mandatory":true,"path":"*cgreq.Tenant","tag":"Tenant","type":"*variable","value":"~*req.6"},{"mandatory":true,"path":"*cgreq.Category","tag":"Category","type":"*variable","value":"~*req.7"},{"mandatory":true,"path":"*cgreq.Account","tag":"Account","type":"*variable","value":"~*req.8"},{"mandatory":true,"path":"*cgreq.Subject","tag":"Subject","type":"*variable","value":"~*req.9"},{"mandatory":true,"path":"*cgreq.Destination","tag":"Destination","type":"*variable","value":"~*req.10"},{"mandatory":true,"path":"*cgreq.SetupTime","tag":"SetupTime","type":"*variable","value":"~*req.11"},{"mandatory":true,"path":"*cgreq.AnswerTime","tag":"AnswerTime","type":"*variable","value":"~*req.12"},{"mandatory":true,"path":"*cgreq.Usage","tag":"Usage","type":"*variable","value":"~*req.13"}],"filters":[],"flags":[],"id":"*default","max_reconnect_interval":"5m0s","opts":{"csvFieldSeparator":",","csvHeaderDefineChar":":","csvRowLength":0,"natsSubject":"cgrates_cdrs","partialCacheAction":"*none","partialOrderField":"~*req.AnswerTime"},"partial_commit_fields":[],"processed_path":"/var/spool/cgrates/ers/out","reconnects":-1,"run_delay":"0","source_path":"/var/spool/cgrates/ers/in","start_delay":"0","tenant":"","timezone":"","type":"*none"}],"sessions_conns":["*internal"]},"filters":{"apiers_conns":[],"rankings_conns":[],"resources_conns":[],"stats_conns":[],"trends_conns":[]},"freeswitch_agent":{"active_session_delimiter":",","create_cdr":false,"empty_balance_ann_file":"","empty_balance_context":"","enabled":false,"event_socket_conns":[{"address":"127.0.0.1:8021","alias":"127.0.0.1:8021","max_reconnect_interval":"0s","password":"ClueCon","reconnects":5,"reply_timeout":"1m0s"}],"extra_fields":"","low_balance_ann_file":"","max_wait_connection":"2s","sessions_conns":["*birpc_internal"],"subscribe_park":true},"general":{"caching_delay":"0","connect_attempts":5,"connect_timeout":"1s","dbdata_encoding":"*msgpack","default_caching":"*reload","default_category":"call","default_request_type":"*rated","default_tenant":"cgrates.org","default_timezone":"Local","digest_equal":":","digest_separator":",","failed_posts_dir":"/var/spool/cgrates/failed_posts","failed_posts_ttl":"5s","locking_timeout":"0","log_level":6,"logger":"*syslog","max_parallel_conns":100,"max_reconnect_interval":"0","node_id":"ENGINE1","poster_attempts":3,"reconnects":-1,"reply_timeout":"2s","rounding_decimals":5,"rsr_separator":";","tpexport_dir":"/var/spool/cgrates/tpe"},"http":{"auth_users":{},"client_opts":{"dialFallbackDelay":"300ms","dialKeepAlive":"30s","dialTimeout":"30s","disableCompression":false,"disableKeepAlives":false,"expectContinueTimeout":"0s","forceAttemptHttp2":true,"idleConnTimeout":"1m30s","maxConnsPerHost":0,"maxIdleConns":100,"maxIdleConnsPerHost":2,"responseHeaderTimeout":"0s","skipTlsVerify":false,"tlsHandshakeTimeout":"10s"},"freeswitch_cdrs_url":"/freeswitch_json","http_cdrs":"/cdr_http","json_rpc_url":"/jsonrpc","pprof_path":"/debug/pprof/","registrars_url":"/registrar","use_basic_auth":false,"ws_url":"/ws"},"http_agent":[],"kamailio_agent":{"create_cdr":false,"enabled":false,"evapi_conns":[{"address":"127.0.0.1:8448","alias":"","max_reconnect_interval":"0s","reconnects":5}],"sessions_conns":["*birpc_internal"],"timezone":""},"listen":{"http":"127.0.0.1:2080","http_tls":"127.0.0.1:2280","rpc_gob":"127.0.0.1:2013","rpc_gob_tls":"127.0.0.1:2023","rpc_json":"127.0.0.1:2012","rpc_json_tls":"127.0.0.1:2022"},"loader":{"caches_conns":["*localhost"],"data_path":"./","disable_reverse":false,"field_separator":",","gapi_credentials":".gapi/credentials.json","gapi_token":".gapi/token.json","scheduler_conns":["*localhost"],"tpid":""},"loaders":[{"caches_conns":["*internal"],"data":[{"fields":[{"mandatory":true,"path":"Tenant","tag":"TenantID","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ProfileID","type":"*variable","value":"~*req.1"},{"path":"Contexts","tag":"Contexts","type":"*variable","value":"~*req.2"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.3"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.4"},{"path":"AttributeFilterIDs","tag":"AttributeFilterIDs","type":"*variable","value":"~*req.5"},{"path":"Path","tag":"Path","type":"*variable","value":"~*req.6"},{"path":"Type","tag":"Type","type":"*variable","value":"~*req.7"},{"path":"Value","tag":"Value","type":"*variable","value":"~*req.8"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.9"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.10"}],"file_name":"Attributes.csv","flags":null,"type":"*attributes"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Type","tag":"Type","type":"*variable","value":"~*req.2"},{"path":"Element","tag":"Element","type":"*variable","value":"~*req.3"},{"path":"Values","tag":"Values","type":"*variable","value":"~*req.4"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.5"}],"file_name":"Filters.csv","flags":null,"type":"*filters"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"UsageTTL","tag":"TTL","type":"*variable","value":"~*req.4"},{"path":"Limit","tag":"Limit","type":"*variable","value":"~*req.5"},{"path":"AllocationMessage","tag":"AllocationMessage","type":"*variable","value":"~*req.6"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.7"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.8"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.9"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.10"}],"file_name":"Resources.csv","flags":null,"type":"*resources"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"QueueLength","tag":"QueueLength","type":"*variable","value":"~*req.4"},{"path":"TTL","tag":"TTL","type":"*variable","value":"~*req.5"},{"path":"MinItems","tag":"MinItems","type":"*variable","value":"~*req.6"},{"path":"MetricIDs","tag":"MetricIDs","type":"*variable","value":"~*req.7"},{"path":"MetricFilterIDs","tag":"MetricFilterIDs","type":"*variable","value":"~*req.8"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.9"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.10"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.11"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.12"}],"file_name":"Stats.csv","flags":null,"type":"*stats"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"MaxHits","tag":"MaxHits","type":"*variable","value":"~*req.4"},{"path":"MinHits","tag":"MinHits","type":"*variable","value":"~*req.5"},{"path":"MinSleep","tag":"MinSleep","type":"*variable","value":"~*req.6"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.7"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.8"},{"path":"ActionIDs","tag":"ActionIDs","type":"*variable","value":"~*req.9"},{"path":"Async","tag":"Async","type":"*variable","value":"~*req.10"}],"file_name":"Thresholds.csv","flags":null,"type":"*thresholds"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"Sorting","tag":"Sorting","type":"*variable","value":"~*req.4"},{"path":"SortingParameters","tag":"SortingParameters","type":"*variable","value":"~*req.5"},{"path":"RouteID","tag":"RouteID","type":"*variable","value":"~*req.6"},{"path":"RouteFilterIDs","tag":"RouteFilterIDs","type":"*variable","value":"~*req.7"},{"path":"RouteAccountIDs","tag":"RouteAccountIDs","type":"*variable","value":"~*req.8"},{"path":"RouteRatingPlanIDs","tag":"RouteRatingPlanIDs","type":"*variable","value":"~*req.9"},{"path":"RouteResourceIDs","tag":"RouteResourceIDs","type":"*variable","value":"~*req.10"},{"path":"RouteStatIDs","tag":"RouteStatIDs","type":"*variable","value":"~*req.11"},{"path":"RouteWeight","tag":"RouteWeight","type":"*variable","value":"~*req.12"},{"path":"RouteBlocker","tag":"RouteBlocker","type":"*variable","value":"~*req.13"},{"path":"RouteParameters","tag":"RouteParameters","type":"*variable","value":"~*req.14"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.15"}],"file_name":"Routes.csv","flags":null,"type":"*routes"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"RunID","tag":"RunID","type":"*variable","value":"~*req.4"},{"path":"AttributeIDs","tag":"AttributeIDs","type":"*variable","value":"~*req.5"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.6"}],"file_name":"Chargers.csv","flags":null,"type":"*chargers"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Contexts","tag":"Contexts","type":"*variable","value":"~*req.2"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.3"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.4"},{"path":"Strategy","tag":"Strategy","type":"*variable","value":"~*req.5"},{"path":"StrategyParameters","tag":"StrategyParameters","type":"*variable","value":"~*req.6"},{"path":"ConnID","tag":"ConnID","type":"*variable","value":"~*req.7"},{"path":"ConnFilterIDs","tag":"ConnFilterIDs","type":"*variable","value":"~*req.8"},{"path":"ConnWeight","tag":"ConnWeight","type":"*variable","value":"~*req.9"},{"path":"ConnBlocker","tag":"ConnBlocker","type":"*variable","value":"~*req.10"},{"path":"ConnParameters","tag":"ConnParameters","type":"*variable","value":"~*req.11"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.12"}],"file_name":"DispatcherProfiles.csv","flags":null,"type":"*dispatchers"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Address","tag":"Address","type":"*variable","value":"~*req.2"},{"path":"Transport","tag":"Transport","type":"*variable","value":"~*req.3"},{"path":"ConnectAttempts","tag":"ConnectAttempts","type":"*variable","value":"~*req.4"},{"path":"Reconnects","tag":"Reconnects","type":"*variable","value":"~*req.5"},{"path":"MaxReconnectInterval","tag":"MaxReconnectInterval","type":"*variable","value":"~*req.6"},{"path":"ConnectTimeout","tag":"ConnectTimeout","type":"*variable","value":"~*req.7"},{"path":"ReplyTimeout","tag":"ReplyTimeout","type":"*variable","value":"~*req.8"},{"path":"TLS","tag":"TLS","type":"*variable","value":"~*req.9"},{"path":"ClientKey","tag":"ClientKey","type":"*variable","value":"~*req.10"},{"path":"ClientCertificate","tag":"ClientCertificate","type":"*variable","value":"~*req.11"},{"path":"CaCertificate","tag":"CaCertificate","type":"*variable","value":"~*req.12"}],"file_name":"DispatcherHosts.csv","flags":null,"type":"*dispatcher_hosts"}],"dry_run":false,"enabled":false,"field_separator":",","id":"*default","lockfile_path":".cgr.lck","run_delay":"0","tenant":"","tp_in_dir":"/var/spool/cgrates/loader/in","tp_out_dir":"/var/spool/cgrates/loader/out"}],"mailer":{"auth_password":"CGRateS.org","auth_user":"cgrates","from_address":"cgr-mailer@localhost.localdomain","server":"localhost"},"migrator":{"out_datadb_encoding":"msgpack","out_datadb_host":"127.0.0.1","out_datadb_name":"10","out_datadb_opts":{"mongoConnScheme":"mongodb","mongoQueryTimeout":"0s","redisCACertificate":"","redisClientCertificate":"","redisClientKey":"","redisCluster":false,"redisClusterOndownDelay":"0s","redisClusterSync":"5s","redisConnectAttempts":20,"redisConnectTimeout":"0s","redisMaxConns":10,"redisPoolPipelineLimit":0,"redisPoolPipelineWindow":"150µs","redisReadTimeout":"0s","redisSentinel":"","redisTLS":false,"redisWriteTimeout":"0s"},"out_datadb_password":"","out_datadb_port":"6379","out_datadb_type":"*redis","out_datadb_user":"cgrates","out_stordb_host":"127.0.0.1","out_stordb_name":"cgrates","out_stordb_opts":{"mongoConnScheme":"mongodb","mongoQueryTimeout":"0s","mysqlDSNParams":null,"mysqlLocation":"","pgSSLMode":"","sqlConnMaxLifetime":"0s","sqlMaxIdleConns":0,"sqlMaxOpenConns":0},"out_stordb_password":"","out_stordb_port":"3306","out_stordb_type":"*mysql","out_stordb_user":"cgrates","users_filters":null},"prometheus_agent":{"collect_go_metrics":false,"collect_process_metrics":false,"cores_conns":null,"enabled":false,"path":"/prometheus","stat_queue_ids":null,"stats_conns":null},"radius_agent":{"client_dictionaries":{"*default":["/usr/share/cgrates/radius/dict/"]},"client_secrets":{"*default":"CGRateS.org"},"coa_template":"*coa","dmr_template":"*dmr","enabled":false,"listeners":[{"acct_address":"127.0.0.1:1813","auth_address":"127.0.0.1:1812","network":"udp"}],"request_processors":[],"requests_cache_key":"","sessions_conns":["*internal"]},"rals":{"balance_rating_subject":{"*any":"*zero1ns","*voice":"*zero1s"},"enabled":false,"fallback_depth":3,"max_computed_usage":{"*any":"189h0m0s","*data":"107374182400","*mms":"10000","*sms":"10000","*voice":"72h0m0s"},"max_increments":1000000,"remove_expired":true,"rp_subject_prefix_matching":false,"sessions_conns":[],"stats_conns":[],"thresholds_conns":[]},"rankings":{"ees_conns":[],"ees_exporter_ids":[],"enabled":false,"scheduled_ids":{},"stats_conns":[],"store_interval":"","thresholds_conns":[]},"registrarc":{"dispatchers":{"hosts":[],"refresh_interval":"5m0s","registrars_conns":[]},"rpc":{"hosts":[],"refresh_interval":"5m0s","registrars_conns":[]}},"resources":{"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"opts":{"*units":1,"*usageID":""},"prefix_indexed_fields":[],"store_interval":"","suffix_indexed_fields":[],"thresholds_conns":[]},"routes":{"attributes_conns":[],"default_ratio":1,"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"opts":{"*context":"*routes","*ignoreErrors":false,"*maxCost":""},"prefix_indexed_fields":[],"rals_conns":[],"resources_conns":[],"stats_conns":[],"suffix_indexed_fields":[]},"rpc_conns":{"*bijson_localhost":{"conns":[{"address":"127.0.0.1:2014","transport":"*birpc_json"}],"poolSize":0,"strategy":"*first"},"*birpc_internal":{"conns":[{"address":"*birpc_internal","transport":""}],"poolSize":0,"strategy":"*first"},"*internal":{"conns":[{"address":"*internal","transport":""}],"poolSize":0,"strategy":"*first"},"*localhost":{"conns":[{"address":"127.0.0.1:2012","transport":"*json"}],"poolSize":0,"strategy":"*first"}},"schedulers":{"cdrs_conns":[],"dynaprepaid_actionplans":[],"enabled":false,"filters":[],"stats_conns":[],"thresholds_conns":[]},"sentrypeer":{"Audience":"https://sentrypeer.com/api","ClientID":"","ClientSecret":"","GrantType":"client_credentials","IpUrl":"https://sentrypeer.com/api/ip-addresses","NumberUrl":"https://sentrypeer.com/api/phone-numbers","TokenURL":"https://authz.sentrypeer.com/oauth/token"},"sessions":{"alterable_fields":[],"attributes_conns":[],"backup_interval":"0","cdrs_conns":[],"channel_sync_interval":"0","chargers_conns":[],"client_protocol":2,"debit_interval":"0","default_usage":{"*any":"3h0m0s","*data":"1048576","*sms":"1","*voice":"3h0m0s"},"enabled":false,"listen_bigob":"","listen_bijson":"127.0.0.1:2014","min_dur_low_balance":"0","rals_conns":[],"replication_conns":[],"resources_conns":[],"routes_conns":[],"scheduler_conns":[],"session_indexes":[],"session_ttl":"0","stale_chan_max_extra_usage":"0","stats_conns":[],"stir":{"allowed_attest":["*any"],"default_attest":"A","payload_maxduration":"-1","privatekey_path":"","publickey_path":""},"store_session_costs":false,"terminate_attempts":5,"thresholds_conns":[]},"sip_agent":{"enabled":false,"listen":"127.0.0.1:5060","listen_net":"udp","request_processors":[],"retransmission_timer":1000000000,"sessions_conns":["*internal"],"timezone":""},"stats":{"ees_conns":[],"ees_exporter_ids":[],"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"opts":{"*profileIDs":[],"*profileIgnoreFilters":false},"prefix_indexed_fields":[],"store_interval":"","store_uncompressed_limit":0,"suffix_indexed_fields":[],"thresholds_conns":[]},"stor_db":{"db_host":"127.0.0.1","db_name":"cgrates","db_password":"CGRateS.org","db_port":3306,"db_type":"*mysql","db_user":"cgrates","items":{"*cdrs":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*session_costs":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_account_actions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_action_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_action_triggers":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_actions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_attributes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_chargers":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_destination_rates":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_destinations":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_dispatcher_hosts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_dispatcher_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_filters":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rankings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rates":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rating_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rating_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_resources":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_routes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_shared_groups":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_stats":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_thresholds":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_timings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_trends":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*versions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false}},"opts":{"internalDBBackupPath":"/var/lib/cgrates/internal_db/backup/stordb","internalDBDumpInterval":"0s","internalDBDumpPath":"/var/lib/cgrates/internal_db/stordb","internalDBFileSizeLimit":1073741824,"internalDBRewriteInterval":"0s","internalDBStartTimeout":"5m0s","mongoConnScheme":"mongodb","mongoQueryTimeout":"10s","mysqlDSNParams":{},"mysqlLocation":"Local","pgSSLMode":"disable","pgSchema":"","sqlConnMaxLifetime":"0s","sqlLogLevel":3,"sqlMaxIdleConns":10,"sqlMaxOpenConns":100},"prefix_indexed_fields":[],"remote_conns":null,"replication_conns":null,"string_indexed_fields":[]},"suretax":{"bill_to_number":"","business_unit":"","client_number":"","client_tracking":"~*req.CGRID","customer_number":"~*req.Subject","include_local_cost":false,"orig_number":"~*req.Subject","p2pplus4":"","p2pzipcode":"","plus4":"","regulatory_code":"03","response_group":"03","response_type":"D4","return_file_code":"0","sales_type_code":"R","tax_exemption_code_list":"","tax_included":"0","tax_situs_rule":"04","term_number":"~*req.Destination","timezone":"UTC","trans_type_code":"010101","unit_type":"00","units":"1","url":"","validation_key":"","zipcode":""},"templates":{"*asr":[{"mandatory":true,"path":"*diamreq.Session-Id","tag":"SessionId","type":"*variable","value":"~*req.Session-Id"},{"mandatory":true,"path":"*diamreq.Origin-Host","tag":"OriginHost","type":"*variable","value":"~*req.Destination-Host"},{"mandatory":true,"path":"*diamreq.Origin-Realm","tag":"OriginRealm","type":"*variable","value":"~*req.Destination-Realm"},{"mandatory":true,"path":"*diamreq.Destination-Realm","tag":"DestinationRealm","type":"*variable","value":"~*req.Origin-Realm"},{"mandatory":true,"path":"*diamreq.Destination-Host","tag":"DestinationHost","type":"*variable","value":"~*req.Origin-Host"},{"mandatory":true,"path":"*diamreq.Auth-Application-Id","tag":"AuthApplicationId","type":"*variable","value":"~*vars.*appid"}],"*cca":[{"mandatory":true,"path":"*rep.Session-Id","tag":"SessionId","type":"*variable","value":"~*req.Session-Id"},{"path":"*rep.Result-Code","tag":"ResultCode","type":"*constant","value":"2001"},{"mandatory":true,"path":"*rep.Origin-Host","tag":"OriginHost","type":"*variable","value":"~*vars.OriginHost"},{"mandatory":true,"path":"*rep.Origin-Realm","tag":"OriginRealm","type":"*variable","value":"~*vars.OriginRealm"},{"mandatory":true,"path":"*rep.Auth-Application-Id","tag":"AuthApplicationId","type":"*variable","value":"~*vars.*appid"},{"mandatory":true,"path":"*rep.CC-Request-Type","tag":"CCRequestType","type":"*variable","value":"~*req.CC-Request-Type"},{"mandatory":true,"path":"*rep.CC-Request-Number","tag":"CCRequestNumber","type":"*variable","value":"~*req.CC-Request-Number"}],"*cdrLog":[{"mandatory":true,"path":"*cdr.ToR","tag":"ToR","type":"*variable","value":"~*req.BalanceType"},{"mandatory":true,"path":"*cdr.OriginHost","tag":"OriginHost","type":"*constant","value":"127.0.0.1"},{"mandatory":true,"path":"*cdr.RequestType","tag":"RequestType","type":"*constant","value":"*none"},{"mandatory":true,"path":"*cdr.Tenant","tag":"Tenant","type":"*variable","value":"~*req.Tenant"},{"mandatory":true,"path":"*cdr.Account","tag":"Account","type":"*variable","value":"~*req.Account"},{"mandatory":true,"path":"*cdr.Subject","tag":"Subject","type":"*variable","value":"~*req.Account"},{"mandatory":true,"path":"*cdr.Cost","tag":"Cost","type":"*variable","value":"~*req.Cost"},{"mandatory":true,"path":"*cdr.Source","tag":"Source","type":"*constant","value":"*cdrLog"},{"mandatory":true,"path":"*cdr.Usage","tag":"Usage","type":"*constant","value":"1"},{"mandatory":true,"path":"*cdr.RunID","tag":"RunID","type":"*variable","value":"~*req.ActionType"},{"mandatory":true,"path":"*cdr.SetupTime","tag":"SetupTime","type":"*constant","value":"*now"},{"mandatory":true,"path":"*cdr.AnswerTime","tag":"AnswerTime","type":"*constant","value":"*now"},{"mandatory":true,"path":"*cdr.PreRated","tag":"PreRated","type":"*constant","value":"true"}],"*coa":[{"path":"*radDAReq.User-Name","tag":"User-Name","type":"*variable","value":"~*oreq.User-Name"},{"path":"*radDAReq.NAS-IP-Address","tag":"NAS-IP-Address","type":"*variable","value":"~*oreq.NAS-IP-Address"},{"path":"*radDAReq.Acct-Session-Id","tag":"Acct-Session-Id","type":"*variable","value":"~*oreq.Acct-Session-Id"},{"path":"*radDAReq.Filter-Id","tag":"Filter-Id","type":"*variable","value":"~*req.CustomFilter"}],"*dmr":[{"path":"*radDAReq.User-Name","tag":"User-Name","type":"*variable","value":"~*oreq.User-Name"},{"path":"*radDAReq.NAS-IP-Address","tag":"NAS-IP-Address","type":"*variable","value":"~*oreq.NAS-IP-Address"},{"path":"*radDAReq.Acct-Session-Id","tag":"Acct-Session-Id","type":"*variable","value":"~*oreq.Acct-Session-Id"},{"path":"*radDAReq.Reply-Message","tag":"Reply-Message","type":"*variable","value":"~*req.DisconnectCause"}],"*err":[{"mandatory":true,"path":"*rep.Session-Id","tag":"SessionId","type":"*variable","value":"~*req.Session-Id"},{"mandatory":true,"path":"*rep.Origin-Host","tag":"OriginHost","type":"*variable","value":"~*vars.OriginHost"},{"mandatory":true,"path":"*rep.Origin-Realm","tag":"OriginRealm","type":"*variable","value":"~*vars.OriginRealm"}],"*errSip":[{"mandatory":true,"path":"*rep.Request","tag":"Request","type":"*constant","value":"SIP/2.0 500 Internal Server Error"}],"*rar":[{"mandatory":true,"path":"*diamreq.Session-Id","tag":"SessionId","type":"*variable","value":"~*req.Session-Id"},{"mandatory":true,"path":"*diamreq.Origin-Host","tag":"OriginHost","type":"*variable","value":"~*req.Destination-Host"},{"mandatory":true,"path":"*diamreq.Origin-Realm","tag":"OriginRealm","type":"*variable","value":"~*req.Destination-Realm"},{"mandatory":true,"path":"*diamreq.Destination-Realm","tag":"DestinationRealm","type":"*variable","value":"~*req.Origin-Realm"},{"mandatory":true,"path":"*diamreq.Destination-Host","tag":"DestinationHost","type":"*variable","value":"~*req.Origin-Host"},{"mandatory":true,"path":"*diamreq.Auth-Application-Id","tag":"AuthApplicationId","type":"*variable","value":"~*vars.*appid"},{"path":"*diamreq.Re-Auth-Request-Type","tag":"ReAuthRequestType","type":"*constant","value":"0"}]},"thresholds":{"ees_conns":[],"ees_exporter_ids":[],"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"opts":{"*profileIDs":[],"*profileIgnoreFilters":false},"prefix_indexed_fields":[],"store_interval":"","suffix_indexed_fields":[]},"tls":{"ca_certificate":"","client_certificate":"","client_key":"","server_certificate":"","server_key":"","server_name":"","server_policy":4},"trends":{"ees_conns":[],"ees_exporter_ids":[],"enabled":false,"scheduled_ids":{},"stats_conns":[],"store_interval":"","store_uncompressed_limit":0,"thresholds_conns":[]}}` + expected := `{"analyzers":{"cleanup_interval":"1h0m0s","db_path":"/var/spool/cgrates/analyzers","enabled":false,"index_type":"*scorch","ttl":"24h0m0s"},"apiban":{"keys":[]},"apiers":{"attributes_conns":[],"caches_conns":["*internal"],"ees_conns":[],"enabled":false,"scheduler_conns":[]},"asterisk_agent":{"asterisk_conns":[{"address":"127.0.0.1:8088","alias":"","connect_attempts":3,"max_reconnect_interval":"0s","password":"CGRateS.org","reconnects":5,"user":"cgrates"}],"create_cdr":false,"enabled":false,"sessions_conns":["*birpc_internal"]},"attributes":{"any_context":true,"apiers_conns":[],"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"opts":{"*processRuns":1,"*profileIDs":[],"*profileIgnoreFilters":false,"*profileRuns":0},"prefix_indexed_fields":[],"resources_conns":[],"stats_conns":[],"suffix_indexed_fields":[]},"caches":{"partitions":{"*account_action_plans":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*action_plans":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*action_triggers":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*actions":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*apiban":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"2m0s"},"*attribute_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*attribute_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*caps_events":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*cdr_ids":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"10m0s"},"*charger_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*charger_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*closed_sessions":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"10s"},"*destinations":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*diameter_messages":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*dispatcher_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_hosts":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_loads":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_routes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatchers":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*event_charges":{"limit":0,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"10s"},"*event_ips":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*event_resources":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*filters":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*ip_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*ip_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*ips":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*load_ids":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*radius_packets":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*ranking_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rankings":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rating_plans":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rating_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*replication_hosts":{"limit":0,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*resource_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*resource_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*resources":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*reverse_destinations":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*reverse_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*route_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*route_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rpc_connections":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rpc_responses":{"limit":0,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"2s"},"*sentrypeer":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":true,"ttl":"24h0m0s"},"*shared_groups":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*stat_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*statqueue_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*statqueues":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*stir":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*threshold_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*threshold_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*thresholds":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*timings":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*trend_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*trends":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*uch":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"}},"remote_conns":[],"replication_conns":[]},"cdrs":{"attributes_conns":[],"chargers_conns":[],"compress_stored_cost":false,"ees_conns":[],"enabled":false,"extra_fields":[],"online_cdr_exports":[],"rals_conns":[],"scheduler_conns":[],"session_cost_retries":5,"stats_conns":[],"store_cdrs":true,"thresholds_conns":[]},"chargers":{"attributes_conns":[],"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"prefix_indexed_fields":[],"suffix_indexed_fields":[]},"configs":{"enabled":false,"root_dir":"/var/spool/cgrates/configs","url":"/configs/"},"cores":{"caps":0,"caps_stats_interval":"0","caps_strategy":"*busy","shutdown_timeout":"1s"},"data_db":{"db_host":"127.0.0.1","db_name":"10","db_password":"","db_port":6379,"db_type":"*redis","db_user":"cgrates","items":{"*account_action_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*accounts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*action_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*action_triggers":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*actions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*attribute_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*attribute_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*charger_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*charger_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*destinations":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_hosts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*filters":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*ip_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*ip_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*ips":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*load_ids":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*ranking_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rankings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rating_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rating_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*resource_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*resource_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*resources":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*reverse_destinations":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*reverse_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*route_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*route_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*sessions_backup":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*shared_groups":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*stat_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*statqueue_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*statqueues":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*threshold_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*threshold_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*thresholds":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*timings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*trend_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*trends":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*versions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false}},"opts":{"internalDBBackupPath":"/var/lib/cgrates/internal_db/backup/datadb","internalDBDumpInterval":"0s","internalDBDumpPath":"/var/lib/cgrates/internal_db/datadb","internalDBFileSizeLimit":1073741824,"internalDBRewriteInterval":"0s","internalDBStartTimeout":"5m0s","mongoConnScheme":"mongodb","mongoQueryTimeout":"10s","redisCACertificate":"","redisClientCertificate":"","redisClientKey":"","redisCluster":false,"redisClusterOndownDelay":"0s","redisClusterSync":"5s","redisConnectAttempts":20,"redisConnectTimeout":"0s","redisMaxConns":10,"redisPoolPipelineLimit":0,"redisPoolPipelineWindow":"150µs","redisReadTimeout":"0s","redisSentinel":"","redisTLS":false,"redisWriteTimeout":"0s"},"remote_conn_id":"","remote_conns":[],"replication_cache":"","replication_conns":[],"replication_failed_dir":"","replication_filtered":false,"replication_interval":"0s"},"diameter_agent":{"asr_template":"","dictionaries_path":"/usr/share/cgrates/diameter/dict/","enabled":false,"forced_disconnect":"*none","listen":"127.0.0.1:3868","listen_net":"tcp","origin_host":"CGR-DA","origin_realm":"cgrates.org","product_name":"CGRateS","rar_template":"","request_processors":[],"sessions_conns":["*birpc_internal"],"synced_conn_requests":false,"vendor_id":0},"dispatchers":{"any_subsystem":true,"attributes_conns":[],"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"prefix_indexed_fields":[],"prevent_loop":false,"suffix_indexed_fields":[]},"dns_agent":{"enabled":false,"listeners":[{"address":"127.0.0.1:53","network":"udp"}],"request_processors":[],"sessions_conns":["*internal"],"timezone":""},"ees":{"attributes_conns":[],"cache":{"*amqp_json_map":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*amqpv1_json_map":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*els":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*file_csv":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*kafka_json_map":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*nats_json_map":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*s3_json_map":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*sql":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*sqs_json_map":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false}},"enabled":false,"exporters":[{"attempts":1,"attribute_context":"","attribute_ids":[],"concurrent_requests":0,"export_path":"/var/spool/cgrates/ees","failed_posts_dir":"/var/spool/cgrates/failed_posts","fields":[],"filters":[],"flags":[],"id":"*default","metrics_reset_schedule":"","opts":{},"synchronous":false,"timezone":"","type":"*none"}]},"ers":{"concurrent_events":1,"ees_conns":[],"enabled":false,"partial_cache_ttl":"1s","readers":[{"cache_dump_fields":[],"concurrent_requests":1024,"fields":[{"mandatory":true,"path":"*cgreq.ToR","tag":"ToR","type":"*variable","value":"~*req.2"},{"mandatory":true,"path":"*cgreq.OriginID","tag":"OriginID","type":"*variable","value":"~*req.3"},{"mandatory":true,"path":"*cgreq.RequestType","tag":"RequestType","type":"*variable","value":"~*req.4"},{"mandatory":true,"path":"*cgreq.Tenant","tag":"Tenant","type":"*variable","value":"~*req.6"},{"mandatory":true,"path":"*cgreq.Category","tag":"Category","type":"*variable","value":"~*req.7"},{"mandatory":true,"path":"*cgreq.Account","tag":"Account","type":"*variable","value":"~*req.8"},{"mandatory":true,"path":"*cgreq.Subject","tag":"Subject","type":"*variable","value":"~*req.9"},{"mandatory":true,"path":"*cgreq.Destination","tag":"Destination","type":"*variable","value":"~*req.10"},{"mandatory":true,"path":"*cgreq.SetupTime","tag":"SetupTime","type":"*variable","value":"~*req.11"},{"mandatory":true,"path":"*cgreq.AnswerTime","tag":"AnswerTime","type":"*variable","value":"~*req.12"},{"mandatory":true,"path":"*cgreq.Usage","tag":"Usage","type":"*variable","value":"~*req.13"}],"filters":[],"flags":[],"id":"*default","max_reconnect_interval":"5m0s","opts":{"csvFieldSeparator":",","csvHeaderDefineChar":":","csvRowLength":0,"natsSubject":"cgrates_cdrs","partialCacheAction":"*none","partialOrderField":"~*req.AnswerTime"},"partial_commit_fields":[],"processed_path":"/var/spool/cgrates/ers/out","reconnects":-1,"run_delay":"0","source_path":"/var/spool/cgrates/ers/in","start_delay":"0","tenant":"","timezone":"","type":"*none"}],"sessions_conns":["*internal"]},"filters":{"apiers_conns":[],"rankings_conns":[],"resources_conns":[],"stats_conns":[],"trends_conns":[]},"freeswitch_agent":{"active_session_delimiter":",","create_cdr":false,"empty_balance_ann_file":"","empty_balance_context":"","enabled":false,"event_socket_conns":[{"address":"127.0.0.1:8021","alias":"127.0.0.1:8021","max_reconnect_interval":"0s","password":"ClueCon","reconnects":5,"reply_timeout":"1m0s"}],"extra_fields":"","low_balance_ann_file":"","max_wait_connection":"2s","sessions_conns":["*birpc_internal"],"subscribe_park":true},"general":{"caching_delay":"0","connect_attempts":5,"connect_timeout":"1s","dbdata_encoding":"*msgpack","default_caching":"*reload","default_category":"call","default_request_type":"*rated","default_tenant":"cgrates.org","default_timezone":"Local","digest_equal":":","digest_separator":",","failed_posts_dir":"/var/spool/cgrates/failed_posts","failed_posts_ttl":"5s","locking_timeout":"0","log_level":6,"logger":"*syslog","max_parallel_conns":100,"max_reconnect_interval":"0","node_id":"ENGINE1","poster_attempts":3,"reconnects":-1,"reply_timeout":"2s","rounding_decimals":5,"rsr_separator":";","tpexport_dir":"/var/spool/cgrates/tpe"},"http":{"auth_users":{},"client_opts":{"dialFallbackDelay":"300ms","dialKeepAlive":"30s","dialTimeout":"30s","disableCompression":false,"disableKeepAlives":false,"expectContinueTimeout":"0s","forceAttemptHttp2":true,"idleConnTimeout":"1m30s","maxConnsPerHost":0,"maxIdleConns":100,"maxIdleConnsPerHost":2,"responseHeaderTimeout":"0s","skipTlsVerify":false,"tlsHandshakeTimeout":"10s"},"freeswitch_cdrs_url":"/freeswitch_json","http_cdrs":"/cdr_http","json_rpc_url":"/jsonrpc","pprof_path":"/debug/pprof/","registrars_url":"/registrar","use_basic_auth":false,"ws_url":"/ws"},"http_agent":[],"ips":{"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"opts":{"*units":1,"*usageID":""},"prefix_indexed_fields":[],"store_interval":"0s","string_indexed_fields":null,"suffix_indexed_fields":[]},"kamailio_agent":{"create_cdr":false,"enabled":false,"evapi_conns":[{"address":"127.0.0.1:8448","alias":"","max_reconnect_interval":"0s","reconnects":5}],"sessions_conns":["*birpc_internal"],"timezone":""},"listen":{"http":"127.0.0.1:2080","http_tls":"127.0.0.1:2280","rpc_gob":"127.0.0.1:2013","rpc_gob_tls":"127.0.0.1:2023","rpc_json":"127.0.0.1:2012","rpc_json_tls":"127.0.0.1:2022"},"loader":{"caches_conns":["*localhost"],"data_path":"./","disable_reverse":false,"field_separator":",","gapi_credentials":".gapi/credentials.json","gapi_token":".gapi/token.json","scheduler_conns":["*localhost"],"tpid":""},"loaders":[{"caches_conns":["*internal"],"data":[{"fields":[{"mandatory":true,"path":"Tenant","tag":"TenantID","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ProfileID","type":"*variable","value":"~*req.1"},{"path":"Contexts","tag":"Contexts","type":"*variable","value":"~*req.2"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.3"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.4"},{"path":"AttributeFilterIDs","tag":"AttributeFilterIDs","type":"*variable","value":"~*req.5"},{"path":"Path","tag":"Path","type":"*variable","value":"~*req.6"},{"path":"Type","tag":"Type","type":"*variable","value":"~*req.7"},{"path":"Value","tag":"Value","type":"*variable","value":"~*req.8"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.9"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.10"}],"file_name":"Attributes.csv","flags":null,"type":"*attributes"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Type","tag":"Type","type":"*variable","value":"~*req.2"},{"path":"Element","tag":"Element","type":"*variable","value":"~*req.3"},{"path":"Values","tag":"Values","type":"*variable","value":"~*req.4"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.5"}],"file_name":"Filters.csv","flags":null,"type":"*filters"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"UsageTTL","tag":"TTL","type":"*variable","value":"~*req.4"},{"path":"Limit","tag":"Limit","type":"*variable","value":"~*req.5"},{"path":"AllocationMessage","tag":"AllocationMessage","type":"*variable","value":"~*req.6"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.7"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.8"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.9"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.10"}],"file_name":"Resources.csv","flags":null,"type":"*resources"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"TTL","tag":"TTL","type":"*variable","value":"~*req.4"},{"path":"Type","tag":"Type","type":"*variable","value":"~*req.5"},{"path":"AddressPool","tag":"AddressPool","type":"*variable","value":"~*req.6"},{"path":"Allocation","tag":"Allocation","type":"*variable","value":"~*req.7"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.8"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.9"}],"file_name":"IPs.csv","flags":null,"type":"*ips"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"QueueLength","tag":"QueueLength","type":"*variable","value":"~*req.4"},{"path":"TTL","tag":"TTL","type":"*variable","value":"~*req.5"},{"path":"MinItems","tag":"MinItems","type":"*variable","value":"~*req.6"},{"path":"MetricIDs","tag":"MetricIDs","type":"*variable","value":"~*req.7"},{"path":"MetricFilterIDs","tag":"MetricFilterIDs","type":"*variable","value":"~*req.8"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.9"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.10"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.11"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.12"}],"file_name":"Stats.csv","flags":null,"type":"*stats"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"MaxHits","tag":"MaxHits","type":"*variable","value":"~*req.4"},{"path":"MinHits","tag":"MinHits","type":"*variable","value":"~*req.5"},{"path":"MinSleep","tag":"MinSleep","type":"*variable","value":"~*req.6"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.7"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.8"},{"path":"ActionIDs","tag":"ActionIDs","type":"*variable","value":"~*req.9"},{"path":"Async","tag":"Async","type":"*variable","value":"~*req.10"}],"file_name":"Thresholds.csv","flags":null,"type":"*thresholds"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"Sorting","tag":"Sorting","type":"*variable","value":"~*req.4"},{"path":"SortingParameters","tag":"SortingParameters","type":"*variable","value":"~*req.5"},{"path":"RouteID","tag":"RouteID","type":"*variable","value":"~*req.6"},{"path":"RouteFilterIDs","tag":"RouteFilterIDs","type":"*variable","value":"~*req.7"},{"path":"RouteAccountIDs","tag":"RouteAccountIDs","type":"*variable","value":"~*req.8"},{"path":"RouteRatingPlanIDs","tag":"RouteRatingPlanIDs","type":"*variable","value":"~*req.9"},{"path":"RouteResourceIDs","tag":"RouteResourceIDs","type":"*variable","value":"~*req.10"},{"path":"RouteStatIDs","tag":"RouteStatIDs","type":"*variable","value":"~*req.11"},{"path":"RouteWeight","tag":"RouteWeight","type":"*variable","value":"~*req.12"},{"path":"RouteBlocker","tag":"RouteBlocker","type":"*variable","value":"~*req.13"},{"path":"RouteParameters","tag":"RouteParameters","type":"*variable","value":"~*req.14"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.15"}],"file_name":"Routes.csv","flags":null,"type":"*routes"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"RunID","tag":"RunID","type":"*variable","value":"~*req.4"},{"path":"AttributeIDs","tag":"AttributeIDs","type":"*variable","value":"~*req.5"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.6"}],"file_name":"Chargers.csv","flags":null,"type":"*chargers"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Contexts","tag":"Contexts","type":"*variable","value":"~*req.2"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.3"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.4"},{"path":"Strategy","tag":"Strategy","type":"*variable","value":"~*req.5"},{"path":"StrategyParameters","tag":"StrategyParameters","type":"*variable","value":"~*req.6"},{"path":"ConnID","tag":"ConnID","type":"*variable","value":"~*req.7"},{"path":"ConnFilterIDs","tag":"ConnFilterIDs","type":"*variable","value":"~*req.8"},{"path":"ConnWeight","tag":"ConnWeight","type":"*variable","value":"~*req.9"},{"path":"ConnBlocker","tag":"ConnBlocker","type":"*variable","value":"~*req.10"},{"path":"ConnParameters","tag":"ConnParameters","type":"*variable","value":"~*req.11"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.12"}],"file_name":"DispatcherProfiles.csv","flags":null,"type":"*dispatchers"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Address","tag":"Address","type":"*variable","value":"~*req.2"},{"path":"Transport","tag":"Transport","type":"*variable","value":"~*req.3"},{"path":"ConnectAttempts","tag":"ConnectAttempts","type":"*variable","value":"~*req.4"},{"path":"Reconnects","tag":"Reconnects","type":"*variable","value":"~*req.5"},{"path":"MaxReconnectInterval","tag":"MaxReconnectInterval","type":"*variable","value":"~*req.6"},{"path":"ConnectTimeout","tag":"ConnectTimeout","type":"*variable","value":"~*req.7"},{"path":"ReplyTimeout","tag":"ReplyTimeout","type":"*variable","value":"~*req.8"},{"path":"TLS","tag":"TLS","type":"*variable","value":"~*req.9"},{"path":"ClientKey","tag":"ClientKey","type":"*variable","value":"~*req.10"},{"path":"ClientCertificate","tag":"ClientCertificate","type":"*variable","value":"~*req.11"},{"path":"CaCertificate","tag":"CaCertificate","type":"*variable","value":"~*req.12"}],"file_name":"DispatcherHosts.csv","flags":null,"type":"*dispatcher_hosts"}],"dry_run":false,"enabled":false,"field_separator":",","id":"*default","lockfile_path":".cgr.lck","run_delay":"0","tenant":"","tp_in_dir":"/var/spool/cgrates/loader/in","tp_out_dir":"/var/spool/cgrates/loader/out"}],"mailer":{"auth_password":"CGRateS.org","auth_user":"cgrates","from_address":"cgr-mailer@localhost.localdomain","server":"localhost"},"migrator":{"out_datadb_encoding":"msgpack","out_datadb_host":"127.0.0.1","out_datadb_name":"10","out_datadb_opts":{"mongoConnScheme":"mongodb","mongoQueryTimeout":"0s","redisCACertificate":"","redisClientCertificate":"","redisClientKey":"","redisCluster":false,"redisClusterOndownDelay":"0s","redisClusterSync":"5s","redisConnectAttempts":20,"redisConnectTimeout":"0s","redisMaxConns":10,"redisPoolPipelineLimit":0,"redisPoolPipelineWindow":"150µs","redisReadTimeout":"0s","redisSentinel":"","redisTLS":false,"redisWriteTimeout":"0s"},"out_datadb_password":"","out_datadb_port":"6379","out_datadb_type":"*redis","out_datadb_user":"cgrates","out_stordb_host":"127.0.0.1","out_stordb_name":"cgrates","out_stordb_opts":{"mongoConnScheme":"mongodb","mongoQueryTimeout":"0s","mysqlDSNParams":null,"mysqlLocation":"","pgSSLMode":"","sqlConnMaxLifetime":"0s","sqlMaxIdleConns":0,"sqlMaxOpenConns":0},"out_stordb_password":"","out_stordb_port":"3306","out_stordb_type":"*mysql","out_stordb_user":"cgrates","users_filters":null},"prometheus_agent":{"collect_go_metrics":false,"collect_process_metrics":false,"cores_conns":null,"enabled":false,"path":"/prometheus","stat_queue_ids":null,"stats_conns":null},"radius_agent":{"client_dictionaries":{"*default":["/usr/share/cgrates/radius/dict/"]},"client_secrets":{"*default":"CGRateS.org"},"coa_template":"*coa","dmr_template":"*dmr","enabled":false,"listeners":[{"acct_address":"127.0.0.1:1813","auth_address":"127.0.0.1:1812","network":"udp"}],"request_processors":[],"requests_cache_key":"","sessions_conns":["*internal"]},"rals":{"balance_rating_subject":{"*any":"*zero1ns","*voice":"*zero1s"},"enabled":false,"fallback_depth":3,"max_computed_usage":{"*any":"189h0m0s","*data":"107374182400","*mms":"10000","*sms":"10000","*voice":"72h0m0s"},"max_increments":1000000,"remove_expired":true,"rp_subject_prefix_matching":false,"sessions_conns":[],"stats_conns":[],"thresholds_conns":[]},"rankings":{"ees_conns":[],"ees_exporter_ids":[],"enabled":false,"scheduled_ids":{},"stats_conns":[],"store_interval":"","thresholds_conns":[]},"registrarc":{"dispatchers":{"hosts":[],"refresh_interval":"5m0s","registrars_conns":[]},"rpc":{"hosts":[],"refresh_interval":"5m0s","registrars_conns":[]}},"resources":{"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"opts":{"*units":1,"*usageID":""},"prefix_indexed_fields":[],"store_interval":"","suffix_indexed_fields":[],"thresholds_conns":[]},"routes":{"attributes_conns":[],"default_ratio":1,"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"opts":{"*context":"*routes","*ignoreErrors":false,"*maxCost":""},"prefix_indexed_fields":[],"rals_conns":[],"resources_conns":[],"stats_conns":[],"suffix_indexed_fields":[]},"rpc_conns":{"*bijson_localhost":{"conns":[{"address":"127.0.0.1:2014","transport":"*birpc_json"}],"poolSize":0,"strategy":"*first"},"*birpc_internal":{"conns":[{"address":"*birpc_internal","transport":""}],"poolSize":0,"strategy":"*first"},"*internal":{"conns":[{"address":"*internal","transport":""}],"poolSize":0,"strategy":"*first"},"*localhost":{"conns":[{"address":"127.0.0.1:2012","transport":"*json"}],"poolSize":0,"strategy":"*first"}},"schedulers":{"cdrs_conns":[],"dynaprepaid_actionplans":[],"enabled":false,"filters":[],"stats_conns":[],"thresholds_conns":[]},"sentrypeer":{"Audience":"https://sentrypeer.com/api","ClientID":"","ClientSecret":"","GrantType":"client_credentials","IpUrl":"https://sentrypeer.com/api/ip-addresses","NumberUrl":"https://sentrypeer.com/api/phone-numbers","TokenURL":"https://authz.sentrypeer.com/oauth/token"},"sessions":{"alterable_fields":[],"attributes_conns":[],"backup_interval":"0","cdrs_conns":[],"channel_sync_interval":"0","chargers_conns":[],"client_protocol":2,"debit_interval":"0","default_usage":{"*any":"3h0m0s","*data":"1048576","*sms":"1","*voice":"3h0m0s"},"enabled":false,"listen_bigob":"","listen_bijson":"127.0.0.1:2014","min_dur_low_balance":"0","rals_conns":[],"replication_conns":[],"resources_conns":[],"routes_conns":[],"scheduler_conns":[],"session_indexes":[],"session_ttl":"0","stale_chan_max_extra_usage":"0","stats_conns":[],"stir":{"allowed_attest":["*any"],"default_attest":"A","payload_maxduration":"-1","privatekey_path":"","publickey_path":""},"store_session_costs":false,"terminate_attempts":5,"thresholds_conns":[]},"sip_agent":{"enabled":false,"listen":"127.0.0.1:5060","listen_net":"udp","request_processors":[],"retransmission_timer":1000000000,"sessions_conns":["*internal"],"timezone":""},"stats":{"ees_conns":[],"ees_exporter_ids":[],"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"opts":{"*profileIDs":[],"*profileIgnoreFilters":false},"prefix_indexed_fields":[],"store_interval":"","store_uncompressed_limit":0,"suffix_indexed_fields":[],"thresholds_conns":[]},"stor_db":{"db_host":"127.0.0.1","db_name":"cgrates","db_password":"CGRateS.org","db_port":3306,"db_type":"*mysql","db_user":"cgrates","items":{"*cdrs":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*session_costs":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_account_actions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_action_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_action_triggers":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_actions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_attributes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_chargers":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_destination_rates":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_destinations":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_dispatcher_hosts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_dispatcher_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_filters":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_ips":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rankings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rates":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rating_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rating_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_resources":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_routes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_shared_groups":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_stats":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_thresholds":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_timings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_trends":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*versions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false}},"opts":{"internalDBBackupPath":"/var/lib/cgrates/internal_db/backup/stordb","internalDBDumpInterval":"0s","internalDBDumpPath":"/var/lib/cgrates/internal_db/stordb","internalDBFileSizeLimit":1073741824,"internalDBRewriteInterval":"0s","internalDBStartTimeout":"5m0s","mongoConnScheme":"mongodb","mongoQueryTimeout":"10s","mysqlDSNParams":{},"mysqlLocation":"Local","pgSSLMode":"disable","pgSchema":"","sqlConnMaxLifetime":"0s","sqlLogLevel":3,"sqlMaxIdleConns":10,"sqlMaxOpenConns":100},"prefix_indexed_fields":[],"remote_conns":null,"replication_conns":null,"string_indexed_fields":[]},"suretax":{"bill_to_number":"","business_unit":"","client_number":"","client_tracking":"~*req.CGRID","customer_number":"~*req.Subject","include_local_cost":false,"orig_number":"~*req.Subject","p2pplus4":"","p2pzipcode":"","plus4":"","regulatory_code":"03","response_group":"03","response_type":"D4","return_file_code":"0","sales_type_code":"R","tax_exemption_code_list":"","tax_included":"0","tax_situs_rule":"04","term_number":"~*req.Destination","timezone":"UTC","trans_type_code":"010101","unit_type":"00","units":"1","url":"","validation_key":"","zipcode":""},"templates":{"*asr":[{"mandatory":true,"path":"*diamreq.Session-Id","tag":"SessionId","type":"*variable","value":"~*req.Session-Id"},{"mandatory":true,"path":"*diamreq.Origin-Host","tag":"OriginHost","type":"*variable","value":"~*req.Destination-Host"},{"mandatory":true,"path":"*diamreq.Origin-Realm","tag":"OriginRealm","type":"*variable","value":"~*req.Destination-Realm"},{"mandatory":true,"path":"*diamreq.Destination-Realm","tag":"DestinationRealm","type":"*variable","value":"~*req.Origin-Realm"},{"mandatory":true,"path":"*diamreq.Destination-Host","tag":"DestinationHost","type":"*variable","value":"~*req.Origin-Host"},{"mandatory":true,"path":"*diamreq.Auth-Application-Id","tag":"AuthApplicationId","type":"*variable","value":"~*vars.*appid"}],"*cca":[{"mandatory":true,"path":"*rep.Session-Id","tag":"SessionId","type":"*variable","value":"~*req.Session-Id"},{"path":"*rep.Result-Code","tag":"ResultCode","type":"*constant","value":"2001"},{"mandatory":true,"path":"*rep.Origin-Host","tag":"OriginHost","type":"*variable","value":"~*vars.OriginHost"},{"mandatory":true,"path":"*rep.Origin-Realm","tag":"OriginRealm","type":"*variable","value":"~*vars.OriginRealm"},{"mandatory":true,"path":"*rep.Auth-Application-Id","tag":"AuthApplicationId","type":"*variable","value":"~*vars.*appid"},{"mandatory":true,"path":"*rep.CC-Request-Type","tag":"CCRequestType","type":"*variable","value":"~*req.CC-Request-Type"},{"mandatory":true,"path":"*rep.CC-Request-Number","tag":"CCRequestNumber","type":"*variable","value":"~*req.CC-Request-Number"}],"*cdrLog":[{"mandatory":true,"path":"*cdr.ToR","tag":"ToR","type":"*variable","value":"~*req.BalanceType"},{"mandatory":true,"path":"*cdr.OriginHost","tag":"OriginHost","type":"*constant","value":"127.0.0.1"},{"mandatory":true,"path":"*cdr.RequestType","tag":"RequestType","type":"*constant","value":"*none"},{"mandatory":true,"path":"*cdr.Tenant","tag":"Tenant","type":"*variable","value":"~*req.Tenant"},{"mandatory":true,"path":"*cdr.Account","tag":"Account","type":"*variable","value":"~*req.Account"},{"mandatory":true,"path":"*cdr.Subject","tag":"Subject","type":"*variable","value":"~*req.Account"},{"mandatory":true,"path":"*cdr.Cost","tag":"Cost","type":"*variable","value":"~*req.Cost"},{"mandatory":true,"path":"*cdr.Source","tag":"Source","type":"*constant","value":"*cdrLog"},{"mandatory":true,"path":"*cdr.Usage","tag":"Usage","type":"*constant","value":"1"},{"mandatory":true,"path":"*cdr.RunID","tag":"RunID","type":"*variable","value":"~*req.ActionType"},{"mandatory":true,"path":"*cdr.SetupTime","tag":"SetupTime","type":"*constant","value":"*now"},{"mandatory":true,"path":"*cdr.AnswerTime","tag":"AnswerTime","type":"*constant","value":"*now"},{"mandatory":true,"path":"*cdr.PreRated","tag":"PreRated","type":"*constant","value":"true"}],"*coa":[{"path":"*radDAReq.User-Name","tag":"User-Name","type":"*variable","value":"~*oreq.User-Name"},{"path":"*radDAReq.NAS-IP-Address","tag":"NAS-IP-Address","type":"*variable","value":"~*oreq.NAS-IP-Address"},{"path":"*radDAReq.Acct-Session-Id","tag":"Acct-Session-Id","type":"*variable","value":"~*oreq.Acct-Session-Id"},{"path":"*radDAReq.Filter-Id","tag":"Filter-Id","type":"*variable","value":"~*req.CustomFilter"}],"*dmr":[{"path":"*radDAReq.User-Name","tag":"User-Name","type":"*variable","value":"~*oreq.User-Name"},{"path":"*radDAReq.NAS-IP-Address","tag":"NAS-IP-Address","type":"*variable","value":"~*oreq.NAS-IP-Address"},{"path":"*radDAReq.Acct-Session-Id","tag":"Acct-Session-Id","type":"*variable","value":"~*oreq.Acct-Session-Id"},{"path":"*radDAReq.Reply-Message","tag":"Reply-Message","type":"*variable","value":"~*req.DisconnectCause"}],"*err":[{"mandatory":true,"path":"*rep.Session-Id","tag":"SessionId","type":"*variable","value":"~*req.Session-Id"},{"mandatory":true,"path":"*rep.Origin-Host","tag":"OriginHost","type":"*variable","value":"~*vars.OriginHost"},{"mandatory":true,"path":"*rep.Origin-Realm","tag":"OriginRealm","type":"*variable","value":"~*vars.OriginRealm"}],"*errSip":[{"mandatory":true,"path":"*rep.Request","tag":"Request","type":"*constant","value":"SIP/2.0 500 Internal Server Error"}],"*rar":[{"mandatory":true,"path":"*diamreq.Session-Id","tag":"SessionId","type":"*variable","value":"~*req.Session-Id"},{"mandatory":true,"path":"*diamreq.Origin-Host","tag":"OriginHost","type":"*variable","value":"~*req.Destination-Host"},{"mandatory":true,"path":"*diamreq.Origin-Realm","tag":"OriginRealm","type":"*variable","value":"~*req.Destination-Realm"},{"mandatory":true,"path":"*diamreq.Destination-Realm","tag":"DestinationRealm","type":"*variable","value":"~*req.Origin-Realm"},{"mandatory":true,"path":"*diamreq.Destination-Host","tag":"DestinationHost","type":"*variable","value":"~*req.Origin-Host"},{"mandatory":true,"path":"*diamreq.Auth-Application-Id","tag":"AuthApplicationId","type":"*variable","value":"~*vars.*appid"},{"path":"*diamreq.Re-Auth-Request-Type","tag":"ReAuthRequestType","type":"*constant","value":"0"}]},"thresholds":{"ees_conns":[],"ees_exporter_ids":[],"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"opts":{"*profileIDs":[],"*profileIgnoreFilters":false},"prefix_indexed_fields":[],"store_interval":"","suffix_indexed_fields":[]},"tls":{"ca_certificate":"","client_certificate":"","client_key":"","server_certificate":"","server_key":"","server_name":"","server_policy":4},"trends":{"ees_conns":[],"ees_exporter_ids":[],"enabled":false,"scheduled_ids":{},"stats_conns":[],"store_interval":"","store_uncompressed_limit":0,"thresholds_conns":[]}}` if err != nil { t.Fatal(err) } @@ -5655,14 +5721,14 @@ func TestReloadSections(t *testing.T) { subsystemsThatNeedDataDB := utils.NewStringSet([]string{SCHEDULER_JSN, RALS_JSN, CDRS_JSN, SessionSJson, ATTRIBUTE_JSN, ChargerSCfgJson, RESOURCES_JSON, STATS_JSON, THRESHOLDS_JSON, - RouteSJson, LoaderJson, DispatcherSJson, ApierS}) + RouteSJson, LoaderJson, DispatcherSJson, ApierS, IPsJSON}) subsystemsThatNeedStorDB := utils.NewStringSet([]string{RALS_JSN, CDRS_JSN, ApierS}) cfgCgr := NewDefaultCGRConfig() for _, section := range []string{RPCConnsJsonName, HTTP_JSN, SCHEDULER_JSN, RALS_JSN, CDRS_JSN, ERsJson, SessionSJson, AsteriskAgentJSN, FreeSWITCHAgentJSN, KamailioAgentJSN, DA_JSN, RA_JSN, HttpAgentJson, DNSAgentJson, ATTRIBUTE_JSN, ChargerSCfgJson, RESOURCES_JSON, STATS_JSON, THRESHOLDS_JSON, RouteSJson, - LoaderJson, DispatcherSJson, ApierS, EEsJson, SIPAgentJson, RegistrarCJson, AnalyzerCfgJson} { + LoaderJson, DispatcherSJson, ApierS, EEsJson, SIPAgentJson, RegistrarCJson, AnalyzerCfgJson, IPsJSON} { for _, section := range sortedCfgSections { cfgCgr.rldChans[section] = make(chan struct{}, 1) } @@ -5902,6 +5968,9 @@ func TestCGRConfigClone(t *testing.T) { if !reflect.DeepEqual(cfg.coreSCfg, rcv.coreSCfg) { t.Errorf("Expected: %+v\nReceived: %+v", utils.ToJSON(cfg.coreSCfg), utils.ToJSON(rcv.coreSCfg)) } + if !reflect.DeepEqual(cfg.ipsCfg, rcv.ipsCfg) { + t.Errorf("Expected: %+v\nReceived: %+v", utils.ToJSON(cfg.ipsCfg), utils.ToJSON(rcv.ipsCfg)) + } } func TestCGRConfigGetDP(t *testing.T) { diff --git a/config/configsanity.go b/config/configsanity.go index b15c4f67c..43f73fcdd 100644 --- a/config/configsanity.go +++ b/config/configsanity.go @@ -139,7 +139,7 @@ func (cfg *CGRConfig) checkConfigSanity() error { } } for _, data := range ldrSCfg.Data { - if !posibleLoaderTypes.Has(data.Type) { + if !possibleLoaderTypes.Has(data.Type) { return fmt.Errorf("<%s> unsupported data type %s", utils.LoaderS, data.Type) } diff --git a/config/ips.go b/config/ips.go new file mode 100644 index 000000000..47305943e --- /dev/null +++ b/config/ips.go @@ -0,0 +1,177 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package config + +import ( + "slices" + "time" + + "github.com/cgrates/cgrates/utils" +) + +// IPsCfg represents the configuration of the IPs module. +type IPsCfg struct { + Enabled bool + IndexedSelects bool + StoreInterval time.Duration + StringIndexedFields *[]string + PrefixIndexedFields *[]string + SuffixIndexedFields *[]string + ExistsIndexedFields *[]string + NestedFields bool + Opts *IPsOpts +} + +func (c *IPsCfg) loadFromJSONCfg(jc *IPsJsonCfg) error { + if jc == nil { + return nil + } + if jc.Enabled != nil { + c.Enabled = *jc.Enabled + } + if jc.IndexedSelects != nil { + c.IndexedSelects = *jc.IndexedSelects + } + if jc.StoreInterval != nil { + v, err := utils.ParseDurationWithNanosecs(*jc.StoreInterval) + if err != nil { + return err + } + c.StoreInterval = v + } + if jc.StringIndexedFields != nil { + sif := slices.Clone(*jc.StringIndexedFields) + c.StringIndexedFields = &sif + } + if jc.PrefixIndexedFields != nil { + pif := slices.Clone(*jc.PrefixIndexedFields) + c.PrefixIndexedFields = &pif + } + if jc.SuffixIndexedFields != nil { + sif := slices.Clone(*jc.SuffixIndexedFields) + c.SuffixIndexedFields = &sif + } + if jc.ExistsIndexedFields != nil { + eif := slices.Clone(*jc.ExistsIndexedFields) + c.ExistsIndexedFields = &eif + } + if jc.NestedFields != nil { + c.NestedFields = *jc.NestedFields + } + if jc.Opts != nil { + if err := c.Opts.loadFromJSONCfg(jc.Opts); err != nil { + return err + } + } + return nil +} + +// Clone returns a deep copy of IPsCfg. +func (c IPsCfg) Clone() *IPsCfg { + clone := &IPsCfg{ + Enabled: c.Enabled, + IndexedSelects: c.IndexedSelects, + StoreInterval: c.StoreInterval, + NestedFields: c.NestedFields, + Opts: c.Opts.Clone(), + } + if c.StringIndexedFields != nil { + idx := slices.Clone(*c.StringIndexedFields) + clone.StringIndexedFields = &idx + } + if c.PrefixIndexedFields != nil { + idx := slices.Clone(*c.PrefixIndexedFields) + clone.PrefixIndexedFields = &idx + } + if c.SuffixIndexedFields != nil { + idx := slices.Clone(*c.SuffixIndexedFields) + clone.SuffixIndexedFields = &idx + } + if c.ExistsIndexedFields != nil { + idx := slices.Clone(*c.ExistsIndexedFields) + clone.ExistsIndexedFields = &idx + } + return clone +} + +// AsMapInterface returns the ips config as a map[string]any. +func (c IPsCfg) AsMapInterface() any { + return map[string]any{ + utils.EnabledCfg: c.Enabled, + utils.IndexedSelectsCfg: c.IndexedSelects, + utils.NestedFieldsCfg: c.NestedFields, + utils.StoreIntervalCfg: c.StoreInterval.String(), + utils.StringIndexedFieldsCfg: c.StringIndexedFields, + utils.PrefixIndexedFieldsCfg: c.PrefixIndexedFields, + utils.SuffixIndexedFieldsCfg: c.SuffixIndexedFields, + utils.ExistsIndexedFieldsCfg: c.ExistsIndexedFields, + utils.OptsCfg: c.Opts.AsMapInterface(), + } +} + +type IPsOpts struct { + UsageID string + TTL *time.Duration + Units float64 +} + +func (o *IPsOpts) loadFromJSONCfg(jc *IPsOptsJson) error { + if jc == nil { + return nil + } + if jc.UsageID != nil { + o.UsageID = *jc.UsageID + } + if jc.TTL != nil { + ttl, err := utils.ParseDurationWithNanosecs(*jc.TTL) + if err != nil { + return err + } + o.TTL = &ttl + } + if jc.Units != nil { + o.Units = *jc.Units + } + return nil +} + +// Clone returns a deep copy of IPsOpts. +func (o *IPsOpts) Clone() *IPsOpts { + cln := &IPsOpts{ + UsageID: o.UsageID, + Units: o.Units, + } + if o.TTL != nil { + cln.TTL = new(time.Duration) + *cln.TTL = *o.TTL + } + return cln +} + +// AsMapInterface returns the config as a map[string]any. +func (o *IPsOpts) AsMapInterface() map[string]any { + m := map[string]any{ + utils.MetaUsageIDCfg: o.UsageID, + utils.MetaUnitsCfg: o.Units, + } + if o.TTL != nil { + m[utils.MetaTTLCfg] = *o.TTL + } + return m +} diff --git a/config/libconfig_json.go b/config/libconfig_json.go index 445d777ab..e348c6b19 100644 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -981,6 +981,26 @@ type CoreSJsonCfg struct { Shutdown_timeout *string } +type IPsOptsJson struct { + UsageID *string `json:"*usageID"` + TTL *string `json:"*ttl"` + Units *float64 `json:"*units"` +} + +// IPsJsonCfg holds the unparsed ips section configuration as found in the +// config file. +type IPsJsonCfg struct { + Enabled *bool `json:"enabled"` + IndexedSelects *bool `json:"indexed_selects"` + StoreInterval *string `json:"store_interval"` + StringIndexedFields *[]string `json:"string_indexed_fields"` + PrefixIndexedFields *[]string `json:"prefix_indexed_fields"` + SuffixIndexedFields *[]string `json:"suffix_indexed_fields"` + ExistsIndexedFields *[]string `json:"exists_indexed_fields"` + NestedFields *bool `json:"nested_fields"` + Opts *IPsOptsJson `json:"opts"` +} + // tagInternalConns adds subsystem to internal connections. func tagInternalConns(conns []string, subsystem string) []string { suffix := utils.ConcatenatedKeySep + subsystem diff --git a/data/conf/samples/radius_coa_disconnect/cgrates.json b/data/conf/samples/radius_coa_disconnect/cgrates.json index bccacc991..017a84a76 100644 --- a/data/conf/samples/radius_coa_disconnect/cgrates.json +++ b/data/conf/samples/radius_coa_disconnect/cgrates.json @@ -13,7 +13,7 @@ }, "filters": { - "apiers_conns": ["*localhost"], + "apiers_conns": ["*localhost"] }, "rals": { @@ -62,12 +62,12 @@ "radius_agent": { "enabled": true, "sessions_conns": ["*bijson_localhost"], - "client_da_addresses": { - "127.0.0.1": { - "transport": "udp", - "host": "", - "port": 3799, - "flags": ["*log"] + "client_da_addresses": { + "127.0.0.1": { + "transport": "udp", + "host": "", + "port": 3799, + "flags": ["*log"] } }, "listeners":[ diff --git a/data/docker/integration/scripts/mysql/create_tariffplan_tables.sql b/data/docker/integration/scripts/mysql/create_tariffplan_tables.sql index c184be757..c480faa71 100644 --- a/data/docker/integration/scripts/mysql/create_tariffplan_tables.sql +++ b/data/docker/integration/scripts/mysql/create_tariffplan_tables.sql @@ -267,6 +267,30 @@ CREATE TABLE tp_resources ( UNIQUE KEY `unique_tp_resource` (`tpid`,`tenant`, `id`,`filter_ids` ) ); +-- +-- Table structure for table `tp_ips` +-- + +DROP TABLE IF EXISTS tp_ips; +CREATE TABLE tp_ips ( + `pk` int(11) NOT NULL AUTO_INCREMENT, + `tpid` varchar(64) NOT NULL, + `tenant` varchar(64) NOT NULL, + `id` varchar(64) NOT NULL, + `filter_ids` varchar(64) NOT NULL, + `activation_interval` varchar(64) NOT NULL, + `ttl` varchar(32) NOT NULL, + `type` varchar(64) NOT NULL, + `address_pool` varchar(64) NOT NULL, + `allocation` varchar(64) NOT NULL, + `stored` BOOLEAN NOT NULL, + `weight` decimal(8,2) NOT NULL, + `created_at` TIMESTAMP, + PRIMARY KEY (`pk`), + KEY `tpid` (`tpid`), + UNIQUE KEY `unique_tp_ip` (`tpid`,`tenant`, `id`,`filter_ids` ) +); + -- -- Table structure for table `tp_stats` -- diff --git a/data/docker/integration/scripts/postgres/create_tariffplan_tables.sql b/data/docker/integration/scripts/postgres/create_tariffplan_tables.sql index 0d0bca9b3..a2de058b5 100644 --- a/data/docker/integration/scripts/postgres/create_tariffplan_tables.sql +++ b/data/docker/integration/scripts/postgres/create_tariffplan_tables.sql @@ -263,6 +263,30 @@ CREATE INDEX tp_resources_idx ON tp_resources (tpid); CREATE INDEX tp_resources_unique ON tp_resources ("tpid", "tenant", "id", "filter_ids"); +-- +-- Table structure for table `tp_ips` +-- + +DROP TABLE IF EXISTS tp_ips; +CREATE TABLE tp_ips ( + "pk" SERIAL PRIMARY KEY, + "tpid" varchar(64) NOT NULL, + "tenant"varchar(64) NOT NULL, + "id" varchar(64) NOT NULL, + "filter_ids" varchar(64) NOT NULL, + "activation_interval" varchar(64) NOT NULL, + "ttl" varchar(32) NOT NULL, + "type" varchar(64) NOT NULL, + "address_pool" varchar(64) NOT NULL, + "allocation" varchar(64) NOT NULL, + "stored" BOOLEAN NOT NULL, + "weight" NUMERIC(8,2) NOT NULL, + "created_at" TIMESTAMP WITH TIME ZONE +); +CREATE INDEX tp_ips_idx ON tp_ips (tpid); +CREATE INDEX tp_ips_unique ON tp_ips ("tpid", "tenant", "id", "filter_ids"); + + -- -- Table structure for table `tp_stats` -- diff --git a/data/storage/docker_mysql/scripts/create_tariffplan_tables.sql b/data/storage/docker_mysql/scripts/create_tariffplan_tables.sql index faa327a9c..8645600a9 100644 --- a/data/storage/docker_mysql/scripts/create_tariffplan_tables.sql +++ b/data/storage/docker_mysql/scripts/create_tariffplan_tables.sql @@ -267,6 +267,30 @@ CREATE TABLE tp_resources ( UNIQUE KEY `unique_tp_resource` (`tpid`,`tenant`, `id`,`filter_ids` ) ); +-- +-- Table structure for table `tp_ips` +-- + +DROP TABLE IF EXISTS tp_ips; +CREATE TABLE tp_ips ( + `pk` int(11) NOT NULL AUTO_INCREMENT, + `tpid` varchar(64) NOT NULL, + `tenant` varchar(64) NOT NULL, + `id` varchar(64) NOT NULL, + `filter_ids` varchar(64) NOT NULL, + `activation_interval` varchar(64) NOT NULL, + `ttl` varchar(32) NOT NULL, + `type` varchar(64) NOT NULL, + `address_pool` varchar(64) NOT NULL, + `allocation` varchar(64) NOT NULL, + `stored` BOOLEAN NOT NULL, + `weight` decimal(8,2) NOT NULL, + `created_at` TIMESTAMP, + PRIMARY KEY (`pk`), + KEY `tpid` (`tpid`), + UNIQUE KEY `unique_tp_ip` (`tpid`,`tenant`, `id`,`filter_ids` ) +); + -- -- Table structure for table `tp_stats` -- diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index 05eb7f48b..169e218fe 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -267,6 +267,30 @@ CREATE TABLE tp_resources ( UNIQUE KEY `unique_tp_resource` (`tpid`,`tenant`, `id`,`filter_ids` ) ); +-- +-- Table structure for table `tp_ips` +-- + +DROP TABLE IF EXISTS tp_ips; +CREATE TABLE tp_ips ( + `pk` int(11) NOT NULL AUTO_INCREMENT, + `tpid` varchar(64) NOT NULL, + `tenant` varchar(64) NOT NULL, + `id` varchar(64) NOT NULL, + `filter_ids` varchar(64) NOT NULL, + `activation_interval` varchar(64) NOT NULL, + `ttl` varchar(32) NOT NULL, + `type` varchar(64) NOT NULL, + `address_pool` varchar(64) NOT NULL, + `allocation` varchar(64) NOT NULL, + `stored` BOOLEAN NOT NULL, + `weight` decimal(8,2) NOT NULL, + `created_at` TIMESTAMP, + PRIMARY KEY (`pk`), + KEY `tpid` (`tpid`), + UNIQUE KEY `unique_tp_ip` (`tpid`,`tenant`, `id`,`filter_ids` ) +); + -- -- Table structure for table `tp_stats` -- diff --git a/data/storage/postgres/create_tariffplan_tables.sql b/data/storage/postgres/create_tariffplan_tables.sql index d2bdd4f38..0e1d7aaf5 100644 --- a/data/storage/postgres/create_tariffplan_tables.sql +++ b/data/storage/postgres/create_tariffplan_tables.sql @@ -263,6 +263,30 @@ CREATE INDEX tp_resources_idx ON tp_resources (tpid); CREATE INDEX tp_resources_unique ON tp_resources ("tpid", "tenant", "id", "filter_ids"); +-- +-- Table structure for table `tp_ips` +-- + +DROP TABLE IF EXISTS tp_ips; +CREATE TABLE tp_ips ( + "pk" SERIAL PRIMARY KEY, + "tpid" varchar(64) NOT NULL, + "tenant"varchar(64) NOT NULL, + "id" varchar(64) NOT NULL, + "filter_ids" varchar(64) NOT NULL, + "activation_interval" varchar(64) NOT NULL, + "ttl" varchar(32) NOT NULL, + "type" varchar(64) NOT NULL, + "address_pool" varchar(64) NOT NULL, + "allocation" varchar(64) NOT NULL, + "stored" BOOLEAN NOT NULL, + "weight" NUMERIC(8,2) NOT NULL, + "created_at" TIMESTAMP WITH TIME ZONE +); +CREATE INDEX tp_ips_idx ON tp_ips (tpid); +CREATE INDEX tp_ips_unique ON tp_ips ("tpid", "tenant", "id", "filter_ids"); + + -- -- Table structure for table `tp_stats` -- @@ -289,6 +313,7 @@ CREATE TABLE tp_stats ( CREATE INDEX tp_stats_idx ON tp_stats (tpid); CREATE INDEX tp_stats_unique ON tp_stats ("tpid","tenant", "id", "filter_ids","metric_ids"); + -- -- Table structure for table `tp_rankings` -- diff --git a/dispatchers/caches_it_test.go b/dispatchers/caches_it_test.go index a9e107019..f4694e745 100644 --- a/dispatchers/caches_it_test.go +++ b/dispatchers/caches_it_test.go @@ -141,7 +141,7 @@ func testDspChcLoadAfterFolder(t *testing.T) { expStats[utils.CacheRouteProfiles].Items = 3 expStats[utils.CacheThresholdProfiles].Items = 2 expStats[utils.CacheThresholds].Items = 2 - expStats[utils.CacheLoadIDs].Items = 35 + expStats[utils.CacheLoadIDs].Items = 38 expStats[utils.CacheTimings].Items = 10 expStats[utils.CacheThresholdFilterIndexes].Items = 2 expStats[utils.CacheThresholdFilterIndexes].Groups = 1 @@ -179,6 +179,8 @@ func testDspChcPrecacheStatus(t *testing.T) { utils.CacheSharedGroups: utils.MetaReady, utils.CacheResourceProfiles: utils.MetaReady, utils.CacheResources: utils.MetaReady, + utils.CacheIPProfiles: utils.MetaReady, + utils.CacheIPs: utils.MetaReady, utils.CacheTimings: utils.MetaReady, utils.CacheStatQueueProfiles: utils.MetaReady, utils.CacheStatQueues: utils.MetaReady, @@ -193,6 +195,7 @@ func testDspChcPrecacheStatus(t *testing.T) { utils.CacheDiameterMessages: utils.MetaReady, utils.CacheAttributeFilterIndexes: utils.MetaReady, utils.CacheResourceFilterIndexes: utils.MetaReady, + utils.CacheIPFilterIndexes: utils.MetaReady, utils.CacheStatFilterIndexes: utils.MetaReady, utils.CacheThresholdFilterIndexes: utils.MetaReady, utils.CacheRouteFilterIndexes: utils.MetaReady, @@ -203,6 +206,7 @@ func testDspChcPrecacheStatus(t *testing.T) { utils.CacheClosedSessions: utils.MetaReady, utils.CacheDispatcherRoutes: utils.MetaReady, utils.CacheEventResources: utils.MetaReady, + utils.CacheEventIPs: utils.MetaReady, utils.CacheRPCConnections: utils.MetaReady, utils.CacheRPCResponses: utils.MetaReady, utils.CacheRatingProfilesTmp: utils.MetaReady, diff --git a/dispatchers/ips.go b/dispatchers/ips.go new file mode 100644 index 000000000..91e5026aa --- /dev/null +++ b/dispatchers/ips.go @@ -0,0 +1,119 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package dispatchers + +import ( + "time" + + "github.com/cgrates/birpc/context" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +func (dS *DispatcherService) IPsV1Ping(ctx *context.Context, args *utils.CGREvent, rpl *string) (err error) { + if args == nil { + args = new(utils.CGREvent) + } + args.Tenant = utils.FirstNonEmpty(args.Tenant, dS.cfg.GeneralCfg().DefaultTenant) + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.IPsV1Ping, args.Tenant, + utils.IfaceAsString(args.APIOpts[utils.OptsAPIKey]), args.Time); err != nil { + return + } + } + return dS.Dispatch(args, utils.MetaIPs, utils.IPsV1Ping, args, rpl) +} + +func (dS *DispatcherService) IPsV1GetIPsForEvent(ctx *context.Context, args *utils.CGREvent, + reply *engine.IPs) (err error) { + tnt := dS.cfg.GeneralCfg().DefaultTenant + if args != nil && args.Tenant != utils.EmptyString { + tnt = args.Tenant + } + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.IPsV1GetIPsForEvent, tnt, + utils.IfaceAsString(args.APIOpts[utils.OptsAPIKey]), args.Time); err != nil { + return + } + } + return dS.Dispatch(args, utils.MetaIPs, utils.IPsV1GetIPsForEvent, args, reply) +} + +func (dS *DispatcherService) IPsV1AuthorizeIPs(ctx *context.Context, args *utils.CGREvent, + reply *string) (err error) { + tnt := dS.cfg.GeneralCfg().DefaultTenant + if args != nil && args.Tenant != utils.EmptyString { + tnt = args.Tenant + } + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.IPsV1AuthorizeIPs, tnt, + utils.IfaceAsString(args.APIOpts[utils.OptsAPIKey]), args.Time); err != nil { + return + } + } + return dS.Dispatch(args, utils.MetaIPs, utils.IPsV1AuthorizeIPs, args, reply) +} + +func (dS *DispatcherService) IPsV1AllocateIPs(ctx *context.Context, args *utils.CGREvent, + reply *string) (err error) { + tnt := dS.cfg.GeneralCfg().DefaultTenant + if args != nil && args.Tenant != utils.EmptyString { + tnt = args.Tenant + } + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.IPsV1AllocateIPs, tnt, + utils.IfaceAsString(args.APIOpts[utils.OptsAPIKey]), args.Time); err != nil { + return + } + } + return dS.Dispatch(args, utils.MetaIPs, utils.IPsV1AllocateIPs, args, reply) +} + +func (dS *DispatcherService) IPsV1ReleaseIPs(ctx *context.Context, args *utils.CGREvent, + reply *string) (err error) { + tnt := dS.cfg.GeneralCfg().DefaultTenant + if args != nil && args.Tenant != utils.EmptyString { + tnt = args.Tenant + } + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.IPsV1ReleaseIPs, tnt, + utils.IfaceAsString(args.APIOpts[utils.OptsAPIKey]), args.Time); err != nil { + return + } + } + return dS.Dispatch(args, utils.MetaIPs, utils.IPsV1ReleaseIPs, args, reply) +} + +func (dS *DispatcherService) IPsV1GetIP(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *engine.IP) (err error) { + tnt := dS.cfg.GeneralCfg().DefaultTenant + if args.TenantID != nil && args.TenantID.Tenant != utils.EmptyString { + tnt = args.TenantID.Tenant + } + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.IPsV1GetIP, tnt, + utils.IfaceAsString(args.APIOpts[utils.OptsAPIKey]), utils.TimePointer(time.Now())); err != nil { + return + } + } + return dS.Dispatch(&utils.CGREvent{ + Tenant: tnt, + ID: args.ID, + APIOpts: args.APIOpts, + }, utils.MetaIPs, utils.IPsV1GetIP, args, reply) +} diff --git a/dispatchers/replicator.go b/dispatchers/replicator.go index 1b3225077..caf87b77f 100644 --- a/dispatchers/replicator.go +++ b/dispatchers/replicator.go @@ -234,6 +234,42 @@ func (dS *DispatcherService) ReplicatorSv1GetResourceProfile(ctx *context.Contex }, utils.MetaReplicator, utils.ReplicatorSv1GetResourceProfile, args, reply) } +func (dS *DispatcherService) ReplicatorSv1GetIP(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *engine.IP) (err error) { + tnt := dS.cfg.GeneralCfg().DefaultTenant + if args.TenantID != nil && args.TenantID.Tenant != utils.EmptyString { + tnt = args.TenantID.Tenant + } + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.ReplicatorSv1GetIP, tnt, + utils.IfaceAsString(args.APIOpts[utils.OptsAPIKey]), utils.TimePointer(time.Now())); err != nil { + return + } + } + return dS.Dispatch(&utils.CGREvent{ + Tenant: tnt, + ID: args.ID, + APIOpts: args.APIOpts, + }, utils.MetaReplicator, utils.ReplicatorSv1GetIP, args, reply) +} + +func (dS *DispatcherService) ReplicatorSv1GetIPProfile(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *engine.IPProfile) (err error) { + tnt := dS.cfg.GeneralCfg().DefaultTenant + if args.TenantID != nil && args.TenantID.Tenant != utils.EmptyString { + tnt = args.TenantID.Tenant + } + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.ReplicatorSv1GetIPProfile, tnt, + utils.IfaceAsString(args.APIOpts[utils.OptsAPIKey]), utils.TimePointer(time.Now())); err != nil { + return + } + } + return dS.Dispatch(&utils.CGREvent{ + Tenant: tnt, + ID: args.ID, + APIOpts: args.APIOpts, + }, utils.MetaReplicator, utils.ReplicatorSv1GetIPProfile, args, reply) +} + func (dS *DispatcherService) ReplicatorSv1GetActionTriggers(ctx *context.Context, args *utils.StringWithAPIOpts, rpl *engine.ActionTriggers) (err error) { if args == nil { args = new(utils.StringWithAPIOpts) @@ -680,6 +716,44 @@ func (dS *DispatcherService) ReplicatorSv1SetResourceProfile(ctx *context.Contex }, utils.MetaReplicator, utils.ReplicatorSv1SetResourceProfile, args, rpl) } +func (dS *DispatcherService) ReplicatorSv1SetIP(ctx *context.Context, args *engine.IPWithAPIOpts, rpl *string) (err error) { + if args == nil { + args = &engine.IPWithAPIOpts{ + IP: &engine.IP{}, + } + } + args.Tenant = utils.FirstNonEmpty(args.Tenant, dS.cfg.GeneralCfg().DefaultTenant) + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.ReplicatorSv1SetIP, args.Tenant, + utils.IfaceAsString(args.APIOpts[utils.OptsAPIKey]), utils.TimePointer(time.Now())); err != nil { + return + } + } + return dS.Dispatch(&utils.CGREvent{ + Tenant: args.Tenant, + APIOpts: args.APIOpts, + }, utils.MetaReplicator, utils.ReplicatorSv1SetIP, args, rpl) +} + +func (dS *DispatcherService) ReplicatorSv1SetIPProfile(ctx *context.Context, args *engine.IPProfileWithAPIOpts, rpl *string) (err error) { + if args == nil { + args = &engine.IPProfileWithAPIOpts{ + IPProfile: &engine.IPProfile{}, + } + } + args.Tenant = utils.FirstNonEmpty(args.Tenant, dS.cfg.GeneralCfg().DefaultTenant) + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.ReplicatorSv1SetIPProfile, args.Tenant, + utils.IfaceAsString(args.APIOpts[utils.OptsAPIKey]), utils.TimePointer(time.Now())); err != nil { + return + } + } + return dS.Dispatch(&utils.CGREvent{ + Tenant: args.Tenant, + APIOpts: args.APIOpts, + }, utils.MetaReplicator, utils.ReplicatorSv1SetIPProfile, args, rpl) +} + func (dS *DispatcherService) ReplicatorSv1SetActionTriggers(ctx *context.Context, args *engine.SetActionTriggersArgWithAPIOpts, rpl *string) (err error) { if args == nil { args = &engine.SetActionTriggersArgWithAPIOpts{} @@ -1095,6 +1169,44 @@ func (dS *DispatcherService) ReplicatorSv1RemoveResourceProfile(ctx *context.Con }, utils.MetaReplicator, utils.ReplicatorSv1RemoveResourceProfile, args, rpl) } +func (dS *DispatcherService) ReplicatorSv1RemoveIP(ctx *context.Context, args *utils.TenantIDWithAPIOpts, rpl *string) (err error) { + if args == nil { + args = &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{}, + } + } + args.Tenant = utils.FirstNonEmpty(args.Tenant, dS.cfg.GeneralCfg().DefaultTenant) + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.ReplicatorSv1RemoveIP, args.Tenant, + utils.IfaceAsString(args.APIOpts[utils.OptsAPIKey]), utils.TimePointer(time.Now())); err != nil { + return + } + } + return dS.Dispatch(&utils.CGREvent{ + Tenant: args.Tenant, + APIOpts: args.APIOpts, + }, utils.MetaReplicator, utils.ReplicatorSv1RemoveIP, args, rpl) +} + +func (dS *DispatcherService) ReplicatorSv1RemoveIPProfile(ctx *context.Context, args *utils.TenantIDWithAPIOpts, rpl *string) (err error) { + if args == nil { + args = &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{}, + } + } + args.Tenant = utils.FirstNonEmpty(args.Tenant, dS.cfg.GeneralCfg().DefaultTenant) + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.ReplicatorSv1RemoveIPProfile, args.Tenant, + utils.IfaceAsString(args.APIOpts[utils.OptsAPIKey]), utils.TimePointer(time.Now())); err != nil { + return + } + } + return dS.Dispatch(&utils.CGREvent{ + Tenant: args.Tenant, + APIOpts: args.APIOpts, + }, utils.MetaReplicator, utils.ReplicatorSv1RemoveIPProfile, args, rpl) +} + func (dS *DispatcherService) ReplicatorSv1RemoveActionTriggers(ctx *context.Context, args *utils.StringWithAPIOpts, rpl *string) (err error) { if args == nil { args = new(utils.StringWithAPIOpts) diff --git a/engine/caches.go b/engine/caches.go index 2e29d4d6b..8cc7285f6 100644 --- a/engine/caches.go +++ b/engine/caches.go @@ -54,6 +54,12 @@ func init() { gob.Register(new(ResourceProfileWithAPIOpts)) gob.Register(new(ResourceWithAPIOpts)) gob.Register(new(utils.TPResourceProfile)) + // IPs + gob.Register(new(IP)) + gob.Register(new(IPProfile)) + gob.Register(new(IPProfileWithAPIOpts)) + gob.Register(new(IPWithAPIOpts)) + gob.Register(new(utils.TPIPProfile)) // StatS gob.Register(new(StatQueue)) gob.Register(new(StatQueueProfile)) diff --git a/engine/datadbmock.go b/engine/datadbmock.go index faf84b854..23ae36526 100644 --- a/engine/datadbmock.go +++ b/engine/datadbmock.go @@ -35,6 +35,10 @@ type DataDBMock struct { SetResourceProfileDrvF func(rp *ResourceProfile) error RemoveResourceProfileDrvF func(tnt, id string) error SetResourceDrvF func(r *Resource) error + GetIPProfileDrvF func(tnt, id string) (*IPProfile, error) + SetIPProfileDrvF func(ipp *IPProfile) error + RemoveIPProfileDrvF func(tnt, id string) error + SetIPDrvF func(ip *IP) error GetStatQueueProfileDrvF func(tenant, id string) (sq *StatQueueProfile, err error) SetStatQueueProfileDrvF func(sq *StatQueueProfile) (err error) RemStatQueueProfileDrvF func(tenant, id string) (err error) @@ -279,6 +283,42 @@ func (dbM *DataDBMock) RemoveResourceDrv(string, string) error { return utils.ErrNotImplemented } +func (dbM *DataDBMock) GetIPProfileDrv(tnt, id string) (*IPProfile, error) { + if dbM.GetIPProfileDrvF != nil { + return dbM.GetIPProfileDrvF(tnt, id) + } + return nil, utils.ErrNotImplemented +} + +func (dbM *DataDBMock) SetIPProfileDrv(ipp *IPProfile) error { + if dbM.SetIPProfileDrvF != nil { + return dbM.SetIPProfileDrvF(ipp) + } + return utils.ErrNotImplemented +} + +func (dbM *DataDBMock) RemoveIPProfileDrv(tnt, id string) error { + if dbM.RemoveIPProfileDrvF != nil { + return dbM.RemoveIPProfileDrvF(tnt, id) + } + return utils.ErrNotImplemented +} + +func (dbM *DataDBMock) GetIPDrv(string, string) (*IP, error) { + return nil, utils.ErrNotImplemented +} + +func (dbM *DataDBMock) SetIPDrv(ip *IP) error { + if dbM.SetIPDrvF != nil { + return dbM.SetIPDrvF(ip) + } + return utils.ErrNotImplemented +} + +func (dbM *DataDBMock) RemoveIPDrv(string, string) error { + return utils.ErrNotImplemented +} + func (dbM *DataDBMock) GetTimingDrv(string) (*utils.TPTiming, error) { return nil, utils.ErrNotImplemented } diff --git a/engine/datamanager.go b/engine/datamanager.go index 3a42613e1..824242c14 100644 --- a/engine/datamanager.go +++ b/engine/datamanager.go @@ -34,6 +34,7 @@ var ( filterIndexesPrefixMap = utils.StringSet{ utils.AttributeFilterIndexes: {}, utils.ResourceFilterIndexes: {}, + utils.IPFilterIndexes: {}, utils.StatFilterIndexes: {}, utils.ThresholdFilterIndexes: {}, utils.RouteFilterIndexes: {}, @@ -53,8 +54,10 @@ var ( utils.ActionTriggerPrefix: {}, utils.SharedGroupPrefix: {}, utils.ResourceProfilesPrefix: {}, + utils.IPProfilesPrefix: {}, utils.TimingsPrefix: {}, utils.ResourcesPrefix: {}, + utils.IPsPrefix: {}, utils.StatQueuePrefix: {}, utils.StatQueueProfilePrefix: {}, utils.ThresholdPrefix: {}, @@ -70,6 +73,7 @@ var ( utils.MetaDispatchers: {}, // not realy a prefix as this is not stored in DB utils.AttributeFilterIndexes: {}, utils.ResourceFilterIndexes: {}, + utils.IPFilterIndexes: {}, utils.StatFilterIndexes: {}, utils.ThresholdFilterIndexes: {}, utils.RouteFilterIndexes: {}, @@ -188,6 +192,16 @@ func (dm *DataManager) CacheDataFromDB(prfx string, ids []string, mustBeCached b lkID := guardian.Guardian.GuardIDs("", config.CgrConfig().GeneralCfg().LockingTimeout, resourceLockKey(tntID.Tenant, tntID.ID)) _, err = dm.GetResource(tntID.Tenant, tntID.ID, false, true, utils.NonTransactional) guardian.Guardian.UnguardIDs(lkID) + case utils.IPProfilesPrefix: + tntID := utils.NewTenantID(dataID) + lkID := guardian.Guardian.GuardIDs("", config.CgrConfig().GeneralCfg().LockingTimeout, ipProfileLockKey(tntID.Tenant, tntID.ID)) + _, err = dm.GetIPProfile(tntID.Tenant, tntID.ID, false, true, utils.NonTransactional) + guardian.Guardian.UnguardIDs(lkID) + case utils.IPsPrefix: + tntID := utils.NewTenantID(dataID) + lkID := guardian.Guardian.GuardIDs("", config.CgrConfig().GeneralCfg().LockingTimeout, ipLockKey(tntID.Tenant, tntID.ID)) + _, err = dm.GetIP(tntID.Tenant, tntID.ID, false, true, utils.NonTransactional) + guardian.Guardian.UnguardIDs(lkID) case utils.StatQueueProfilePrefix: tntID := utils.NewTenantID(dataID) lkID := guardian.Guardian.GuardIDs("", config.CgrConfig().GeneralCfg().LockingTimeout, statQueueProfileLockKey(tntID.Tenant, tntID.ID)) @@ -2081,6 +2095,236 @@ func (dm *DataManager) RemoveResourceProfile(tenant, id string, withIndex bool) return dm.RemoveResource(tenant, id) } +func (dm *DataManager) GetIP(tenant, id string, cacheRead, cacheWrite bool, + transactionID string) (rs *IP, err error) { + tntID := utils.ConcatenatedKey(tenant, id) + if cacheRead { + if x, ok := Cache.Get(utils.CacheIPs, tntID); ok { + if x == nil { + return nil, utils.ErrNotFound + } + return x.(*IP), nil + } + } + if dm == nil { + err = utils.ErrNoDatabaseConn + return + } + rs, err = dm.dataDB.GetIPDrv(tenant, id) + if err != nil { + if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaIPs]; err == utils.ErrNotFound && itm.Remote { + if err = dm.connMgr.Call(context.TODO(), config.CgrConfig().DataDbCfg().RmtConns, + utils.ReplicatorSv1GetIP, + &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: tenant, ID: id}, + APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, + utils.FirstNonEmpty(config.CgrConfig().DataDbCfg().RmtConnID, + config.CgrConfig().GeneralCfg().NodeID)), + }, &rs); err == nil { + err = dm.dataDB.SetIPDrv(rs) + } + } + if err != nil { + err = utils.CastRPCErr(err) + if err == utils.ErrNotFound && cacheWrite { + if errCh := Cache.Set(utils.CacheIPs, tntID, nil, nil, + cacheCommit(transactionID), transactionID); errCh != nil { + return nil, errCh + } + + } + return nil, err + } + } + if cacheWrite { + if errCh := Cache.Set(utils.CacheIPs, tntID, rs, nil, + cacheCommit(transactionID), transactionID); errCh != nil { + return nil, errCh + } + } + return +} + +func (dm *DataManager) SetIP(rs *IP) (err error) { + if dm == nil { + return utils.ErrNoDatabaseConn + } + if err = dm.DataDB().SetIPDrv(rs); err != nil { + return + } + itm := config.CgrConfig().DataDbCfg().Items[utils.MetaIPs] + return dm.replicator.replicate( + utils.IPsPrefix, rs.TenantID(), // these are used to get the host IDs from cache + utils.ReplicatorSv1SetIP, + &IPWithAPIOpts{ + IP: rs, + APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, + config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString), + }, itm) +} + +func (dm *DataManager) RemoveIP(tenant, id string) (err error) { + if dm == nil { + return utils.ErrNoDatabaseConn + } + if err = dm.DataDB().RemoveIPDrv(tenant, id); err != nil { + return + } + itm := config.CgrConfig().DataDbCfg().Items[utils.MetaIPs] + _ = dm.replicator.replicate( + utils.IPsPrefix, utils.ConcatenatedKey(tenant, id), // these are used to get the host IDs from cache + utils.ReplicatorSv1RemoveIP, + &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: tenant, ID: id}, + APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, + config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString), + }, itm) + return +} + +func (dm *DataManager) GetIPProfile(tenant, id string, cacheRead, cacheWrite bool, + transactionID string) (ipp *IPProfile, err error) { + tntID := utils.ConcatenatedKey(tenant, id) + if cacheRead { + if x, ok := Cache.Get(utils.CacheIPProfiles, tntID); ok { + if x == nil { + return nil, utils.ErrNotFound + } + return x.(*IPProfile), nil + } + } + if dm == nil { + err = utils.ErrNoDatabaseConn + return + } + ipp, err = dm.dataDB.GetIPProfileDrv(tenant, id) + if err != nil { + if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaIPProfiles]; err == utils.ErrNotFound && itm.Remote { + if err = dm.connMgr.Call(context.TODO(), config.CgrConfig().DataDbCfg().RmtConns, + utils.ReplicatorSv1GetIPProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: tenant, ID: id}, + APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, + utils.FirstNonEmpty(config.CgrConfig().DataDbCfg().RmtConnID, + config.CgrConfig().GeneralCfg().NodeID)), + }, &ipp); err == nil { + err = dm.dataDB.SetIPProfileDrv(ipp) + } + } + if err != nil { + err = utils.CastRPCErr(err) + if err == utils.ErrNotFound && cacheWrite { + if errCh := Cache.Set(utils.CacheIPProfiles, tntID, nil, nil, + cacheCommit(transactionID), transactionID); errCh != nil { + return nil, errCh + } + + } + return nil, err + } + } + if cacheWrite { + if errCh := Cache.Set(utils.CacheIPProfiles, tntID, ipp, nil, + cacheCommit(transactionID), transactionID); errCh != nil { + return nil, errCh + } + } + return +} + +func (dm *DataManager) SetIPProfile(ipp *IPProfile, withIndex bool) (err error) { + if dm == nil { + return utils.ErrNoDatabaseConn + } + if withIndex { + if err = dm.checkFilters(ipp.Tenant, ipp.FilterIDs); err != nil { + // if we get a broken filter do not set the profile + return fmt.Errorf("%+s for item with ID: %+v", + err, ipp.TenantID()) + } + } + oldIP, err := dm.GetIPProfile(ipp.Tenant, ipp.ID, true, false, utils.NonTransactional) + if err != nil && err != utils.ErrNotFound { + return err + } + if err = dm.DataDB().SetIPProfileDrv(ipp); err != nil { + return err + } + if withIndex { + var oldFiltersIDs *[]string + if oldIP != nil { + oldFiltersIDs = &oldIP.FilterIDs + } + if err := updatedIndexes(dm, utils.CacheIPFilterIndexes, ipp.Tenant, + utils.EmptyString, ipp.ID, oldFiltersIDs, ipp.FilterIDs, false); err != nil { + return err + } + Cache.Clear([]string{utils.CacheEventIPs}) + } + itm := config.CgrConfig().DataDbCfg().Items[utils.MetaIPProfiles] + if err = dm.replicator.replicate( + utils.IPProfilesPrefix, ipp.TenantID(), // these are used to get the host IDs from cache + utils.ReplicatorSv1SetIPProfile, + &IPProfileWithAPIOpts{ + IPProfile: ipp, + APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, + config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString), + }, itm); err != nil { + return + } + if oldIP == nil || // create the IP if it didn't exist before + oldIP.TTL != ipp.TTL || + (oldIP.Stored != ipp.Stored && oldIP.Stored) { // reset the IP the profile changed these fields + err = dm.SetIP(&IP{ + Tenant: ipp.Tenant, + ID: ipp.ID, + Usages: make(map[string]*IPUsage), + }) + } else if _, errGet := dm.GetIP(ipp.Tenant, ipp.ID, // do not try to get the IP if the configuration changed + true, false, utils.NonTransactional); errGet == utils.ErrNotFound { // the IP does not exist + err = dm.SetIP(&IP{ + Tenant: ipp.Tenant, + ID: ipp.ID, + Usages: make(map[string]*IPUsage), + }) + } + return +} + +func (dm *DataManager) RemoveIPProfile(tenant, id string, withIndex bool) (err error) { + if dm == nil { + return utils.ErrNoDatabaseConn + } + oldRes, err := dm.GetIPProfile(tenant, id, true, false, utils.NonTransactional) + if err != nil && err != utils.ErrNotFound { + return err + } + if err = dm.DataDB().RemoveIPProfileDrv(tenant, id); err != nil { + return + } + if oldRes == nil { + return utils.ErrNotFound + } + if withIndex { + if err = removeIndexFiltersItem(dm, utils.CacheIPFilterIndexes, tenant, id, oldRes.FilterIDs); err != nil { + return + } + if err = removeItemFromFilterIndex(dm, utils.CacheIPFilterIndexes, + tenant, utils.EmptyString, id, oldRes.FilterIDs); err != nil { + return + } + } + itm := config.CgrConfig().DataDbCfg().Items[utils.MetaIPProfiles] + _ = dm.replicator.replicate( + utils.IPProfilesPrefix, utils.ConcatenatedKey(tenant, id), // these are used to get the host IDs from cache + utils.ReplicatorSv1RemoveIPProfile, + &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: tenant, ID: id}, + APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, + config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString), + }, itm) + return dm.RemoveIP(tenant, id) +} + func (dm *DataManager) GetActionTriggers(id string, skipCache bool, transactionID string) (attrs ActionTriggers, err error) { if !skipCache { diff --git a/engine/ips.go b/engine/ips.go new file mode 100644 index 000000000..8dd8547b3 --- /dev/null +++ b/engine/ips.go @@ -0,0 +1,853 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package engine + +import ( + "fmt" + "runtime" + "slices" + "sort" + "sync" + "time" + + "github.com/cgrates/birpc/context" + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/guardian" + "github.com/cgrates/cgrates/utils" +) + +// IPProfile defines the configuration of the IP. +type IPProfile struct { + Tenant string + ID string + FilterIDs []string + ActivationInterval *utils.ActivationInterval + TTL time.Duration + Type string + AddressPool string + Allocation string + Stored bool + Weight float64 + + lkID string +} + +// Clone creates a deep copy of IPProfile for thread-safe use. +func (ip *IPProfile) Clone() *IPProfile { + if ip == nil { + return nil + } + return &IPProfile{ + Tenant: ip.Tenant, + ID: ip.ID, + FilterIDs: slices.Clone(ip.FilterIDs), + ActivationInterval: ip.ActivationInterval.Clone(), + TTL: ip.TTL, + Type: ip.Type, + AddressPool: ip.AddressPool, + Allocation: ip.Allocation, + Stored: ip.Stored, + Weight: ip.Weight, + } +} + +// CacheClone returns a clone of IPProfile used by ltcache CacheCloner. +func (ip *IPProfile) CacheClone() any { + return ip.Clone() +} + +// IPProfileWithAPIOpts wraps IPProfile with APIOpts. +type IPProfileWithAPIOpts struct { + *IPProfile + APIOpts map[string]any +} + +// TenantID returns the concatenated tenant and ID. +func (ip *IPProfile) TenantID() string { + return utils.ConcatenatedKey(ip.Tenant, ip.ID) +} + +// ipProfileLockKey returns the ID used to lock an IPProfile with guardian. +func ipProfileLockKey(tnt, id string) string { + return utils.ConcatenatedKey(utils.CacheIPProfiles, tnt, id) +} + +// lock will lock the IPProfile using guardian and store the lock within lkID. +// If lkID is provided as an argument, it assumes the lock is already acquired. +func (ip *IPProfile) lock(lkID string) { + if lkID == utils.EmptyString { + lkID = guardian.Guardian.GuardIDs("", + config.CgrConfig().GeneralCfg().LockingTimeout, + ipProfileLockKey(ip.Tenant, ip.ID)) + } + ip.lkID = lkID +} + +// unlock releases the lock on the IPProfile and clears the lock ID. +func (ip *IPProfile) unlock() { + if ip.lkID == utils.EmptyString { + return + } + guardian.Guardian.UnguardIDs(ip.lkID) + ip.lkID = utils.EmptyString +} + +// IPUsage represents an usage counted. +type IPUsage struct { + Tenant string + ID string + ExpiryTime time.Time + Units float64 +} + +// TenantID returns the concatenated key between tenant and ID +func (u *IPUsage) TenantID() string { + return utils.ConcatenatedKey(u.Tenant, u.ID) +} + +// isActive checks ExpiryTime at some time +func (u *IPUsage) isActive(atTime time.Time) bool { + return u.ExpiryTime.IsZero() || u.ExpiryTime.Sub(atTime) > 0 +} + +// Clone duplicates ru +func (u *IPUsage) Clone() *IPUsage { + if u == nil { + return nil + } + clone := *u + return &clone +} + +// IP represents ... +type IP struct { + Tenant string + ID string + Usages map[string]*IPUsage + TTLIdx []string + lkID string + ttl *time.Duration + tUsage *float64 + dirty *bool + cfg *IPProfile +} + +// Clone clones *IP (lkID excluded) +func (ip *IP) Clone() *IP { + if ip == nil { + return nil + } + clone := &IP{ + Tenant: ip.Tenant, + ID: ip.ID, + TTLIdx: slices.Clone(ip.TTLIdx), + cfg: ip.cfg.Clone(), + } + if ip.Usages != nil { + clone.Usages = make(map[string]*IPUsage, len(ip.Usages)) + for key, usage := range ip.Usages { + clone.Usages[key] = usage.Clone() + } + } + if ip.ttl != nil { + ttlCopy := *ip.ttl + clone.ttl = &ttlCopy + } + if ip.tUsage != nil { + tUsageCopy := *ip.tUsage + clone.tUsage = &tUsageCopy + } + if ip.dirty != nil { + dirtyCopy := *ip.dirty + clone.dirty = &dirtyCopy + } + return clone +} + +// CacheClone returns a clone of IP used by ltcache CacheCloner +func (ip *IP) CacheClone() any { + return ip.Clone() +} + +// ipLockKey returns the ID used to lock a ip with guardian +func ipLockKey(tnt, id string) string { + return utils.ConcatenatedKey(utils.CacheIPs, tnt, id) +} + +// lock will lock the ip using guardian and store the lock within r.lkID +// if lkID is passed as argument, the lock is considered as executed +func (ip *IP) lock(lkID string) { + if lkID == utils.EmptyString { + lkID = guardian.Guardian.GuardIDs("", + config.CgrConfig().GeneralCfg().LockingTimeout, + ipLockKey(ip.Tenant, ip.ID)) + } + ip.lkID = lkID +} + +// unlock will unlock the ip and clear r.lkID +func (ip *IP) unlock() { + if ip.lkID == utils.EmptyString { + return + } + guardian.Guardian.UnguardIDs(ip.lkID) + ip.lkID = utils.EmptyString +} + +type IPWithAPIOpts struct { + *IP + APIOpts map[string]any +} + +// TenantID returns the unique ID in a multi-tenant environment +func (ip *IP) TenantID() string { + return utils.ConcatenatedKey(ip.Tenant, ip.ID) +} + +// removeExpiredUnits removes units which are expired from the ip +func (ip *IP) removeExpiredUnits() { + var firstActive int + for _, rID := range ip.TTLIdx { + if r, has := ip.Usages[rID]; has && r.isActive(time.Now()) { + break + } + firstActive++ + } + if firstActive == 0 { + return + } + for _, rID := range ip.TTLIdx[:firstActive] { + ru, has := ip.Usages[rID] + if !has { + continue + } + delete(ip.Usages, rID) + if ip.tUsage != nil { // total usage was not yet calculated so we do not need to update it + *ip.tUsage -= ru.Units + if *ip.tUsage < 0 { // something went wrong + utils.Logger.Warning( + fmt.Sprintf("resetting total usage for ipID: %s, usage smaller than 0: %f", ip.ID, *ip.tUsage)) + ip.tUsage = nil + } + } + } + ip.TTLIdx = ip.TTLIdx[firstActive:] + ip.tUsage = nil +} + +// TotalUsage returns the sum of all usage units. +func (ip *IP) TotalUsage() float64 { + if ip.tUsage == nil { + var tu float64 + for _, ru := range ip.Usages { + tu += ru.Units + } + ip.tUsage = &tu + } + if ip.tUsage == nil { + return 0 + } + return *ip.tUsage +} + +// recordUsage records a new usage +func (ip *IP) recordUsage(ru *IPUsage) (err error) { + if _, hasID := ip.Usages[ru.ID]; hasID { + return fmt.Errorf("duplicate ip usage with id: %s", ru.TenantID()) + } + if ip.ttl != nil && *ip.ttl != -1 { + if *ip.ttl == 0 { + return // no recording for ttl of 0 + } + ru = ru.Clone() // don't influence the initial ru + ru.ExpiryTime = time.Now().Add(*ip.ttl) + } + ip.Usages[ru.ID] = ru + if ip.tUsage != nil { + *ip.tUsage += ru.Units + } + if !ru.ExpiryTime.IsZero() { + ip.TTLIdx = append(ip.TTLIdx, ru.ID) + } + return +} + +// clearUsage clears the usage for an ID +func (ip *IP) clearUsage(ruID string) (err error) { + ru, hasIt := ip.Usages[ruID] + if !hasIt { + return fmt.Errorf("cannot find usage record with id: %s", ruID) + } + if !ru.ExpiryTime.IsZero() { + for i, ruIDIdx := range ip.TTLIdx { + if ruIDIdx == ruID { + ip.TTLIdx = slices.Delete(ip.TTLIdx, i, i+1) + break + } + } + } + if ip.tUsage != nil { + *ip.tUsage -= ru.Units + } + delete(ip.Usages, ruID) + return +} + +// IPs is an orderable list of IPs based on Weight +type IPs []*IP + +// Sort sorts based on Weight +func (ips IPs) Sort() { + sort.Slice(ips, func(i, j int) bool { return ips[i].cfg.Weight > ips[j].cfg.Weight }) +} + +// unlock will unlock ips part of this slice +func (ips IPs) unlock() { + for _, ip := range ips { + ip.unlock() + if ip.cfg != nil { + ip.cfg.unlock() + } + } +} + +// ids returns a map of ip IDs which is used for caching +func (ips IPs) ids() utils.StringSet { + mp := make(utils.StringSet, len(ips)) + for _, ip := range ips { + mp.Add(ip.ID) + } + return mp +} + +// NewIPService returns a new IPService +func NewIPService(dm *DataManager, cgrcfg *config.CGRConfig, + filterS *FilterS, connMgr *ConnManager) *IPService { + return &IPService{dm: dm, + storedIPs: make(utils.StringSet), + cfg: cgrcfg, + fs: filterS, + loopStopped: make(chan struct{}), + stopBackup: make(chan struct{}), + cm: connMgr, + } + +} + +// IPService is the service handling ips +type IPService struct { + cfg *config.CGRConfig + cm *ConnManager + dm *DataManager + fs *FilterS + storedIPsMux sync.RWMutex // protects storedIPs + storedIPs utils.StringSet // keep a record of ips which need saving, map[ipID]bool + stopBackup chan struct{} // control storing process + loopStopped chan struct{} +} + +// Reload stops the backupLoop and restarts it +func (rS *IPService) Reload() { + close(rS.stopBackup) + <-rS.loopStopped // wait until the loop is done + rS.stopBackup = make(chan struct{}) + go rS.runBackup() +} + +// StartLoop starts the gorutine with the backup loop +func (rS *IPService) StartLoop() { + go rS.runBackup() +} + +// Shutdown is called to shutdown the service +func (rS *IPService) Shutdown() { + utils.Logger.Info(" service shutdown initialized") + close(rS.stopBackup) + rS.storeIPs() + utils.Logger.Info(" service shutdown complete") +} + +// backup will regularly store ips changed to dataDB +func (rS *IPService) runBackup() { + storeInterval := rS.cfg.IPsCfg().StoreInterval + if storeInterval <= 0 { + rS.loopStopped <- struct{}{} + return + } + for { + rS.storeIPs() + select { + case <-rS.stopBackup: + rS.loopStopped <- struct{}{} + return + case <-time.After(storeInterval): + } + } +} + +// storeIPs represents one task of complete backup +func (rS *IPService) storeIPs() { + var failedRIDs []string + for { // don't stop until we store all dirty ips + rS.storedIPsMux.Lock() + rID := rS.storedIPs.GetOne() + if rID != "" { + rS.storedIPs.Remove(rID) + } + rS.storedIPsMux.Unlock() + if rID == "" { + break // no more keys, backup completed + } + rIf, ok := Cache.Get(utils.CacheIPs, rID) + if !ok || rIf == nil { + utils.Logger.Warning(fmt.Sprintf("<%s> failed retrieving from cache ip with ID: %s", utils.IPs, rID)) + continue + } + r := rIf.(*IP) + r.lock(utils.EmptyString) + if err := rS.storeIP(r); err != nil { + failedRIDs = append(failedRIDs, rID) // record failure so we can schedule it for next backup + } + r.unlock() + // randomize the CPU load and give up thread control + runtime.Gosched() + } + if len(failedRIDs) != 0 { // there were errors on save, schedule the keys for next backup + rS.storedIPsMux.Lock() + rS.storedIPs.AddSlice(failedRIDs) + rS.storedIPsMux.Unlock() + } +} + +// StoreIP stores the ip in DB and corrects dirty flag +func (rS *IPService) storeIP(r *IP) (err error) { + if r.dirty == nil || !*r.dirty { + return + } + if err = rS.dm.SetIP(r); err != nil { + utils.Logger.Warning( + fmt.Sprintf(" failed saving IP with ID: %s, error: %s", + r.ID, err.Error())) + return + } + //since we no longer handle cache in DataManager do here a manual caching + if tntID := r.TenantID(); Cache.HasItem(utils.CacheIPs, tntID) { // only cache if previously there + if err = Cache.Set(utils.CacheIPs, tntID, r, nil, + true, utils.NonTransactional); err != nil { + utils.Logger.Warning( + fmt.Sprintf(" failed caching IP with ID: %s, error: %s", + tntID, err.Error())) + return + } + } + *r.dirty = false + return +} + +// storeMatchedIPs will store the list of ips based on the StoreInterval +func (s *IPService) storeMatchedIPs(matchedIPs IPs) (err error) { + if s.cfg.IPsCfg().StoreInterval == 0 { + return + } + if s.cfg.IPsCfg().StoreInterval > 0 { + s.storedIPsMux.Lock() + defer s.storedIPsMux.Unlock() + } + for _, ip := range matchedIPs { + if ip.dirty != nil { + *ip.dirty = true // mark it to be saved + if s.cfg.IPsCfg().StoreInterval > 0 { + s.storedIPs.Add(ip.TenantID()) + continue + } + if err = s.storeIP(ip); err != nil { + return + } + } + + } + return +} + +// matchingIPsForEvent returns ordered list of matching ips which are active by the time of the call +func (s *IPService) matchingIPsForEvent(tnt string, ev *utils.CGREvent, + evUUID string, usageTTL *time.Duration) (ips IPs, err error) { + var ipIDs utils.StringSet + evNm := utils.MapStorage{ + utils.MetaReq: ev.Event, + utils.MetaOpts: ev.APIOpts, + } + if x, ok := Cache.Get(utils.CacheEventIPs, evUUID); ok { // The IPIDs were cached as utils.StringSet{"ipID":bool} + if x == nil { + return nil, utils.ErrNotFound + } + ipIDs = x.(utils.StringSet) + defer func() { // make sure we uncache if we find errors + if err != nil { + if errCh := Cache.Remove(utils.CacheEventIPs, evUUID, + cacheCommit(utils.NonTransactional), utils.NonTransactional); errCh != nil { + err = errCh + } + } + }() + + } else { // select the ipIDs out of dataDB + ipIDs, err = MatchingItemIDsForEvent(evNm, + s.cfg.IPsCfg().StringIndexedFields, + s.cfg.IPsCfg().PrefixIndexedFields, + s.cfg.IPsCfg().SuffixIndexedFields, + s.cfg.IPsCfg().ExistsIndexedFields, + s.dm, utils.CacheIPFilterIndexes, tnt, + s.cfg.IPsCfg().IndexedSelects, + s.cfg.IPsCfg().NestedFields, + ) + if err != nil { + if err == utils.ErrNotFound { + if errCh := Cache.Set(utils.CacheEventIPs, evUUID, nil, nil, true, ""); errCh != nil { // cache negative match + return nil, errCh + } + } + return + } + } + ips = make(IPs, 0, len(ipIDs)) + for id := range ipIDs { + lkPrflID := guardian.Guardian.GuardIDs("", + config.CgrConfig().GeneralCfg().LockingTimeout, + ipProfileLockKey(tnt, id)) + var profile *IPProfile + if profile, err = s.dm.GetIPProfile(tnt, id, + true, true, utils.NonTransactional); err != nil { + guardian.Guardian.UnguardIDs(lkPrflID) + if err == utils.ErrNotFound { + continue + } + ips.unlock() + return + } + profile.lock(lkPrflID) + if profile.ActivationInterval != nil && ev.Time != nil && + !profile.ActivationInterval.IsActiveAtTime(*ev.Time) { // not active + profile.unlock() + continue + } + var pass bool + if pass, err = s.fs.Pass(tnt, profile.FilterIDs, + evNm); err != nil { + profile.unlock() + ips.unlock() + return nil, err + } else if !pass { + profile.unlock() + continue + } + lkID := guardian.Guardian.GuardIDs(utils.EmptyString, + config.CgrConfig().GeneralCfg().LockingTimeout, + ipLockKey(profile.Tenant, profile.ID)) + var ip *IP + if ip, err = s.dm.GetIP(profile.Tenant, profile.ID, true, true, ""); err != nil { + guardian.Guardian.UnguardIDs(lkID) + profile.unlock() + ips.unlock() + return nil, err + } + ip.lock(lkID) // pass the lock into ip so we have it as reference + if profile.Stored && ip.dirty == nil { + ip.dirty = utils.BoolPointer(false) + } + if usageTTL != nil { + if *usageTTL != 0 { + ip.ttl = usageTTL + } + } else if profile.TTL >= 0 { + ip.ttl = utils.DurationPointer(profile.TTL) + } + ip.cfg = profile + ips = append(ips, ip) + } + + if len(ips) == 0 { + return nil, utils.ErrNotFound + } + ips.Sort() + if err = Cache.Set(utils.CacheEventIPs, evUUID, ips.ids(), nil, true, ""); err != nil { + ips.unlock() + } + return +} + +// V1GetIPsForEvent returns active ip configs matching the event +func (s *IPService) V1GetIPsForEvent(ctx *context.Context, args *utils.CGREvent, reply *IPs) (err error) { + if args == nil { + return utils.NewErrMandatoryIeMissing(utils.Event) + } + if missing := utils.MissingStructFields(args, []string{utils.ID, utils.Event}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + usageID := utils.GetStringOpts(args, s.cfg.IPsCfg().Opts.UsageID, utils.OptsIPsUsageID) + if usageID == utils.EmptyString { + return utils.NewErrMandatoryIeMissing(utils.UsageID) + } + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = s.cfg.GeneralCfg().DefaultTenant + } + + // RPC caching + if config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses].Limit != 0 { + cacheKey := utils.ConcatenatedKey(utils.IPsV1GetIPsForEvent, utils.ConcatenatedKey(tnt, args.ID)) + refID := guardian.Guardian.GuardIDs("", + config.CgrConfig().GeneralCfg().LockingTimeout, cacheKey) // RPC caching needs to be atomic + defer guardian.Guardian.UnguardIDs(refID) + if itm, has := Cache.Get(utils.CacheRPCResponses, cacheKey); has { + cachedResp := itm.(*utils.CachedRPCResponse) + if cachedResp.Error == nil { + *reply = *cachedResp.Result.(*IPs) + } + return cachedResp.Error + } + defer Cache.Set(utils.CacheRPCResponses, cacheKey, + &utils.CachedRPCResponse{Result: reply, Error: err}, + nil, true, utils.NonTransactional) + } + // end of RPC caching + + var ttl *time.Duration + if ttl, err = utils.GetDurationPointerOpts(args, s.cfg.IPsCfg().Opts.TTL, + utils.OptsIPsTTL); err != nil { + return + } + var ips IPs + if ips, err = s.matchingIPsForEvent(tnt, args, usageID, ttl); err != nil { + return err + } + defer ips.unlock() + *reply = ips + return +} + +// V1AuthorizeIPs queries service to find if an Usage is allowed. +func (s *IPService) V1AuthorizeIPs(ctx *context.Context, args *utils.CGREvent, reply *string) (err error) { + if args == nil { + return utils.NewErrMandatoryIeMissing(utils.Event) + } + if missing := utils.MissingStructFields(args, []string{utils.ID, utils.Event}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + usageID := utils.GetStringOpts(args, s.cfg.IPsCfg().Opts.UsageID, utils.OptsIPsUsageID) + if usageID == utils.EmptyString { + return utils.NewErrMandatoryIeMissing(utils.UsageID) + } + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = s.cfg.GeneralCfg().DefaultTenant + } + + // RPC caching + if config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses].Limit != 0 { + cacheKey := utils.ConcatenatedKey(utils.IPsV1AuthorizeIPs, utils.ConcatenatedKey(tnt, args.ID)) + refID := guardian.Guardian.GuardIDs("", + config.CgrConfig().GeneralCfg().LockingTimeout, cacheKey) // RPC caching needs to be atomic + defer guardian.Guardian.UnguardIDs(refID) + if itm, has := Cache.Get(utils.CacheRPCResponses, cacheKey); has { + cachedResp := itm.(*utils.CachedRPCResponse) + if cachedResp.Error == nil { + *reply = *cachedResp.Result.(*string) + } + return cachedResp.Error + } + defer Cache.Set(utils.CacheRPCResponses, cacheKey, + &utils.CachedRPCResponse{Result: reply, Error: err}, + nil, true, utils.NonTransactional) + } + // end of RPC caching + + var ttl *time.Duration + if ttl, err = utils.GetDurationPointerOpts(args, s.cfg.IPsCfg().Opts.TTL, + utils.OptsIPsTTL); err != nil { + return + } + var ips IPs + if ips, err = s.matchingIPsForEvent(tnt, args, usageID, ttl); err != nil { + return err + } + defer ips.unlock() + + if _, err = utils.GetFloat64Opts(args, s.cfg.IPsCfg().Opts.Units, + utils.OptsIPsUnits); err != nil { + return + } + + /* + authorize logic + ... + */ + + *reply = utils.OK + return +} + +// V1AllocateIPs is called when a ip requires allocation. +func (s *IPService) V1AllocateIPs(ctx *context.Context, args *utils.CGREvent, reply *string) (err error) { + if args == nil { + return utils.NewErrMandatoryIeMissing(utils.Event) + } + if missing := utils.MissingStructFields(args, []string{utils.ID, utils.Event}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + usageID := utils.GetStringOpts(args, s.cfg.IPsCfg().Opts.UsageID, utils.OptsIPsUsageID) + if usageID == utils.EmptyString { + return utils.NewErrMandatoryIeMissing(utils.UsageID) + } + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = s.cfg.GeneralCfg().DefaultTenant + } + + // RPC caching + if config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses].Limit != 0 { + cacheKey := utils.ConcatenatedKey(utils.IPsV1AllocateIPs, utils.ConcatenatedKey(tnt, args.ID)) + refID := guardian.Guardian.GuardIDs("", + config.CgrConfig().GeneralCfg().LockingTimeout, cacheKey) // RPC caching needs to be atomic + defer guardian.Guardian.UnguardIDs(refID) + if itm, has := Cache.Get(utils.CacheRPCResponses, cacheKey); has { + cachedResp := itm.(*utils.CachedRPCResponse) + if cachedResp.Error == nil { + *reply = *cachedResp.Result.(*string) + } + return cachedResp.Error + } + defer Cache.Set(utils.CacheRPCResponses, cacheKey, + &utils.CachedRPCResponse{Result: reply, Error: err}, + nil, true, utils.NonTransactional) + } + // end of RPC caching + + var ttl *time.Duration + if ttl, err = utils.GetDurationPointerOpts(args, s.cfg.IPsCfg().Opts.TTL, + utils.OptsIPsTTL); err != nil { + return + } + var ips IPs + if ips, err = s.matchingIPsForEvent(tnt, args, usageID, + ttl); err != nil { + return err + } + defer ips.unlock() + + if _, err = utils.GetFloat64Opts(args, s.cfg.IPsCfg().Opts.Units, + utils.OptsIPsUnits); err != nil { + return + } + + /* + allocate logic + ... + */ + + // index it for storing + if err = s.storeMatchedIPs(ips); err != nil { + return + } + *reply = utils.OK + return +} + +// V1ReleaseIPs is called when we need to clear an allocation. +func (s *IPService) V1ReleaseIPs(ctx *context.Context, args *utils.CGREvent, reply *string) (err error) { + if args == nil { + return utils.NewErrMandatoryIeMissing(utils.Event) + } + if missing := utils.MissingStructFields(args, []string{utils.ID, utils.Event}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + usageID := utils.GetStringOpts(args, s.cfg.IPsCfg().Opts.UsageID, utils.OptsIPsUsageID) + if usageID == utils.EmptyString { + return utils.NewErrMandatoryIeMissing(utils.UsageID) + } + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = s.cfg.GeneralCfg().DefaultTenant + } + + // RPC caching + if config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses].Limit != 0 { + cacheKey := utils.ConcatenatedKey(utils.IPsV1ReleaseIPs, utils.ConcatenatedKey(tnt, args.ID)) + refID := guardian.Guardian.GuardIDs("", + config.CgrConfig().GeneralCfg().LockingTimeout, cacheKey) // RPC caching needs to be atomic + defer guardian.Guardian.UnguardIDs(refID) + if itm, has := Cache.Get(utils.CacheRPCResponses, cacheKey); has { + cachedResp := itm.(*utils.CachedRPCResponse) + if cachedResp.Error == nil { + *reply = *cachedResp.Result.(*string) + } + return cachedResp.Error + } + defer Cache.Set(utils.CacheRPCResponses, cacheKey, + &utils.CachedRPCResponse{Result: reply, Error: err}, + nil, true, utils.NonTransactional) + } + // end of RPC caching + + var ttl *time.Duration + if ttl, err = utils.GetDurationPointerOpts(args, s.cfg.IPsCfg().Opts.TTL, + utils.OptsIPsTTL); err != nil { + return + } + var ips IPs + if ips, err = s.matchingIPsForEvent(tnt, args, usageID, + ttl); err != nil { + return + } + defer ips.unlock() + + /* + release logic + ... + */ + + if err = s.storeMatchedIPs(ips); err != nil { + return + } + *reply = utils.OK + return +} + +// V1GetIP returns retrieves an IP from database. +func (s *IPService) V1GetIP(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *IP) error { + if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + tnt := arg.Tenant + if tnt == utils.EmptyString { + tnt = s.cfg.GeneralCfg().DefaultTenant + } + + lkID := guardian.Guardian.GuardIDs(utils.EmptyString, + config.CgrConfig().GeneralCfg().LockingTimeout, + ipLockKey(tnt, arg.ID)) + defer guardian.Guardian.UnguardIDs(lkID) + + ip, err := s.dm.GetIP(tnt, arg.ID, true, true, utils.NonTransactional) + if err != nil { + return err + } + *reply = *ip + return nil +} diff --git a/engine/libindex_health.go b/engine/libindex_health.go index 2d0ab3a7f..7d8ed6229 100644 --- a/engine/libindex_health.go +++ b/engine/libindex_health.go @@ -310,6 +310,12 @@ func getFiltersAndContexts(dm *DataManager, indxType, tnt, id string) (filterIDs return } filterIDs = rs.FilterIDs + case utils.CacheIPFilterIndexes: + var rs *IPProfile + if rs, err = dm.GetIPProfile(tnt, id, true, false, utils.NonTransactional); err != nil { + return + } + filterIDs = rs.FilterIDs case utils.CacheStatFilterIndexes: var st *StatQueueProfile if st, err = dm.GetStatQueueProfile(tnt, id, true, false, utils.NonTransactional); err != nil { diff --git a/engine/libtest.go b/engine/libtest.go index 302a993b5..43170b693 100644 --- a/engine/libtest.go +++ b/engine/libtest.go @@ -319,12 +319,16 @@ func GetDefaultEmptyCacheStats() map[string]*ltcache.CacheStats { utils.CacheDispatchers: {}, utils.CacheDestinations: {}, utils.CacheEventResources: {}, + utils.CacheEventIPs: {}, utils.CacheFilters: {}, utils.CacheRatingPlans: {}, utils.CacheRatingProfiles: {}, utils.CacheResourceFilterIndexes: {}, utils.CacheResourceProfiles: {}, utils.CacheResources: {}, + utils.CacheIPFilterIndexes: {}, + utils.CacheIPProfiles: {}, + utils.CacheIPs: {}, utils.CacheReverseDestinations: {}, utils.CacheRPCResponses: {}, utils.CacheSharedGroups: {}, diff --git a/engine/libtest_test.go b/engine/libtest_test.go index e702de0a9..0870ed29a 100644 --- a/engine/libtest_test.go +++ b/engine/libtest_test.go @@ -49,12 +49,16 @@ func TestGetDefaultEmptyCacheStats(t *testing.T) { utils.CacheDispatchers, utils.CacheDestinations, utils.CacheEventResources, + utils.CacheEventIPs, utils.CacheFilters, utils.CacheRatingPlans, utils.CacheRatingProfiles, utils.CacheResourceFilterIndexes, utils.CacheResourceProfiles, utils.CacheResources, + utils.CacheIPFilterIndexes, + utils.CacheIPProfiles, + utils.CacheIPs, utils.CacheReverseDestinations, utils.CacheRPCResponses, utils.CacheSharedGroups, diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index b38c2ed52..09073c3a0 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -228,6 +228,10 @@ cgrates.org,round,TOPUP10_AT,,false,false #Tenant[0],Id[1],FilterIDs[2],ActivationInterval[3],TTL[4],Limit[5],AllocationMessage[6],Blocker[7],Stored[8],Weight[9],Thresholds[10] cgrates.org,ResGroup21,*string:~*req.Account:1001,2014-07-29T15:00:00Z,1s,2,call,true,true,10, cgrates.org,ResGroup22,*string:~*req.Account:dan,2014-07-29T15:00:00Z,3600s,2,premium_call,true,true,10, +` + IPsCSVContent = ` +#Tenant[0],Id[1],FilterIDs[2],ActivationInterval[3],TTL[4],Type[5],AddressPool[6],Allocation[7],Stored[8],Weight[9] +cgrates.org,IPs1,*string:~*req.Account:1001,2014-07-29T15:00:00Z,-1,ipv4,127.0.0.1/24,*ascending,true,10 ` StatsCSVContent = ` #Tenant[0],Id[1],FilterIDs[2],ActivationInterval[3],QueueLength[4],TTL[5],MinItems[6],Metrics[7],MetricFilterIDs[8],Stored[9],Blocker[10],Weight[11],ThresholdIDs[12] @@ -294,9 +298,9 @@ func init() { DestinationsCSVContent, TimingsCSVContent, RatesCSVContent, DestinationRatesCSVContent, RatingPlansCSVContent, RatingProfilesCSVContent, SharedGroupsCSVContent, ActionsCSVContent, ActionPlansCSVContent, ActionTriggersCSVContent, AccountActionsCSVContent, - ResourcesCSVContent, StatsCSVContent, TrendsCSVContent, RankingsCSVContent, ThresholdsCSVContent, FiltersCSVContent, - RoutesCSVContent, AttributesCSVContent, ChargersCSVContent, DispatcherCSVContent, - DispatcherHostCSVContent), testTPID, "", nil, nil, false) + ResourcesCSVContent, IPsCSVContent, StatsCSVContent, TrendsCSVContent, RankingsCSVContent, + ThresholdsCSVContent, FiltersCSVContent, RoutesCSVContent, AttributesCSVContent, + ChargersCSVContent, DispatcherCSVContent, DispatcherHostCSVContent), testTPID, "", nil, nil, false) if err != nil { log.Print("error when creating TpReader:", err) } @@ -339,6 +343,9 @@ func init() { if err := csvr.LoadResourceProfiles(); err != nil { log.Print("error in LoadResourceProfiles:", err) } + if err := csvr.LoadIPProfiles(); err != nil { + log.Print("error in LoadIPProfiles:", err) + } if err := csvr.LoadStats(); err != nil { log.Print("error in LoadStats:", err) } diff --git a/engine/model_helpers.go b/engine/model_helpers.go index c51647da1..8c0e48641 100644 --- a/engine/model_helpers.go +++ b/engine/model_helpers.go @@ -1228,6 +1228,193 @@ func ResourceProfileToAPI(rp *ResourceProfile) (tpRL *utils.TPResourceProfile) { return } +type IPMdls []*IPMdl + +// CSVHeader return the header for csv fields as a slice of string +func (tps IPMdls) CSVHeader() []string { + return []string{ + "#" + utils.Tenant, + utils.ID, + utils.FilterIDs, + utils.ActivationIntervalString, + utils.TTL, + utils.Limit, + utils.AllocationMessage, + utils.Type, + utils.AddressPool, + utils.Allocation, + utils.Blocker, + utils.Stored, + utils.Weight, + } +} + +func (tps IPMdls) AsTPIPs() []*utils.TPIPProfile { + mrl := make(map[string]*utils.TPIPProfile) + filterMap := make(map[string]utils.StringSet) + for _, tp := range tps { + tenID := (&utils.TenantID{Tenant: tp.Tenant, ID: tp.ID}).TenantID() + rl, found := mrl[tenID] + if !found { + rl = &utils.TPIPProfile{ + TPid: tp.Tpid, + Tenant: tp.Tenant, + ID: tp.ID, + Stored: tp.Stored, + Type: tp.Type, + AddressPool: tp.AddressPool, + Allocation: tp.Allocation, + } + } + if tp.TTL != utils.EmptyString { + rl.TTL = tp.TTL + } + if tp.Weight != 0 { + rl.Weight = tp.Weight + } + rl.Stored = tp.Stored + if len(tp.ActivationInterval) != 0 { + rl.ActivationInterval = new(utils.TPActivationInterval) + aiSplt := strings.Split(tp.ActivationInterval, utils.InfieldSep) + if len(aiSplt) == 2 { + rl.ActivationInterval.ActivationTime = aiSplt[0] + rl.ActivationInterval.ExpiryTime = aiSplt[1] + } else if len(aiSplt) == 1 { + rl.ActivationInterval.ActivationTime = aiSplt[0] + } + } + if tp.FilterIDs != utils.EmptyString { + if _, has := filterMap[tenID]; !has { + filterMap[tenID] = make(utils.StringSet) + } + filterMap[tenID].AddSlice(strings.Split(tp.FilterIDs, utils.InfieldSep)) + } + mrl[tenID] = rl + } + result := make([]*utils.TPIPProfile, len(mrl)) + i := 0 + for tntID, rl := range mrl { + result[i] = rl + result[i].FilterIDs = filterMap[tntID].AsSlice() + i++ + } + return result +} + +func APItoModelIP(tp *utils.TPIPProfile) IPMdls { + if tp == nil { + return nil + } + var mdls IPMdls + // In case that TPIPProfile don't have filter + if len(tp.FilterIDs) == 0 { + mdl := &IPMdl{ + Tpid: tp.TPid, + Tenant: tp.Tenant, + ID: tp.ID, + Type: tp.Type, + AddressPool: tp.AddressPool, + Allocation: tp.Allocation, + Stored: tp.Stored, + TTL: tp.TTL, + Weight: tp.Weight, + } + if tp.ActivationInterval != nil { + if tp.ActivationInterval.ActivationTime != utils.EmptyString { + mdl.ActivationInterval = tp.ActivationInterval.ActivationTime + } + if tp.ActivationInterval.ExpiryTime != utils.EmptyString { + mdl.ActivationInterval += utils.InfieldSep + tp.ActivationInterval.ExpiryTime + } + } + mdls = append(mdls, mdl) + } + for i, fltr := range tp.FilterIDs { + mdl := &IPMdl{ + Tpid: tp.TPid, + Tenant: tp.Tenant, + ID: tp.ID, + Stored: tp.Stored, + } + if i == 0 { + mdl.Type = tp.Type + mdl.AddressPool = tp.AddressPool + mdl.Allocation = tp.Allocation + mdl.TTL = tp.TTL + mdl.Weight = tp.Weight + if tp.ActivationInterval != nil { + if tp.ActivationInterval.ActivationTime != utils.EmptyString { + mdl.ActivationInterval = tp.ActivationInterval.ActivationTime + } + if tp.ActivationInterval.ExpiryTime != utils.EmptyString { + mdl.ActivationInterval += utils.InfieldSep + tp.ActivationInterval.ExpiryTime + } + } + } + mdl.FilterIDs = fltr + mdls = append(mdls, mdl) + } + return mdls +} + +func APItoIP(tp *utils.TPIPProfile, timezone string) (*IPProfile, error) { + ipp := &IPProfile{ + Tenant: tp.Tenant, + ID: tp.ID, + Type: tp.Type, + AddressPool: tp.AddressPool, + Allocation: tp.Allocation, + Weight: tp.Weight, + Stored: tp.Stored, + FilterIDs: make([]string, len(tp.FilterIDs)), + } + if tp.TTL != utils.EmptyString { + var err error + if ipp.TTL, err = utils.ParseDurationWithNanosecs(tp.TTL); err != nil { + return nil, err + } + } + + copy(ipp.FilterIDs, tp.FilterIDs) + + if tp.ActivationInterval != nil { + var err error + if ipp.ActivationInterval, err = tp.ActivationInterval.AsActivationInterval(timezone); err != nil { + return nil, err + } + } + return ipp, nil +} + +func IPProfileToAPI(ipp *IPProfile) *utils.TPIPProfile { + tp := &utils.TPIPProfile{ + Tenant: ipp.Tenant, + ID: ipp.ID, + FilterIDs: make([]string, len(ipp.FilterIDs)), + ActivationInterval: new(utils.TPActivationInterval), + Type: ipp.Type, + AddressPool: ipp.AddressPool, + Allocation: ipp.Allocation, + Stored: ipp.Stored, + Weight: ipp.Weight, + } + if ipp.TTL != time.Duration(0) { + tp.TTL = ipp.TTL.String() + } + + copy(tp.FilterIDs, ipp.FilterIDs) + + if ipp.ActivationInterval != nil { + if !ipp.ActivationInterval.ActivationTime.IsZero() { + tp.ActivationInterval.ActivationTime = ipp.ActivationInterval.ActivationTime.Format(time.RFC3339) + } + if !ipp.ActivationInterval.ExpiryTime.IsZero() { + tp.ActivationInterval.ExpiryTime = ipp.ActivationInterval.ExpiryTime.Format(time.RFC3339) + } + } + return tp +} + type StatMdls []*StatMdl // CSVHeader return the header for csv fields as a slice of string diff --git a/engine/models.go b/engine/models.go index b1b768fe7..56b7ceead 100644 --- a/engine/models.go +++ b/engine/models.go @@ -260,6 +260,26 @@ func (ResourceMdl) TableName() string { return utils.TBLTPResources } +type IPMdl struct { + PK uint `gorm:"primary_key"` + Tpid string + Tenant string `index:"0" re:".*"` + ID string `index:"1" re:".*"` + FilterIDs string `index:"2" re:".*"` + ActivationInterval string `index:"3" re:".*"` + TTL string `index:"4" re:".*"` + Type string `index:"5" re:".*"` + AddressPool string `index:"6" re:".*"` + Allocation string `index:"7" re:".*"` + Stored bool `index:"8" re:".*"` + Weight float64 `index:"9" re:".*"` + CreatedAt time.Time +} + +func (IPMdl) TableName() string { + return utils.TBLTPIPs +} + type StatMdl struct { PK uint `gorm:"primary_key"` Tpid string diff --git a/engine/storage_csv.go b/engine/storage_csv.go index 8548a4a76..f08699580 100644 --- a/engine/storage_csv.go +++ b/engine/storage_csv.go @@ -57,6 +57,7 @@ type CSVStorage struct { actiontriggersFn []string accountactionsFn []string resProfilesFn []string + ipProfilesFn []string statsFn []string trendsFn []string rankingsFn []string @@ -74,7 +75,7 @@ func NewCSVStorage(sep rune, destinationsFn, timingsFn, ratesFn, destinationratesFn, destinationratetimingsFn, ratingprofilesFn, sharedgroupsFn, actionsFn, actiontimingsFn, actiontriggersFn, accountactionsFn, - resProfilesFn, statsFn, trendsFn, rankingsFn, thresholdsFn, filterFn, routeProfilesFn, + resProfilesFn, ipProfilesFn, statsFn, trendsFn, rankingsFn, thresholdsFn, filterFn, routeProfilesFn, attributeProfilesFn, chargerProfilesFn, dispatcherProfilesFn, dispatcherHostsFn []string) *CSVStorage { return &CSVStorage{ sep: sep, @@ -91,6 +92,7 @@ func NewCSVStorage(sep rune, actiontriggersFn: actiontriggersFn, accountactionsFn: accountactionsFn, resProfilesFn: resProfilesFn, + ipProfilesFn: ipProfilesFn, statsFn: statsFn, trendsFn: trendsFn, rankingsFn: rankingsFn, @@ -122,6 +124,7 @@ func NewFileCSVStorage(sep rune, dataPath string) (*CSVStorage, error) { actionTriggersPaths := appendName(allFoldersPath, utils.ActionTriggersCsv) accountActionsPaths := appendName(allFoldersPath, utils.AccountActionsCsv) resourcesPaths := appendName(allFoldersPath, utils.ResourcesCsv) + ipsPaths := appendName(allFoldersPath, utils.IPsCsv) statsPaths := appendName(allFoldersPath, utils.StatsCsv) trendsPaths := appendName(allFoldersPath, utils.TrendsCsv) rankingsPaths := appendName(allFoldersPath, utils.RankingsCsv) @@ -145,6 +148,7 @@ func NewFileCSVStorage(sep rune, dataPath string) (*CSVStorage, error) { actionTriggersPaths, accountActionsPaths, resourcesPaths, + ipsPaths, statsPaths, trendsPaths, rankingsPaths, @@ -163,13 +167,13 @@ func NewStringCSVStorage(sep rune, destinationsFn, timingsFn, ratesFn, destinationratesFn, destinationratetimingsFn, ratingprofilesFn, sharedgroupsFn, actionsFn, actiontimingsFn, actiontriggersFn, accountactionsFn, - resProfilesFn, statsFn, trendsFn, rankingsFn, thresholdsFn, filterFn, routeProfilesFn, + resProfilesFn, ipProfilesFn, statsFn, trendsFn, rankingsFn, thresholdsFn, filterFn, routeProfilesFn, attributeProfilesFn, chargerProfilesFn, dispatcherProfilesFn, dispatcherHostsFn string) *CSVStorage { c := NewCSVStorage(sep, []string{destinationsFn}, []string{timingsFn}, []string{ratesFn}, []string{destinationratesFn}, []string{destinationratetimingsFn}, []string{ratingprofilesFn}, []string{sharedgroupsFn}, []string{actionsFn}, []string{actiontimingsFn}, []string{actiontriggersFn}, []string{accountactionsFn}, - []string{resProfilesFn}, []string{statsFn}, []string{trendsFn}, []string{rankingsFn}, []string{thresholdsFn}, []string{filterFn}, + []string{resProfilesFn}, []string{ipProfilesFn}, []string{statsFn}, []string{trendsFn}, []string{rankingsFn}, []string{thresholdsFn}, []string{filterFn}, []string{routeProfilesFn}, []string{attributeProfilesFn}, []string{chargerProfilesFn}, []string{dispatcherProfilesFn}, []string{dispatcherHostsFn}) c.generator = NewCsvString @@ -205,6 +209,7 @@ func NewGoogleCSVStorage(sep rune, spreadsheetID string) (*CSVStorage, error) { getIfExist(utils.ActionTriggers), getIfExist(utils.AccountActions), getIfExist(utils.Resources), + getIfExist(utils.IPs), getIfExist(utils.Stats), getIfExist(utils.Trends), getIfExist(utils.Trends), @@ -239,6 +244,7 @@ func NewURLCSVStorage(sep rune, dataPath string) *CSVStorage { var actionTriggersPaths []string var accountActionsPaths []string var resourcesPaths []string + var ipsPaths []string var statsPaths []string var trendsPaths []string var rankingsPaths []string @@ -264,6 +270,7 @@ func NewURLCSVStorage(sep rune, dataPath string) *CSVStorage { actionTriggersPaths = append(actionTriggersPaths, joinURL(baseURL, utils.ActionTriggersCsv)) accountActionsPaths = append(accountActionsPaths, joinURL(baseURL, utils.AccountActionsCsv)) resourcesPaths = append(resourcesPaths, joinURL(baseURL, utils.ResourcesCsv)) + ipsPaths = append(ipsPaths, joinURL(baseURL, utils.IPsCsv)) statsPaths = append(statsPaths, joinURL(baseURL, utils.StatsCsv)) trendsPaths = append(trendsPaths, joinURL(baseURL, utils.TrendsCsv)) rankingsPaths = append(rankingsPaths, joinURL(baseURL, utils.RankingsCsv)) @@ -301,6 +308,8 @@ func NewURLCSVStorage(sep rune, dataPath string) *CSVStorage { accountActionsPaths = append(accountActionsPaths, baseURL) case strings.HasSuffix(baseURL, utils.ResourcesCsv): resourcesPaths = append(resourcesPaths, baseURL) + case strings.HasSuffix(baseURL, utils.IPsCsv): + ipsPaths = append(ipsPaths, baseURL) case strings.HasSuffix(baseURL, utils.StatsCsv): statsPaths = append(statsPaths, baseURL) case strings.HasSuffix(baseURL, utils.TrendsCsv): @@ -337,6 +346,7 @@ func NewURLCSVStorage(sep rune, dataPath string) *CSVStorage { actionTriggersPaths, accountActionsPaths, resourcesPaths, + ipsPaths, statsPaths, trendsPaths, rankingsPaths, @@ -565,6 +575,18 @@ func (csvs *CSVStorage) GetTPResources(tpid, tenant, id string) ([]*utils.TPReso return tpResLimits.AsTPResources(), nil } +func (csvs *CSVStorage) GetTPIPs(tpid, tenant, id string) ([]*utils.TPIPProfile, error) { + var mdls IPMdls + if err := csvs.proccesData(IPMdl{}, csvs.ipProfilesFn, func(tp any) { + mdl := tp.(IPMdl) + mdl.Tpid = tpid + mdls = append(mdls, &mdl) + }); err != nil { + return nil, err + } + return mdls.AsTPIPs(), nil +} + func (csvs *CSVStorage) GetTPStats(tpid, tenant, id string) ([]*utils.TPStatProfile, error) { var tpStats StatMdls if err := csvs.proccesData(StatMdl{}, csvs.statsFn, func(tp any) { diff --git a/engine/storage_interface.go b/engine/storage_interface.go index 197f68214..f355af526 100644 --- a/engine/storage_interface.go +++ b/engine/storage_interface.go @@ -86,6 +86,12 @@ type DataDB interface { GetResourceDrv(string, string) (*Resource, error) SetResourceDrv(*Resource) error RemoveResourceDrv(string, string) error + GetIPProfileDrv(string, string) (*IPProfile, error) + SetIPProfileDrv(*IPProfile) error + RemoveIPProfileDrv(string, string) error + GetIPDrv(string, string) (*IP, error) + SetIPDrv(*IP) error + RemoveIPDrv(string, string) error GetTimingDrv(string) (*utils.TPTiming, error) SetTimingDrv(*utils.TPTiming) error RemoveTimingDrv(string) error @@ -187,6 +193,7 @@ type LoadReader interface { GetTPActionTriggers(string, string) ([]*utils.TPActionTriggers, error) GetTPAccountActions(*utils.TPAccountActions) ([]*utils.TPAccountActions, error) GetTPResources(string, string, string) ([]*utils.TPResourceProfile, error) + GetTPIPs(string, string, string) ([]*utils.TPIPProfile, error) GetTPStats(string, string, string) ([]*utils.TPStatProfile, error) GetTPTrends(string, string, string) ([]*utils.TPTrendsProfile, error) GetTPRankings(string, string, string) ([]*utils.TPRankingProfile, error) @@ -213,6 +220,7 @@ type LoadWriter interface { SetTPActionTriggers([]*utils.TPActionTriggers) error SetTPAccountActions([]*utils.TPAccountActions) error SetTPResources([]*utils.TPResourceProfile) error + SetTPIPs([]*utils.TPIPProfile) error SetTPStats([]*utils.TPStatProfile) error SetTPTrends([]*utils.TPTrendsProfile) error SetTPRankings([]*utils.TPRankingProfile) error diff --git a/engine/storage_internal_datadb.go b/engine/storage_internal_datadb.go index 243a61939..49827addc 100644 --- a/engine/storage_internal_datadb.go +++ b/engine/storage_internal_datadb.go @@ -216,10 +216,11 @@ func (iDB *InternalDB) HasDataDrv(category, subject, tenant string) (bool, error case utils.DestinationPrefix, utils.RatingPlanPrefix, utils.RatingProfilePrefix, utils.ActionPrefix, utils.ActionPlanPrefix, utils.AccountPrefix: return iDB.db.HasItem(utils.CachePrefixToInstance[category], subject), nil - case utils.ResourcesPrefix, utils.ResourceProfilesPrefix, utils.StatQueuePrefix, - utils.StatQueueProfilePrefix, utils.ThresholdPrefix, utils.ThresholdProfilePrefix, - utils.FilterPrefix, utils.RouteProfilePrefix, utils.AttributeProfilePrefix, - utils.ChargerProfilePrefix, utils.DispatcherProfilePrefix, utils.DispatcherHostPrefix: + case utils.ResourcesPrefix, utils.ResourceProfilesPrefix, utils.IPsPrefix, + utils.IPProfilesPrefix, utils.StatQueuePrefix, utils.StatQueueProfilePrefix, + utils.ThresholdPrefix, utils.ThresholdProfilePrefix, utils.FilterPrefix, + utils.RouteProfilePrefix, utils.AttributeProfilePrefix, utils.ChargerProfilePrefix, + utils.DispatcherProfilePrefix, utils.DispatcherHostPrefix: return iDB.db.HasItem(utils.CachePrefixToInstance[category], utils.ConcatenatedKey(tenant, subject)), nil } return false, errors.New("Unsupported HasData category") @@ -533,6 +534,44 @@ func (iDB *InternalDB) RemoveResourceDrv(tenant, id string) (err error) { return } +func (iDB *InternalDB) GetIPProfileDrv(tenant, id string) (*IPProfile, error) { + if x, ok := iDB.db.Get(utils.CacheIPProfiles, utils.ConcatenatedKey(tenant, id)); ok && x != nil { + return x.(*IPProfile), nil + } + return nil, utils.ErrNotFound +} + +func (iDB *InternalDB) SetIPProfileDrv(ipp *IPProfile) error { + iDB.db.Set(utils.CacheIPProfiles, ipp.TenantID(), ipp, nil, + true, utils.NonTransactional) + return nil +} + +func (iDB *InternalDB) RemoveIPProfileDrv(tenant, id string) error { + iDB.db.Remove(utils.CacheIPProfiles, utils.ConcatenatedKey(tenant, id), + true, utils.NonTransactional) + return nil +} + +func (iDB *InternalDB) GetIPDrv(tenant, id string) (*IP, error) { + if x, ok := iDB.db.Get(utils.CacheIPs, utils.ConcatenatedKey(tenant, id)); ok && x != nil { + return x.(*IP), nil + } + return nil, utils.ErrNotFound +} + +func (iDB *InternalDB) SetIPDrv(ip *IP) error { + iDB.db.Set(utils.CacheIPs, ip.TenantID(), ip, nil, + true, utils.NonTransactional) + return nil +} + +func (iDB *InternalDB) RemoveIPDrv(tenant, id string) error { + iDB.db.Remove(utils.CacheIPs, utils.ConcatenatedKey(tenant, id), + true, utils.NonTransactional) + return nil +} + func (iDB *InternalDB) GetTimingDrv(id string) (tmg *utils.TPTiming, err error) { x, ok := iDB.db.Get(utils.CacheTimings, id) if !ok || x == nil { diff --git a/engine/storage_internal_stordb.go b/engine/storage_internal_stordb.go index db8cb0e1b..5b992cfb0 100644 --- a/engine/storage_internal_stordb.go +++ b/engine/storage_internal_stordb.go @@ -381,6 +381,30 @@ func (iDB *InternalDB) GetTPResources(tpid, tenant, id string) (resources []*uti return } +func (iDB *InternalDB) GetTPIPs(tpid, tenant, id string) ([]*utils.TPIPProfile, error) { + key := tpid + if tenant != utils.EmptyString { + key += utils.ConcatenatedKeySep + tenant + } + if id != utils.EmptyString { + key += utils.ConcatenatedKeySep + id + } + ids := iDB.db.GetItemIDs(utils.CacheTBLTPIPs, key) + ips := make([]*utils.TPIPProfile, 0, len(ids)) + for _, id := range ids { + x, ok := iDB.db.Get(utils.CacheTBLTPIPs, id) + if !ok || x == nil { + return nil, utils.ErrNotFound + } + ips = append(ips, x.(*utils.TPIPProfile)) + + } + if len(ips) == 0 { + return nil, utils.ErrNotFound + } + return ips, nil +} + func (iDB *InternalDB) GetTPStats(tpid, tenant, id string) (stats []*utils.TPStatProfile, err error) { key := tpid if tenant != utils.EmptyString { @@ -772,6 +796,18 @@ func (iDB *InternalDB) SetTPResources(resources []*utils.TPResourceProfile) (err } return } + +func (iDB *InternalDB) SetTPIPs(ips []*utils.TPIPProfile) (err error) { + if len(ips) == 0 { + return nil + } + for _, ip := range ips { + iDB.db.Set(utils.CacheTBLTPIPs, utils.ConcatenatedKey(ip.TPid, ip.Tenant, ip.ID), ip, nil, + cacheCommit(utils.NonTransactional), utils.NonTransactional) + } + return +} + func (iDB *InternalDB) SetTPStats(stats []*utils.TPStatProfile) (err error) { if len(stats) == 0 { return nil diff --git a/engine/storage_mongo_datadb.go b/engine/storage_mongo_datadb.go index baa0a5c07..9d13ccf62 100644 --- a/engine/storage_mongo_datadb.go +++ b/engine/storage_mongo_datadb.go @@ -60,9 +60,11 @@ const ( ColLht = "load_history" ColVer = "versions" ColRsP = "resource_profiles" + ColIPp = "ip_profiles" ColIndx = "indexes" ColTmg = "timings" ColRes = "resources" + ColIPs = "ips" ColSqs = "statqueues" ColTrp = "trend_profiles" ColTrd = "trends" @@ -304,7 +306,7 @@ func (ms *MongoStorage) ensureIndexesForCol(col string) error { // exported for switch col { case ColAct, ColApl, ColAAp, ColAtr, ColRpl, ColDst, ColRds, ColLht, ColIndx: err = ms.enusureIndex(col, true, "key") - case ColRsP, ColRes, ColSqs, ColRgp, ColTrp, ColRnk, ColSqp, ColTps, ColThs, ColTrd, ColRts, ColAttr, ColFlt, ColCpp, ColDpp, ColDph: + case ColRsP, ColRes, ColIPp, ColIPs, ColSqs, ColRgp, ColTrp, ColRnk, ColSqp, ColTps, ColThs, ColTrd, ColRts, ColAttr, ColFlt, ColCpp, ColDpp, ColDph: err = ms.enusureIndex(col, true, "tenant", "id") case ColRpf, ColShg, ColAcc: err = ms.enusureIndex(col, true, "id") @@ -313,9 +315,9 @@ func (ms *MongoStorage) ensureIndexesForCol(col string) error { // exported for utils.TBLTPDestinationRates, utils.TBLTPRatingPlans, utils.TBLTPSharedGroups, utils.TBLTPActions, utils.TBLTPRankings, utils.TBLTPActionPlans, utils.TBLTPActionTriggers, - utils.TBLTPStats, utils.TBLTPResources, utils.TBLTPDispatchers, - utils.TBLTPDispatcherHosts, utils.TBLTPChargers, - utils.TBLTPRoutes, utils.TBLTPThresholds: + utils.TBLTPStats, utils.TBLTPResources, utils.TBLTPIPs, + utils.TBLTPDispatchers, utils.TBLTPDispatcherHosts, + utils.TBLTPChargers, utils.TBLTPRoutes, utils.TBLTPThresholds: err = ms.enusureIndex(col, true, "tpid", "id") case utils.TBLTPRatingProfiles: err = ms.enusureIndex(col, true, "tpid", "tenant", @@ -349,15 +351,15 @@ func (ms *MongoStorage) EnsureIndexes(cols ...string) error { if ms.IsDataDB() { cols = []string{ ColAct, ColApl, ColAAp, ColAtr, ColRpl, ColDst, ColRds, ColLht, ColIndx, - ColRsP, ColRes, ColSqs, ColSqp, ColTps, ColThs, ColRts, ColAttr, ColFlt, ColCpp, - ColDpp, ColRpf, ColShg, ColAcc, ColRgp, ColTrp, ColTrd, ColRnk, + ColRsP, ColRes, ColIPs, ColSqs, ColSqp, ColTps, ColThs, ColRts, ColAttr, + ColFlt, ColCpp, ColDpp, ColRpf, ColShg, ColAcc, ColRgp, ColTrp, ColTrd, ColRnk, } } else { cols = []string{ utils.TBLTPTimings, utils.TBLTPDestinations, utils.TBLTPDestinationRates, utils.TBLTPRatingPlans, utils.TBLTPSharedGroups, utils.TBLTPActions, utils.TBLTPActionPlans, - utils.TBLTPActionTriggers, utils.TBLTPRankings, utils.TBLTPStats, utils.TBLTPResources, utils.TBLTPRatingProfiles, - utils.CDRsTBL, utils.SessionCostsTBL, + utils.TBLTPActionTriggers, utils.TBLTPRankings, utils.TBLTPStats, utils.TBLTPResources, + utils.TBLTPIPs, utils.TBLTPRatingProfiles, utils.CDRsTBL, utils.SessionCostsTBL, } } } @@ -433,6 +435,10 @@ func (ms *MongoStorage) RemoveKeysForPrefix(prefix string) error { colName = ColRes case utils.ResourceProfilesPrefix: colName = ColRsP + case utils.IPsPrefix: + colName = ColIPs + case utils.IPProfilesPrefix: + colName = ColIPp case utils.ThresholdProfilePrefix: colName = ColTps case utils.StatQueueProfilePrefix: @@ -608,6 +614,10 @@ func (ms *MongoStorage) GetKeysForPrefix(prefix string) (keys []string, err erro keys, qryErr = ms.getAllKeysMatchingTenantID(sctx, ColRsP, utils.ResourceProfilesPrefix, subject, tntID) case utils.ResourcesPrefix: keys, qryErr = ms.getAllKeysMatchingTenantID(sctx, ColRes, utils.ResourcesPrefix, subject, tntID) + case utils.IPProfilesPrefix: + keys, qryErr = ms.getAllKeysMatchingTenantID(sctx, ColIPp, utils.IPProfilesPrefix, subject, tntID) + case utils.IPsPrefix: + keys, qryErr = ms.getAllKeysMatchingTenantID(sctx, ColIPs, utils.IPsPrefix, subject, tntID) case utils.StatQueuePrefix: keys, qryErr = ms.getAllKeysMatchingTenantID(sctx, ColSqs, utils.StatQueuePrefix, subject, tntID) case utils.RankingsProfilePrefix: @@ -644,6 +654,8 @@ func (ms *MongoStorage) GetKeysForPrefix(prefix string) (keys []string, err erro keys, qryErr = ms.getAllIndexKeys(sctx, utils.AttributeFilterIndexes) case utils.ResourceFilterIndexes: keys, qryErr = ms.getAllIndexKeys(sctx, utils.ResourceFilterIndexes) + case utils.IPFilterIndexes: + keys, qryErr = ms.getAllIndexKeys(sctx, utils.IPFilterIndexes) case utils.StatFilterIndexes: keys, qryErr = ms.getAllIndexKeys(sctx, utils.StatFilterIndexes) case utils.ThresholdFilterIndexes: @@ -686,6 +698,10 @@ func (ms *MongoStorage) HasDataDrv(category, subject, tenant string) (has bool, count, err = ms.getCol(ColRes).CountDocuments(sctx, bson.M{"tenant": tenant, "id": subject}) case utils.ResourceProfilesPrefix: count, err = ms.getCol(ColRsP).CountDocuments(sctx, bson.M{"tenant": tenant, "id": subject}) + case utils.IPsPrefix: + count, err = ms.getCol(ColIPs).CountDocuments(sctx, bson.M{"tenant": tenant, "id": subject}) + case utils.IPProfilesPrefix: + count, err = ms.getCol(ColIPp).CountDocuments(sctx, bson.M{"tenant": tenant, "id": subject}) case utils.StatQueuePrefix: count, err = ms.getCol(ColSqs).CountDocuments(sctx, bson.M{"tenant": tenant, "id": subject}) case utils.StatQueueProfilePrefix: @@ -1431,6 +1447,72 @@ func (ms *MongoStorage) RemoveResourceDrv(tenant, id string) error { }) } +func (ms *MongoStorage) GetIPProfileDrv(tenant, id string) (*IPProfile, error) { + ipp := new(IPProfile) + err := ms.query(func(sctx mongo.SessionContext) error { + sr := ms.getCol(ColIPp).FindOne(sctx, bson.M{"tenant": tenant, "id": id}) + decodeErr := sr.Decode(ipp) + if errors.Is(decodeErr, mongo.ErrNoDocuments) { + return utils.ErrNotFound + } + return decodeErr + }) + return ipp, err +} + +func (ms *MongoStorage) SetIPProfileDrv(ip *IPProfile) error { + return ms.query(func(sctx mongo.SessionContext) error { + _, err := ms.getCol(ColIPp).UpdateOne(sctx, bson.M{"tenant": ip.Tenant, "id": ip.ID}, + bson.M{"$set": ip}, + options.Update().SetUpsert(true), + ) + return err + }) +} + +func (ms *MongoStorage) RemoveIPProfileDrv(tenant, id string) error { + return ms.query(func(sctx mongo.SessionContext) error { + dr, err := ms.getCol(ColIPp).DeleteOne(sctx, bson.M{"tenant": tenant, "id": id}) + if dr.DeletedCount == 0 { + return utils.ErrNotFound + } + return err + }) +} + +func (ms *MongoStorage) GetIPDrv(tenant, id string) (*IP, error) { + ip := new(IP) + err := ms.query(func(sctx mongo.SessionContext) error { + sr := ms.getCol(ColIPs).FindOne(sctx, bson.M{"tenant": tenant, "id": id}) + decodeErr := sr.Decode(ip) + if errors.Is(decodeErr, mongo.ErrNoDocuments) { + return utils.ErrNotFound + } + return decodeErr + }) + return ip, err +} + +func (ms *MongoStorage) SetIPDrv(ip *IP) error { + return ms.query(func(sctx mongo.SessionContext) error { + _, err := ms.getCol(ColIPs).UpdateOne(sctx, bson.M{"tenant": ip.Tenant, "id": ip.ID}, + bson.M{"$set": ip}, + options.Update().SetUpsert(true), + ) + return err + }) +} + +func (ms *MongoStorage) RemoveIPDrv(tenant, id string) error { + return ms.query(func(sctx mongo.SessionContext) error { + dr, err := ms.getCol(ColIPs).DeleteOne(sctx, bson.M{"tenant": tenant, "id": id}) + if dr.DeletedCount == 0 { + return utils.ErrNotFound + } + return err + }) +} + func (ms *MongoStorage) GetTimingDrv(id string) (*utils.TPTiming, error) { timing := new(utils.TPTiming) err := ms.query(func(sctx mongo.SessionContext) error { diff --git a/engine/storage_mongo_stordb.go b/engine/storage_mongo_stordb.go index ec6e13545..056ba07cb 100644 --- a/engine/storage_mongo_stordb.go +++ b/engine/storage_mongo_stordb.go @@ -409,6 +409,36 @@ func (ms *MongoStorage) GetTPResources(tpid, tenant, id string) ([]*utils.TPReso return results, err } +func (ms *MongoStorage) GetTPIPs(tpid, tenant, id string) ([]*utils.TPIPProfile, error) { + filter := bson.M{"tpid": tpid} + if id != "" { + filter["id"] = id + } + if tenant != "" { + filter["tenant"] = tenant + } + var results []*utils.TPIPProfile + err := ms.query(func(sctx mongo.SessionContext) (err error) { + cur, err := ms.getCol(utils.TBLTPIPs).Find(sctx, filter) + if err != nil { + return err + } + for cur.Next(sctx) { + var el utils.TPIPProfile + err := cur.Decode(&el) + if err != nil { + return err + } + results = append(results, &el) + } + if len(results) == 0 { + return utils.ErrNotFound + } + return cur.Close(sctx) + }) + return results, err +} + func (ms *MongoStorage) GetTPStats(tpid, tenant, id string) ([]*utils.TPStatProfile, error) { filter := bson.M{ "tpid": tpid, @@ -912,6 +942,22 @@ func (ms *MongoStorage) SetTPResources(tpRLs []*utils.TPResourceProfile) (err er }) } +func (ms *MongoStorage) SetTPIPs(tps []*utils.TPIPProfile) (err error) { + if len(tps) == 0 { + return + } + return ms.query(func(sctx mongo.SessionContext) (err error) { + for _, tp := range tps { + _, err = ms.getCol(utils.TBLTPIPs).UpdateOne(sctx, bson.M{"tpid": tp.TPid, "id": tp.ID}, + bson.M{"$set": tp}, options.Update().SetUpsert(true)) + if err != nil { + return err + } + } + return nil + }) +} + func (ms *MongoStorage) SetTPRStats(tps []*utils.TPStatProfile) (err error) { if len(tps) == 0 { return diff --git a/engine/storage_redis.go b/engine/storage_redis.go index 2a2466944..2dc8f4c42 100644 --- a/engine/storage_redis.go +++ b/engine/storage_redis.go @@ -296,10 +296,11 @@ func (rs *RedisStorage) HasDataDrv(category, subject, tenant string) (exists boo utils.ActionPrefix, utils.ActionPlanPrefix, utils.AccountPrefix: err = rs.Cmd(&i, redis_EXISTS, category+subject) return i == 1, err - case utils.ResourcesPrefix, utils.ResourceProfilesPrefix, utils.StatQueuePrefix, - utils.StatQueueProfilePrefix, utils.ThresholdPrefix, utils.ThresholdProfilePrefix, - utils.FilterPrefix, utils.RouteProfilePrefix, utils.AttributeProfilePrefix, - utils.ChargerProfilePrefix, utils.DispatcherProfilePrefix, utils.DispatcherHostPrefix: + case utils.ResourcesPrefix, utils.ResourceProfilesPrefix, utils.IPsPrefix, + utils.IPProfilesPrefix, utils.StatQueuePrefix, utils.StatQueueProfilePrefix, + utils.ThresholdPrefix, utils.ThresholdProfilePrefix, utils.FilterPrefix, + utils.RouteProfilePrefix, utils.AttributeProfilePrefix, utils.ChargerProfilePrefix, + utils.DispatcherProfilePrefix, utils.DispatcherHostPrefix: err := rs.Cmd(&i, redis_EXISTS, category+utils.ConcatenatedKey(tenant, subject)) return i == 1, err } @@ -806,6 +807,60 @@ func (rs *RedisStorage) RemoveResourceDrv(tenant, id string) (err error) { return rs.Cmd(nil, redis_DEL, utils.ResourcesPrefix+utils.ConcatenatedKey(tenant, id)) } +func (rs *RedisStorage) GetIPProfileDrv(tenant, id string) (*IPProfile, error) { + var values []byte + if err := rs.Cmd(&values, redis_GET, utils.IPProfilesPrefix+utils.ConcatenatedKey(tenant, id)); err != nil { + return nil, err + } + if len(values) == 0 { + return nil, utils.ErrNotFound + } + var ipp *IPProfile + if err := rs.ms.Unmarshal(values, &ipp); err != nil { + return nil, err + } + return ipp, nil +} + +func (rs *RedisStorage) SetIPProfileDrv(ipp *IPProfile) error { + result, err := rs.ms.Marshal(ipp) + if err != nil { + return err + } + return rs.Cmd(nil, redis_SET, utils.IPProfilesPrefix+ipp.TenantID(), string(result)) +} + +func (rs *RedisStorage) RemoveIPProfileDrv(tenant, id string) error { + return rs.Cmd(nil, redis_DEL, utils.IPProfilesPrefix+utils.ConcatenatedKey(tenant, id)) +} + +func (rs *RedisStorage) GetIPDrv(tenant, id string) (*IP, error) { + var values []byte + if err := rs.Cmd(&values, redis_GET, utils.IPsPrefix+utils.ConcatenatedKey(tenant, id)); err != nil { + return nil, err + } + if len(values) == 0 { + return nil, utils.ErrNotFound + } + var ip *IP + if err := rs.ms.Unmarshal(values, &ip); err != nil { + return nil, err + } + return ip, nil +} + +func (rs *RedisStorage) SetIPDrv(ip *IP) error { + result, err := rs.ms.Marshal(ip) + if err != nil { + return err + } + return rs.Cmd(nil, redis_SET, utils.IPsPrefix+ip.TenantID(), string(result)) +} + +func (rs *RedisStorage) RemoveIPDrv(tenant, id string) error { + return rs.Cmd(nil, redis_DEL, utils.IPsPrefix+utils.ConcatenatedKey(tenant, id)) +} + func (rs *RedisStorage) GetTimingDrv(id string) (t *utils.TPTiming, err error) { var values []byte if err = rs.Cmd(&values, redis_GET, utils.TimingsPrefix+id); err != nil { diff --git a/engine/storage_sql.go b/engine/storage_sql.go index 790931998..59397483c 100644 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -556,6 +556,28 @@ func (sqls *SQLStorage) SetTPResources(rls []*utils.TPResourceProfile) error { return nil } +func (sqls *SQLStorage) SetTPIPs(tps []*utils.TPIPProfile) error { + if len(tps) == 0 { + return nil + } + tx := sqls.db.Begin() + for _, tp := range tps { + // Remove previous + if err := tx.Where(&IPMdl{Tpid: tp.TPid, ID: tp.ID}).Delete(IPMdl{}).Error; err != nil { + tx.Rollback() + return err + } + for _, mrl := range APItoModelIP(tp) { + if err := tx.Create(&mrl).Error; err != nil { + tx.Rollback() + return err + } + } + } + tx.Commit() + return nil +} + func (sqls *SQLStorage) SetTPStats(sts []*utils.TPStatProfile) error { if len(sts) == 0 { return nil @@ -1446,6 +1468,25 @@ func (sqls *SQLStorage) GetTPResources(tpid, tenant, id string) ([]*utils.TPReso return arls, nil } +func (sqls *SQLStorage) GetTPIPs(tpid, tenant, id string) ([]*utils.TPIPProfile, error) { + var rls IPMdls + q := sqls.db.Where("tpid = ?", tpid) + if len(id) != 0 { + q = q.Where("id = ?", id) + } + if len(tenant) != 0 { + q = q.Where("tenant = ?", tenant) + } + if err := q.Find(&rls).Error; err != nil { + return nil, err + } + arls := rls.AsTPIPs() + if len(arls) == 0 { + return arls, utils.ErrNotFound + } + return arls, nil +} + func (sqls *SQLStorage) GetTPStats(tpid, tenant, id string) ([]*utils.TPStatProfile, error) { var sts StatMdls q := sqls.db.Where("tpid = ?", tpid) diff --git a/engine/tpexporter.go b/engine/tpexporter.go index 8d7cfd234..74e07c9d1 100644 --- a/engine/tpexporter.go +++ b/engine/tpexporter.go @@ -264,6 +264,21 @@ func (tpExp *TPExporter) Run() error { } } + storDataIPs, err := tpExp.storDb.GetTPIPs(tpExp.tpID, "", "") + if err != nil && err.Error() != utils.ErrNotFound.Error() { + utils.Logger.Warning(fmt.Sprintf("<%s> error: %s, when getting %s from stordb for export", utils.ApierS, err, utils.TpIPs)) + withError = true + } + if len(storDataIPs) != 0 { + toExportMap[utils.IPsCsv] = make([]any, 0, len(storDataIPs)) + for _, sd := range storDataIPs { + sdModels := APItoModelIP(sd) + for _, sdModel := range sdModels { + toExportMap[utils.IPsCsv] = append(toExportMap[utils.IPsCsv], sdModel) + } + } + } + storDataStats, err := tpExp.storDb.GetTPStats(tpExp.tpID, "", "") if err != nil && err.Error() != utils.ErrNotFound.Error() { utils.Logger.Warning(fmt.Sprintf("<%s> error: %s, when getting %s from stordb for export", utils.ApierS, err, utils.TpStats)) diff --git a/engine/tpimporter_csv.go b/engine/tpimporter_csv.go index 7e6f4f406..abfba110c 100644 --- a/engine/tpimporter_csv.go +++ b/engine/tpimporter_csv.go @@ -276,6 +276,17 @@ func (tpImp *TPCSVImporter) importResources(fn string) error { return tpImp.StorDb.SetTPResources(rls) } +func (tpImp *TPCSVImporter) importIPs(fn string) error { + if tpImp.Verbose { + log.Printf("Processing file: <%s> ", fn) + } + ips, err := tpImp.csvr.GetTPIPs(tpImp.TPid, "", "") + if err != nil { + return err + } + return tpImp.StorDb.SetTPIPs(ips) +} + func (tpImp *TPCSVImporter) importStats(fn string) error { if tpImp.Verbose { log.Printf("Processing file: <%s> ", fn) diff --git a/engine/tpreader.go b/engine/tpreader.go index ae5d8ad5f..64a2f7b55 100644 --- a/engine/tpreader.go +++ b/engine/tpreader.go @@ -48,6 +48,7 @@ type TpReader struct { ratingProfiles map[string]*RatingProfile sharedGroups map[string]*SharedGroup resProfiles map[utils.TenantID]*utils.TPResourceProfile + ipProfiles map[utils.TenantID]*utils.TPIPProfile sqProfiles map[utils.TenantID]*utils.TPStatProfile trProfiles map[utils.TenantID]*utils.TPTrendsProfile rgProfiles map[utils.TenantID]*utils.TPRankingProfile @@ -96,6 +97,7 @@ func (tpr *TpReader) Init() { tpr.sharedGroups = make(map[string]*SharedGroup) tpr.accountActions = make(map[string]*Account) tpr.resProfiles = make(map[utils.TenantID]*utils.TPResourceProfile) + tpr.ipProfiles = make(map[utils.TenantID]*utils.TPIPProfile) tpr.sqProfiles = make(map[utils.TenantID]*utils.TPStatProfile) tpr.rgProfiles = make(map[utils.TenantID]*utils.TPRankingProfile) tpr.thProfiles = make(map[utils.TenantID]*utils.TPThresholdProfile) @@ -1117,6 +1119,26 @@ func (tpr *TpReader) LoadResourceProfiles() error { return tpr.LoadResourceProfilesFiltered("") } +func (tpr *TpReader) LoadIPProfilesFiltered(tag string) (err error) { + ips, err := tpr.lr.GetTPIPs(tpr.tpid, "", tag) + if err != nil { + return err + } + mapIPPfls := make(map[utils.TenantID]*utils.TPIPProfile) + for _, ip := range ips { + if err = verifyInlineFilterS(ip.FilterIDs); err != nil { + return + } + mapIPPfls[utils.TenantID{Tenant: ip.Tenant, ID: ip.ID}] = ip + } + tpr.ipProfiles = mapIPPfls + return nil +} + +func (tpr *TpReader) LoadIPProfiles() error { + return tpr.LoadIPProfilesFiltered("") +} + func (tpr *TpReader) LoadStatsFiltered(tag string) (err error) { tps, err := tpr.lr.GetTPStats(tpr.tpid, "", tag) if err != nil { @@ -1351,6 +1373,9 @@ func (tpr *TpReader) LoadAll() (err error) { if err = tpr.LoadResourceProfiles(); err != nil && err.Error() != utils.NotFoundCaps { return } + if err = tpr.LoadIPProfiles(); err != nil && err.Error() != utils.NotFoundCaps { + return + } if err = tpr.LoadStats(); err != nil && err.Error() != utils.NotFoundCaps { return } @@ -1596,6 +1621,25 @@ func (tpr *TpReader) WriteToDatabase(verbose, disableReverse bool) (err error) { loadIDs[utils.CacheResourceProfiles] = loadID loadIDs[utils.CacheResources] = loadID } + if verbose { + log.Print("IPProfiles:") + } + for _, tpIPp := range tpr.ipProfiles { + var ipp *IPProfile + if ipp, err = APItoIP(tpIPp, tpr.timezone); err != nil { + return + } + if err = tpr.dm.SetIPProfile(ipp, true); err != nil { + return + } + if verbose { + log.Print("\t", ipp.TenantID()) + } + } + if len(tpr.ipProfiles) != 0 { + loadIDs[utils.CacheIPProfiles] = loadID + loadIDs[utils.CacheIPs] = loadID + } if verbose { log.Print("StatQueueProfiles:") } @@ -1835,6 +1879,8 @@ func (tpr *TpReader) ShowStatistics() { log.Print("Account actions: ", len(tpr.accountActions)) // resource profiles log.Print("ResourceProfiles: ", len(tpr.resProfiles)) + // ip profiles + log.Print("IPProfiles: ", len(tpr.ipProfiles)) // stats log.Print("Stats: ", len(tpr.sqProfiles)) // thresholds @@ -1936,6 +1982,14 @@ func (tpr *TpReader) GetLoadedIds(categ string) ([]string, error) { i++ } return keys, nil + case utils.IPProfilesPrefix: + keys := make([]string, len(tpr.ipProfiles)) + i := 0 + for k := range tpr.ipProfiles { + keys[i] = k.TenantID() + i++ + } + return keys, nil case utils.ActionTriggerPrefix: keys := make([]string, len(tpr.actionsTriggers)) i := 0 @@ -2121,6 +2175,17 @@ func (tpr *TpReader) RemoveFromDatabase(verbose, disableReverse bool) (err error log.Print("\t", utils.ConcatenatedKey(tpRsp.Tenant, tpRsp.ID)) } } + if verbose { + log.Print("IPProfiles:") + } + for _, ipp := range tpr.ipProfiles { + if err = tpr.dm.RemoveIPProfile(ipp.Tenant, ipp.ID, true); err != nil { + return + } + if verbose { + log.Print("\t", utils.ConcatenatedKey(ipp.Tenant, ipp.ID)) + } + } if verbose { log.Print("StatQueueProfiles:") } @@ -2287,6 +2352,10 @@ func (tpr *TpReader) RemoveFromDatabase(verbose, disableReverse bool) (err error loadIDs[utils.CacheResourceProfiles] = loadID loadIDs[utils.CacheResources] = loadID } + if len(tpr.ipProfiles) != 0 { + loadIDs[utils.CacheIPProfiles] = loadID + loadIDs[utils.CacheIPs] = loadID + } if len(tpr.sqProfiles) != 0 { loadIDs[utils.CacheStatQueueProfiles] = loadID loadIDs[utils.CacheStatQueues] = loadID @@ -2337,6 +2406,7 @@ func (tpr *TpReader) ReloadCache(caching string, verbose bool, opts map[string]a aapIDs, _ := tpr.GetLoadedIds(utils.AccountActionPlansPrefix) shgIds, _ := tpr.GetLoadedIds(utils.SharedGroupPrefix) rspIDs, _ := tpr.GetLoadedIds(utils.ResourceProfilesPrefix) + ippIDs, _ := tpr.GetLoadedIds(utils.IPProfilesPrefix) aatIDs, _ := tpr.GetLoadedIds(utils.ActionTriggerPrefix) stqpIDs, _ := tpr.GetLoadedIds(utils.StatQueueProfilePrefix) sgIDS, _ := tpr.GetLoadedIds(utils.RankingsProfilePrefix) @@ -2362,6 +2432,8 @@ func (tpr *TpReader) ReloadCache(caching string, verbose bool, opts map[string]a utils.CacheSharedGroups: shgIds, utils.CacheResourceProfiles: rspIDs, utils.CacheResources: rspIDs, + utils.CacheIPProfiles: ippIDs, + utils.CacheIPs: ippIDs, utils.CacheActionTriggers: aatIDs, utils.CacheStatQueues: stqpIDs, utils.CacheStatQueueProfiles: stqpIDs, @@ -2393,6 +2465,9 @@ func (tpr *TpReader) ReloadCache(caching string, verbose bool, opts map[string]a if len(rspIDs) != 0 { cacheIDs = append(cacheIDs, utils.CacheResourceFilterIndexes) } + if len(ippIDs) != 0 { + cacheIDs = append(cacheIDs, utils.CacheIPFilterIndexes) + } if len(chargerIDs) != 0 { cacheIDs = append(cacheIDs, utils.CacheChargerFilterIndexes) } diff --git a/engine/tpreader_test.go b/engine/tpreader_test.go index bb2c85eb0..9939dfb15 100644 --- a/engine/tpreader_test.go +++ b/engine/tpreader_test.go @@ -937,6 +937,7 @@ func TestTPReaderReloadCache(t *testing.T) { RatingProfileIDs: []string{"RatingProfilesID"}, SharedGroupIDs: []string{"SharedGroupsID"}, ResourceProfileIDs: []string{"cgrates.org:resourceProfilesID"}, + IPProfileIDs: []string{"cgrates.org:ipProfilesID"}, StatsQueueProfileIDs: []string{"cgrates.org:statProfilesID"}, ThresholdProfileIDs: []string{"cgrates.org:thresholdProfilesID"}, FilterIDs: []string{"cgrates.org:filtersID"}, @@ -946,6 +947,7 @@ func TestTPReaderReloadCache(t *testing.T) { DispatcherProfileIDs: []string{"cgrates.org:dispatcherProfilesID"}, DispatcherHostIDs: []string{"cgrates.org:dispatcherHostsID"}, ResourceIDs: []string{"cgrates.org:resourceProfilesID"}, + IPIDs: []string{"cgrates.org:ipProfilesID"}, StatsQueueIDs: []string{"cgrates.org:statProfilesID"}, ThresholdIDs: []string{"cgrates.org:thresholdProfilesID"}, AccountActionPlanIDs: []string{"AccountActionPlansID"}, @@ -1003,6 +1005,9 @@ func TestTPReaderReloadCache(t *testing.T) { resProfiles: map[utils.TenantID]*utils.TPResourceProfile{ {Tenant: "cgrates.org", ID: "resourceProfilesID"}: {}, }, + ipProfiles: map[utils.TenantID]*utils.TPIPProfile{ + {Tenant: "cgrates.org", ID: "ipProfilesID"}: {}, + }, sqProfiles: map[utils.TenantID]*utils.TPStatProfile{ {Tenant: "cgrates.org", ID: "statProfilesID"}: {}, }, @@ -1138,6 +1143,9 @@ func TestTpReaderReloadScheduler(t *testing.T) { resProfiles: map[utils.TenantID]*utils.TPResourceProfile{ {Tenant: "cgrates.org", ID: "resourceProfilesID"}: {}, }, + ipProfiles: map[utils.TenantID]*utils.TPIPProfile{ + {Tenant: "cgrates.org", ID: "ipProfilesID"}: {}, + }, sqProfiles: map[utils.TenantID]*utils.TPStatProfile{ {Tenant: "cgrates.org", ID: "statProfilesID"}: {}, }, @@ -1395,6 +1403,9 @@ func TestTPCSVImporterErrs(t *testing.T) { if err := tpImp.importResources(fn); err == nil || err != utils.ErrNotFound { t.Error(err) } + if err := tpImp.importIPs(fn); err == nil || err != utils.ErrNotFound { + t.Error(err) + } if err := tpImp.importStats(fn); err == nil || err != utils.ErrNotFound { t.Error(err) } diff --git a/engine/version.go b/engine/version.go index aefc108c5..87e1b2b0c 100644 --- a/engine/version.go +++ b/engine/version.go @@ -160,6 +160,7 @@ func CurrentDataDBVersions() Versions { utils.Timing: 1, utils.RQF: 5, utils.Resource: 1, + utils.IP: 1, utils.Subscribers: 1, utils.Destinations: 1, utils.ReverseDestinations: 1, @@ -190,9 +191,11 @@ func CurrentStorDBVersions() Versions { utils.TpSharedGroups: 1, utils.TpRatingProfiles: 1, utils.TpResources: 1, + utils.TpIPs: 1, utils.TpRates: 1, utils.TpTiming: 1, utils.TpResource: 1, + utils.TpIP: 1, utils.TpDestinations: 1, utils.TpRatingPlan: 1, utils.TpRatingProfile: 1, diff --git a/engine/version_test.go b/engine/version_test.go index 4b3a69777..734fcb96b 100644 --- a/engine/version_test.go +++ b/engine/version_test.go @@ -91,7 +91,7 @@ func TestCurrentDBVersions(t *testing.T) { utils.StatS: 4, utils.Accounts: 3, utils.Actions: 2, utils.ActionTriggers: 2, utils.ActionPlans: 3, utils.SharedGroups: 2, utils.Thresholds: 4, utils.Routes: 2, utils.Attributes: 6, - utils.Timing: 1, utils.RQF: 5, utils.Resource: 1, + utils.Timing: 1, utils.RQF: 5, utils.Resource: 1, utils.IP: 1, utils.Subscribers: 1, utils.Destinations: 1, utils.ReverseDestinations: 1, utils.RatingPlan: 1, utils.RatingProfile: 1, utils.Chargers: 2, utils.Dispatchers: 2, utils.LoadIDsVrs: 1, @@ -102,9 +102,10 @@ func TestCurrentDBVersions(t *testing.T) { utils.TpActionTriggers: 1, utils.TpAccountActionsV: 1, utils.TpActionPlans: 1, utils.TpActions: 1, utils.TpThresholds: 1, utils.TpRoutes: 1, utils.TpStats: 1, utils.TpSharedGroups: 1, utils.TpRatingProfiles: 1, - utils.TpResources: 1, utils.TpRates: 1, utils.TpTiming: 1, - utils.TpResource: 1, utils.TpDestinations: 1, utils.TpRatingPlan: 1, - utils.TpRatingProfile: 1, utils.TpChargers: 1, utils.TpDispatchers: 1, + utils.TpResources: 1, utils.TpIPs: 1, utils.TpIP: 1, utils.TpRates: 1, + utils.TpTiming: 1, utils.TpResource: 1, utils.TpDestinations: 1, + utils.TpRatingPlan: 1, utils.TpRatingProfile: 1, utils.TpChargers: 1, + utils.TpDispatchers: 1, } if vrs := CurrentDBVersions(utils.MetaMongo, true); !reflect.DeepEqual(expVersDataDB, vrs) { t.Errorf("Expectred %+v, received %+v", expVersDataDB, vrs) diff --git a/general_tests/acntacts_test.go b/general_tests/acntacts_test.go index aa5b9cfb4..f9a755b1a 100644 --- a/general_tests/acntacts_test.go +++ b/general_tests/acntacts_test.go @@ -52,6 +52,7 @@ ENABLE_ACNT,*enable_account,,,,,,,,,,,,,false,false,10` actionTriggers := `` accountActions := `cgrates.org,1,TOPUP10_AT,,,` resLimits := `` + ips := `` stats := `` trends := `` rankings := `` @@ -63,7 +64,7 @@ ENABLE_ACNT,*enable_account,,,,,,,,,,,,,false,false,10` csvr, err := engine.NewTpReader(dbAcntActs.DataDB(), engine.NewStringCSVStorage(utils.CSVSep, destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles, sharedGroups, actions, actionPlans, actionTriggers, accountActions, - resLimits, stats, trends, rankings, thresholds, filters, suppliers, attrProfiles, chargerProfiles, ``, ""), "", "", nil, nil, false) + resLimits, ips, stats, trends, rankings, thresholds, filters, suppliers, attrProfiles, chargerProfiles, ``, ""), "", "", nil, nil, false) if err != nil { t.Error(err) } diff --git a/general_tests/auth_test.go b/general_tests/auth_test.go index 1bdc70e72..a16fba116 100644 --- a/general_tests/auth_test.go +++ b/general_tests/auth_test.go @@ -56,6 +56,7 @@ func TestAuthLoadCsvError(t *testing.T) { actionTriggers := `` accountActions := `` resLimits := `` + ips := `` stats := `` trends := `` rankings := `` @@ -66,7 +67,7 @@ func TestAuthLoadCsvError(t *testing.T) { chargerProfiles := `` csvr, err := engine.NewTpReader(dbAuth.DataDB(), engine.NewStringCSVStorage(utils.CSVSep, destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles, sharedGroups, actions, actionPlans, actionTriggers, accountActions, - resLimits, stats, trends, rankings, thresholds, filters, suppliers, attrProfiles, chargerProfiles, ``, ""), "", "", nil, nil, false) + resLimits, ips, stats, trends, rankings, thresholds, filters, suppliers, attrProfiles, chargerProfiles, ``, ""), "", "", nil, nil, false) if err != nil { t.Error(err) } @@ -92,6 +93,7 @@ cgrates.org,call,*any,2013-01-06T00:00:00Z,RP_ANY,` actionTriggers := `` accountActions := `cgrates.org,testauthpostpaid1,TOPUP10_AT,,,` resLimits := `` + ips := `` stats := `` trends := `` rankings := `` @@ -102,7 +104,7 @@ cgrates.org,call,*any,2013-01-06T00:00:00Z,RP_ANY,` chargerProfiles := `` csvr, err := engine.NewTpReader(dbAuth.DataDB(), engine.NewStringCSVStorage(utils.CSVSep, destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles, sharedGroups, actions, actionPlans, actionTriggers, accountActions, - resLimits, stats, trends, rankings, thresholds, filters, suppliers, attrProfiles, chargerProfiles, ``, ""), "", "", nil, nil, false) + resLimits, ips, stats, trends, rankings, thresholds, filters, suppliers, attrProfiles, chargerProfiles, ``, ""), "", "", nil, nil, false) if err != nil { t.Error(err) } diff --git a/general_tests/costs1_test.go b/general_tests/costs1_test.go index c9b91cd1c..92da2ceb9 100644 --- a/general_tests/costs1_test.go +++ b/general_tests/costs1_test.go @@ -58,12 +58,11 @@ cgrates.org,data,*any,2012-01-01T00:00:00Z,RP_DATA1, cgrates.org,sms,*any,2012-01-01T00:00:00Z,RP_SMS1,` csvr, err := engine.NewTpReader(dataDB.DataDB(), engine.NewStringCSVStorage(utils.CSVSep, dests, timings, rates, destinationRates, ratingPlans, ratingProfiles, utils.EmptyString, - utils.EmptyString, utils.EmptyString, utils.EmptyString, - utils.EmptyString, utils.EmptyString, utils.EmptyString, - utils.EmptyString, utils.EmptyString, utils.EmptyString, - utils.EmptyString, utils.EmptyString, utils.EmptyString, - utils.EmptyString, utils.EmptyString, utils.EmptyString), - utils.EmptyString, utils.EmptyString, nil, nil, false) + utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, + utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, + utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, + utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, + ), utils.EmptyString, utils.EmptyString, nil, nil, false) if err != nil { t.Error(err) } diff --git a/general_tests/datachrg1_test.go b/general_tests/datachrg1_test.go index 7aad4f397..e7c9f4eca 100644 --- a/general_tests/datachrg1_test.go +++ b/general_tests/datachrg1_test.go @@ -48,9 +48,11 @@ RP_DATA1,DR_DATA_2,TM2,10` ratingProfiles := `cgrates.org,data,*any,2012-01-01T00:00:00Z,RP_DATA1,` csvr, err := engine.NewTpReader(dataDB.DataDB(), engine.NewStringCSVStorage(utils.CSVSep, utils.EmptyString, timings, rates, destinationRates, ratingPlans, ratingProfiles, utils.EmptyString, - utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, - utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, - utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString), + utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, + utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, + utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, + utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, + ), utils.EmptyString, utils.EmptyString, nil, nil, false) if err != nil { t.Error(err) diff --git a/general_tests/ddazmbl1_test.go b/general_tests/ddazmbl1_test.go index fd284cac2..c64b0e023 100644 --- a/general_tests/ddazmbl1_test.go +++ b/general_tests/ddazmbl1_test.go @@ -60,6 +60,7 @@ TOPUP10_AT,TOPUP10_AC1,ASAP,10` actionTriggers := `` accountActions := `cgrates.org,12344,TOPUP10_AT,,,` resLimits := `` + ips := `` stats := `` trends := `` rankings := `` @@ -72,7 +73,7 @@ TOPUP10_AT,TOPUP10_AC1,ASAP,10` engine.NewStringCSVStorage(utils.CSVSep, destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles, sharedGroups, actions, actionPlans, actionTriggers, accountActions, - resLimits, stats, trends, rankings, thresholds, filters, suppliers, + resLimits, ips, stats, trends, rankings, thresholds, filters, suppliers, attrProfiles, chargerProfiles, ``, ""), "", "", nil, nil, false) if err != nil { t.Error(err) diff --git a/general_tests/ddazmbl2_test.go b/general_tests/ddazmbl2_test.go index 01680a239..f21fda059 100644 --- a/general_tests/ddazmbl2_test.go +++ b/general_tests/ddazmbl2_test.go @@ -60,6 +60,7 @@ TOPUP10_AT,TOPUP10_AC1,ASAP,10` actionTriggers := `` accountActions := `cgrates.org,12345,TOPUP10_AT,,,` resLimits := `` + ips := `` stats := `` trends := `` rankings := `` @@ -70,7 +71,7 @@ TOPUP10_AT,TOPUP10_AC1,ASAP,10` chargerProfiles := `` csvr, err := engine.NewTpReader(dataDB2.DataDB(), engine.NewStringCSVStorage(utils.CSVSep, destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles, sharedGroups, actions, actionPlans, - actionTriggers, accountActions, resLimits, + actionTriggers, accountActions, resLimits, ips, stats, trends, rankings, thresholds, filters, suppliers, attrProfiles, chargerProfiles, ``, ""), "", "", nil, nil, false) if err != nil { t.Error(err) diff --git a/general_tests/ddazmbl3_test.go b/general_tests/ddazmbl3_test.go index a1ba716d1..059ca4b4b 100644 --- a/general_tests/ddazmbl3_test.go +++ b/general_tests/ddazmbl3_test.go @@ -58,6 +58,7 @@ cgrates.org,call,discounted_minutes,2013-01-06T00:00:00Z,RP_UK_Mobile_BIG5_PKG,` actionTriggers := `` accountActions := `cgrates.org,12346,TOPUP10_AT,,,` resLimits := `` + ips := `` stats := `` trends := `` rankings := `` @@ -68,7 +69,7 @@ cgrates.org,call,discounted_minutes,2013-01-06T00:00:00Z,RP_UK_Mobile_BIG5_PKG,` chargerProfiles := `` csvr, err := engine.NewTpReader(dataDB3.DataDB(), engine.NewStringCSVStorage(utils.CSVSep, destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles, sharedGroups, actions, actionPlans, actionTriggers, - accountActions, resLimits, stats, trends, rankings, + accountActions, resLimits, ips, stats, trends, rankings, thresholds, filters, suppliers, attrProfiles, chargerProfiles, ``, ""), "", "", nil, nil, false) if err != nil { t.Error(err) diff --git a/general_tests/offline_internal_it_test.go b/general_tests/offline_internal_it_test.go index b85ab8042..3919dc327 100644 --- a/general_tests/offline_internal_it_test.go +++ b/general_tests/offline_internal_it_test.go @@ -293,8 +293,8 @@ func TestOfflineInternal(t *testing.T) { // run with sudo return nil }); err != nil { t.Error(err) - } else if dirs != 40 { - t.Errorf("expected <%d> directories, received <%d>", 40, dirs) + } else if dirs != 43 { + t.Errorf("expected <%d> directories, received <%d>", 43, dirs) } else if i > 6 && (files != 29 && files != 30) { // depends if rewriting is scheduled or not by the time we shutdown t.Errorf("expected 29 or 30 files, received <%d>", files) } else if i < 6 && files != 28 { @@ -316,8 +316,8 @@ func TestOfflineInternal(t *testing.T) { // run with sudo return nil }); err != nil { t.Error(err) - } else if dirs != 27 { - t.Errorf("expected <%d> directories, received <%d>", 27, dirs) + } else if dirs != 28 { + t.Errorf("expected <%d> directories, received <%d>", 28, dirs) } else if files != 1 { t.Errorf("expected <%d> files, received <%d>", 1, files) } diff --git a/general_tests/smschrg1_test.go b/general_tests/smschrg1_test.go index bea5f1be6..463522b7a 100644 --- a/general_tests/smschrg1_test.go +++ b/general_tests/smschrg1_test.go @@ -50,8 +50,8 @@ func TestSMSLoadCsvTpSmsChrg1(t *testing.T) { utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, - utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString), utils.EmptyString, - utils.EmptyString, nil, nil, false) + utils.EmptyString, utils.EmptyString, utils.EmptyString, utils.EmptyString, + utils.EmptyString), utils.EmptyString, utils.EmptyString, nil, nil, false) if err != nil { t.Error(err) } diff --git a/general_tests/tut_smgeneric_it_test.go b/general_tests/tut_smgeneric_it_test.go index d179abefe..089888cad 100644 --- a/general_tests/tut_smgeneric_it_test.go +++ b/general_tests/tut_smgeneric_it_test.go @@ -150,7 +150,7 @@ func testTutSMGCacheStats(t *testing.T) { expectedStats[utils.CacheAttributeProfiles].Items = 3 expectedStats[utils.MetaDefault].Items = 1 expectedStats[utils.CacheActionTriggers].Items = 1 - expectedStats[utils.CacheLoadIDs].Items = 35 + expectedStats[utils.CacheLoadIDs].Items = 38 expectedStats[utils.CacheChargerProfiles].Items = 1 expectedStats[utils.CacheRPCConnections].Items = 2 expectedStats[utils.CacheTimings].Items = 14 diff --git a/loaders/loader.go b/loaders/loader.go index 619203957..7d5db3fa7 100644 --- a/loaders/loader.go +++ b/loaders/loader.go @@ -349,6 +349,37 @@ func (ldr *Loader) storeLoadedData(loaderType string, cacheArgs[utils.CacheResources] = ids } } + case utils.MetaIPs: + cacheIDs = []string{utils.CacheIPFilterIndexes} + for _, lDataSet := range lds { + ipMdls := make(engine.IPMdls, len(lDataSet)) + for i, ld := range lDataSet { + ipMdls[i] = new(engine.IPMdl) + if err = utils.UpdateStructWithIfaceMap(ipMdls[i], ld); err != nil { + return + } + } + + for _, tpIP := range ipMdls.AsTPIPs() { + res, err := engine.APItoIP(tpIP, ldr.timezone) + if err != nil { + return err + } + if ldr.dryRun { + utils.Logger.Info( + fmt.Sprintf("<%s-%s> DRY_RUN: IPProfile: %s", + utils.LoaderS, ldr.ldrID, utils.ToJSON(res))) + continue + } + // get IDs so we can reload in cache + ids = append(ids, res.TenantID()) + if err := ldr.dm.SetIPProfile(res, true); err != nil { + return err + } + cacheArgs[utils.CacheIPProfiles] = ids + cacheArgs[utils.CacheIPs] = ids + } + } case utils.MetaFilters: for _, lDataSet := range lds { fltrModels := make(engine.FilterMdls, len(lDataSet)) diff --git a/loaders/loader_test.go b/loaders/loader_test.go index 65baa325e..742263287 100644 --- a/loaders/loader_test.go +++ b/loaders/loader_test.go @@ -1506,6 +1506,7 @@ func TestNewLoaderWithMultiFiles(t *testing.T) { "File1.csv": {}, "File2.csv": {}, utils.FiltersCsv: {}, + utils.IPsCsv: {}, utils.ResourcesCsv: {}, utils.RoutesCsv: {}, utils.StatsCsv: {}, diff --git a/migrator/migrator.go b/migrator/migrator.go index 9318d4223..c0d647f2a 100644 --- a/migrator/migrator.go +++ b/migrator/migrator.go @@ -180,6 +180,8 @@ func (m *Migrator) Migrate(taskIDs []string) (stats map[string]int, err error) { err = m.migrateTPratingprofiles() case utils.MetaTpResources: err = m.migrateTPresources() + case utils.MetaTpIPs: + err = m.migrateTPips() case utils.MetaTpRates: err = m.migrateTPrates() case utils.MetaTpTimings: diff --git a/migrator/tp_resources.go b/migrator/tp_resources.go index 5cc25cfa1..1b12c7d79 100644 --- a/migrator/tp_resources.go +++ b/migrator/tp_resources.go @@ -76,3 +76,21 @@ func (m *Migrator) migrateTPresources() (err error) { } return m.ensureIndexesStorDB(utils.TBLTPResources) } + +func (m *Migrator) migrateTPips() (err error) { + var vrs engine.Versions + current := engine.CurrentStorDBVersions() + if vrs, err = m.getVersions(utils.TpIPs); err != nil { + return + } + switch vrs[utils.TpIPs] { + case current[utils.TpIPs]: + if m.sameStorDB { + break + } + if err := m.migrateCurrentTPresources(); err != nil { + return err + } + } + return m.ensureIndexesStorDB(utils.TBLTPIPs) +} diff --git a/services/datadb_it_test.go b/services/datadb_it_test.go index 219717023..8a5472573 100644 --- a/services/datadb_it_test.go +++ b/services/datadb_it_test.go @@ -123,8 +123,10 @@ func TestDataDBReload(t *testing.T) { utils.MetaSharedGroups: {Limit: -1}, utils.MetaTimings: {Limit: -1}, utils.MetaResourceProfile: {Limit: -1}, + utils.MetaIPProfiles: {Limit: -1}, utils.MetaStatQueues: {Limit: -1}, utils.MetaResources: {Limit: -1}, + utils.MetaIPs: {Limit: -1}, utils.MetaStatQueueProfiles: {Limit: -1}, utils.MetaRankings: {Limit: -1}, utils.MetaRankingProfiles: {Limit: -1}, @@ -143,6 +145,7 @@ func TestDataDBReload(t *testing.T) { utils.CacheVersions: {Limit: -1}, utils.CacheResourceFilterIndexes: {Limit: -1}, + utils.CacheIPFilterIndexes: {Limit: -1}, utils.CacheStatFilterIndexes: {Limit: -1}, utils.CacheThresholdFilterIndexes: {Limit: -1}, utils.CacheRouteFilterIndexes: {Limit: -1}, diff --git a/services/ips.go b/services/ips.go new file mode 100644 index 000000000..9be6cb811 --- /dev/null +++ b/services/ips.go @@ -0,0 +1,139 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package services + +import ( + "fmt" + "sync" + + "github.com/cgrates/birpc" + v1 "github.com/cgrates/cgrates/apier/v1" + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/cores" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/servmanager" + "github.com/cgrates/cgrates/utils" +) + +// NewIPService returns the IP Service. +func NewIPService(cfg *config.CGRConfig, dm *DataDBService, + cache *engine.CacheS, fsChan chan *engine.FilterS, + server *cores.Server, intIPsChan chan birpc.ClientConnector, + cm *engine.ConnManager, anz *AnalyzerService, + srvDep map[string]*sync.WaitGroup) servmanager.Service { + return &IPService{ + connChan: intIPsChan, + cfg: cfg, + dbs: dm, + cache: cache, + fsChan: fsChan, + server: server, + cm: cm, + anz: anz, + srvDep: srvDep, + } +} + +// IPService implements Service interface. +type IPService struct { + mu sync.RWMutex + cfg *config.CGRConfig + cm *engine.ConnManager + + dbs *DataDBService + cache *engine.CacheS + fsChan chan *engine.FilterS + + ips *engine.IPService + server *cores.Server + connChan chan birpc.ClientConnector + anz *AnalyzerService + + srvDep map[string]*sync.WaitGroup +} + +// Start should handle the sercive start +func (s *IPService) Start() error { + if s.IsRunning() { + return utils.ErrServiceAlreadyRunning + } + s.srvDep[utils.DataDB].Add(1) + <-s.cache.GetPrecacheChannel(utils.CacheIPProfiles) + <-s.cache.GetPrecacheChannel(utils.CacheIPs) + <-s.cache.GetPrecacheChannel(utils.CacheIPFilterIndexes) + + fltrs := <-s.fsChan + s.fsChan <- fltrs + dmChan := s.dbs.GetDMChan() + dm := <-dmChan + dmChan <- dm + + utils.Logger.Info(fmt.Sprintf("<%s> starting <%s> subsystem", + utils.CoreS, utils.IPs)) + s.mu.Lock() + defer s.mu.Unlock() + + s.ips = engine.NewIPService(dm, s.cfg, fltrs, s.cm) + s.ips.StartLoop() + srv, err := engine.NewService(v1.NewIPsV1(s.ips)) + if err != nil { + return err + } + if !s.cfg.DispatcherSCfg().Enabled { + s.server.RpcRegister(srv) + } + s.connChan <- s.anz.GetInternalCodec(srv, utils.IPs) + return nil +} + +// Reload handles configuration changes. +func (s *IPService) Reload() error { + s.mu.Lock() + s.ips.Reload() + s.mu.Unlock() + return nil +} + +// Shutdown stops the service. +func (s *IPService) Shutdown() error { + defer s.srvDep[utils.DataDB].Done() + s.mu.Lock() + defer s.mu.Unlock() + s.ips.Shutdown() + s.ips = nil + <-s.connChan + return nil +} + +// ServiceName returns the service name. +func (s *IPService) ServiceName() string { + return utils.IPs +} + +// ShouldRun returns if the service should be running. +func (s *IPService) ShouldRun() bool { + return s.cfg.IPsCfg().Enabled +} + +// IsRunning checks whether the service is running. +func (s *IPService) IsRunning() bool { + s.mu.RLock() + defer s.mu.RUnlock() + return s.ips != nil +} diff --git a/servmanager/servmanager.go b/servmanager/servmanager.go index ff3bf1fc4..42bcdda6e 100644 --- a/servmanager/servmanager.go +++ b/servmanager/servmanager.go @@ -228,6 +228,8 @@ func (srvMngr *ServiceManager) handleReload() { go srvMngr.reloadService(utils.CoreS) case <-srvMngr.GetConfig().GetReloadChan(config.JanusAgentJson): go srvMngr.reloadService(utils.JanusAgent) + case <-srvMngr.GetConfig().GetReloadChan(config.IPsJSON): + // go srvMngr.reloadService(utils.IPs) } // handle RPC server } diff --git a/utils/apitpdata.go b/utils/apitpdata.go index abe3b4a5f..71b4cbf31 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -1422,6 +1422,43 @@ func (trp *TPResourceProfile) CacheClone() any { return trp.Clone() } +// TPIPProfile is used in APIs to manage remotely offline IPProfile +type TPIPProfile struct { + TPid string + Tenant string + ID string + FilterIDs []string + ActivationInterval *TPActivationInterval + TTL string + Type string + AddressPool string + Allocation string + Stored bool + Weight float64 +} + +// Clone method for TPIPProfile +func (tp *TPIPProfile) Clone() *TPIPProfile { + if tp == nil { + return nil + } + return &TPIPProfile{ + TPid: tp.TPid, + Tenant: tp.Tenant, + ID: tp.ID, + FilterIDs: slices.Clone(tp.FilterIDs), + ActivationInterval: tp.ActivationInterval.Clone(), + TTL: tp.TTL, + Stored: tp.Stored, + Weight: tp.Weight, + } +} + +// CacheClone returns a clone of TPIPProfile used by ltcache CacheCloner +func (tp *TPIPProfile) CacheClone() any { + return tp.Clone() +} + // TPActivationInterval represents an activation interval for an item type TPActivationInterval struct { ActivationTime string @@ -2312,6 +2349,8 @@ func NewAttrReloadCacheWithOpts() *AttrReloadCacheWithAPIOpts { SharedGroupIDs: []string{MetaAny}, ResourceProfileIDs: []string{MetaAny}, ResourceIDs: []string{MetaAny}, + IPProfileIDs: []string{MetaAny}, + IPIDs: []string{MetaAny}, StatsQueueIDs: []string{MetaAny}, StatsQueueProfileIDs: []string{MetaAny}, RankingIDs: []string{MetaAny}, @@ -2329,6 +2368,7 @@ func NewAttrReloadCacheWithOpts() *AttrReloadCacheWithAPIOpts { TimingIDs: []string{MetaAny}, AttributeFilterIndexIDs: []string{MetaAny}, ResourceFilterIndexIDs: []string{MetaAny}, + IPFilterIndexIDs: []string{MetaAny}, StatFilterIndexIDs: []string{MetaAny}, ThresholdFilterIndexIDs: []string{MetaAny}, RouteFilterIndexIDs: []string{MetaAny}, @@ -2355,6 +2395,8 @@ func NewAttrReloadCacheWithOptsFromMap(arg map[string][]string, tnt string, opts SharedGroupIDs: arg[CacheSharedGroups], ResourceProfileIDs: arg[CacheResourceProfiles], ResourceIDs: arg[CacheResources], + IPProfileIDs: arg[CacheIPProfiles], + IPIDs: arg[CacheIPs], StatsQueueIDs: arg[CacheStatQueues], StatsQueueProfileIDs: arg[CacheStatQueueProfiles], RankingIDs: arg[CacheRankings], @@ -2373,6 +2415,7 @@ func NewAttrReloadCacheWithOptsFromMap(arg map[string][]string, tnt string, opts TimingIDs: arg[CacheTimings], AttributeFilterIndexIDs: arg[CacheAttributeFilterIndexes], ResourceFilterIndexIDs: arg[CacheResourceFilterIndexes], + IPFilterIndexIDs: arg[CacheIPFilterIndexes], StatFilterIndexIDs: arg[CacheStatFilterIndexes], ThresholdFilterIndexIDs: arg[CacheThresholdFilterIndexes], RouteFilterIndexIDs: arg[CacheRouteFilterIndexes], @@ -2396,6 +2439,8 @@ type AttrReloadCacheWithAPIOpts struct { SharedGroupIDs []string `json:",omitempty"` ResourceProfileIDs []string `json:",omitempty"` ResourceIDs []string `json:",omitempty"` + IPProfileIDs []string `json:",omitempty"` + IPIDs []string `json:",omitempty"` StatsQueueIDs []string `json:",omitempty"` StatsQueueProfileIDs []string `json:",omitempty"` RankingIDs []string `json:",omitempty"` @@ -2414,6 +2459,7 @@ type AttrReloadCacheWithAPIOpts struct { TimingIDs []string `json:",omitempty"` AttributeFilterIndexIDs []string `json:",omitempty"` ResourceFilterIndexIDs []string `json:",omitempty"` + IPFilterIndexIDs []string `json:",omitempty"` StatFilterIndexIDs []string `json:",omitempty"` ThresholdFilterIndexIDs []string `json:",omitempty"` RouteFilterIndexIDs []string `json:",omitempty"` @@ -2435,6 +2481,8 @@ func (a *AttrReloadCacheWithAPIOpts) Map() map[string][]string { CacheSharedGroups: a.SharedGroupIDs, CacheResourceProfiles: a.ResourceProfileIDs, CacheResources: a.ResourceIDs, + CacheIPProfiles: a.IPProfileIDs, + CacheIPs: a.IPIDs, CacheStatQueues: a.StatsQueueIDs, CacheStatQueueProfiles: a.StatsQueueProfileIDs, CacheThresholds: a.ThresholdIDs, @@ -2453,6 +2501,7 @@ func (a *AttrReloadCacheWithAPIOpts) Map() map[string][]string { CacheTimings: a.TimingIDs, CacheAttributeFilterIndexes: a.AttributeFilterIndexIDs, CacheResourceFilterIndexes: a.ResourceFilterIndexIDs, + CacheIPFilterIndexes: a.IPFilterIndexIDs, CacheStatFilterIndexes: a.StatFilterIndexIDs, CacheThresholdFilterIndexes: a.ThresholdFilterIndexIDs, CacheRouteFilterIndexes: a.RouteFilterIndexIDs, diff --git a/utils/apitpdata_test.go b/utils/apitpdata_test.go index 274977b11..934a5c4d3 100644 --- a/utils/apitpdata_test.go +++ b/utils/apitpdata_test.go @@ -977,6 +977,8 @@ func TestNewAttrReloadCacheWithOpts(t *testing.T) { SharedGroupIDs: []string{MetaAny}, ResourceProfileIDs: []string{MetaAny}, ResourceIDs: []string{MetaAny}, + IPProfileIDs: []string{MetaAny}, + IPIDs: []string{MetaAny}, StatsQueueIDs: []string{MetaAny}, StatsQueueProfileIDs: []string{MetaAny}, ThresholdIDs: []string{MetaAny}, @@ -993,6 +995,7 @@ func TestNewAttrReloadCacheWithOpts(t *testing.T) { TimingIDs: []string{MetaAny}, AttributeFilterIndexIDs: []string{MetaAny}, ResourceFilterIndexIDs: []string{MetaAny}, + IPFilterIndexIDs: []string{MetaAny}, StatFilterIndexIDs: []string{MetaAny}, ThresholdFilterIndexIDs: []string{MetaAny}, RouteFilterIndexIDs: []string{MetaAny}, diff --git a/utils/consts.go b/utils/consts.go index 3fae3739c..339c0e651 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -36,20 +36,31 @@ var ( CacheCDRIDs, CacheRPCConnections, CacheUCH, CacheSTIR, CacheEventCharges, MetaAPIBan, MetaSentryPeer, CacheRatingProfilesTmp, CacheCapsEvents, CacheReplicationHosts}) - DataDBPartitions = NewStringSet([]string{CacheDestinations, CacheReverseDestinations, CacheRatingPlans, - CacheRatingProfiles, CacheDispatcherProfiles, CacheDispatcherHosts, CacheChargerProfiles, CacheActions, CacheActionTriggers, CacheSharedGroups, CacheTimings, - CacheResourceProfiles, CacheResources, CacheEventResources, CacheStatQueueProfiles, CacheRankingProfiles, CacheRankings, CacheStatQueues, - CacheThresholdProfiles, CacheThresholds, CacheFilters, CacheRouteProfiles, CacheAttributeProfiles, CacheTrendProfiles, CacheTrends, - CacheResourceFilterIndexes, CacheStatFilterIndexes, CacheThresholdFilterIndexes, CacheRouteFilterIndexes, - CacheAttributeFilterIndexes, CacheChargerFilterIndexes, CacheDispatcherFilterIndexes, CacheLoadIDs, - CacheReverseFilterIndexes, CacheActionPlans, CacheAccountActionPlans, CacheAccounts, CacheVersions}) + DataDBPartitions = NewStringSet([]string{ + CacheDestinations, CacheReverseDestinations, CacheRatingPlans, + CacheRatingProfiles, CacheDispatcherProfiles, CacheDispatcherHosts, + CacheChargerProfiles, CacheActions, CacheActionTriggers, CacheSharedGroups, + CacheTimings, CacheResourceProfiles, CacheResources, CacheEventResources, + CacheIPProfiles, CacheIPs, CacheEventIPs, CacheStatQueueProfiles, + CacheRankingProfiles, CacheRankings, CacheStatQueues, CacheThresholdProfiles, + CacheThresholds, CacheFilters, CacheRouteProfiles, CacheAttributeProfiles, + CacheTrendProfiles, CacheTrends, CacheResourceFilterIndexes, CacheIPFilterIndexes, + CacheStatFilterIndexes, CacheThresholdFilterIndexes, CacheRouteFilterIndexes, + CacheAttributeFilterIndexes, CacheChargerFilterIndexes, + CacheDispatcherFilterIndexes, CacheLoadIDs, CacheReverseFilterIndexes, + CacheActionPlans, CacheAccountActionPlans, CacheAccounts, CacheVersions, + }) - StorDBPartitions = NewStringSet([]string{CacheTBLTPTimings, CacheTBLTPDestinations, CacheTBLTPRates, CacheTBLTPDestinationRates, - CacheTBLTPRatingPlans, CacheTBLTPRatingProfiles, CacheTBLTPSharedGroups, CacheTBLTPActions, - CacheTBLTPActionPlans, CacheTBLTPActionTriggers, CacheTBLTPAccountActions, CacheTBLTPResources, - CacheTBLTPStats, CacheTBLTPThresholds, CacheTBLTPRankings, CacheTBLTPFilters, CacheSessionCostsTBL, CacheCDRsTBL, - CacheTBLTPRoutes, CacheTBLTPAttributes, CacheTBLTPChargers, CacheTBLTPDispatchers, - CacheTBLTPDispatcherHosts, CacheVersions}) + StorDBPartitions = NewStringSet([]string{ + CacheTBLTPTimings, CacheTBLTPDestinations, CacheTBLTPRates, + CacheTBLTPDestinationRates, CacheTBLTPRatingPlans, CacheTBLTPRatingProfiles, + CacheTBLTPSharedGroups, CacheTBLTPActions, CacheTBLTPActionPlans, + CacheTBLTPActionTriggers, CacheTBLTPAccountActions, CacheTBLTPResources, + CacheTBLTPIPs, CacheTBLTPStats, CacheTBLTPThresholds, CacheTBLTPRankings, + CacheTBLTPFilters, CacheSessionCostsTBL, CacheCDRsTBL, CacheTBLTPRoutes, + CacheTBLTPAttributes, CacheTBLTPChargers, CacheTBLTPDispatchers, + CacheTBLTPDispatcherHosts, CacheVersions, + }) // CachePartitions enables creation of cache partitions CachePartitions = JoinStringSet(extraDBPartition, DataDBPartitions) @@ -66,6 +77,8 @@ var ( CacheSharedGroups: SharedGroupPrefix, CacheResourceProfiles: ResourceProfilesPrefix, CacheResources: ResourcesPrefix, + CacheIPProfiles: IPProfilesPrefix, + CacheIPs: IPsPrefix, CacheTimings: TimingsPrefix, CacheStatQueueProfiles: StatQueueProfilePrefix, CacheStatQueues: StatQueuePrefix, @@ -82,6 +95,7 @@ var ( CacheDispatcherProfiles: DispatcherProfilePrefix, CacheDispatcherHosts: DispatcherHostPrefix, CacheResourceFilterIndexes: ResourceFilterIndexes, + CacheIPFilterIndexes: IPFilterIndexes, CacheStatFilterIndexes: StatFilterIndexes, CacheThresholdFilterIndexes: ThresholdFilterIndexes, CacheRouteFilterIndexes: RouteFilterIndexes, @@ -100,6 +114,7 @@ var ( CacheIndexesToPrefix = map[string]string{ // used by match index to get all the ids when index selects is disabled and for compute indexes CacheThresholdFilterIndexes: ThresholdProfilePrefix, CacheResourceFilterIndexes: ResourceProfilesPrefix, + CacheIPFilterIndexes: IPProfilesPrefix, CacheStatFilterIndexes: StatQueueProfilePrefix, CacheRouteFilterIndexes: RouteProfilePrefix, CacheAttributeFilterIndexes: AttributeProfilePrefix, @@ -111,6 +126,7 @@ var ( CacheInstanceToCacheIndex = map[string]string{ CacheThresholdProfiles: CacheThresholdFilterIndexes, CacheResourceProfiles: CacheResourceFilterIndexes, + CacheIPProfiles: CacheIPFilterIndexes, CacheStatQueueProfiles: CacheStatFilterIndexes, CacheRouteProfiles: CacheRouteFilterIndexes, CacheAttributeProfiles: CacheAttributeFilterIndexes, @@ -138,6 +154,7 @@ var ( TBLTPActionTriggers: CacheTBLTPActionTriggers, TBLTPAccountActions: CacheTBLTPAccountActions, TBLTPResources: CacheTBLTPResources, + TBLTPIPs: CacheTBLTPIPs, TBLTPStats: CacheTBLTPStats, TBLTPTrends: CacheTBLTPTrends, TBLTPRankings: CacheTBLTPRankings, @@ -295,6 +312,8 @@ const ( UsersPrefix = "usr_" ResourcesPrefix = "res_" ResourceProfilesPrefix = "rsp_" + IPsPrefix = "ips_" + IPProfilesPrefix = "ipp_" ThresholdPrefix = "thd_" TrendPrefix = "trd_" RankingPrefix = "rnk_" @@ -481,7 +500,6 @@ const ( ID = "ID" UniqueID = "UniqueID" Address = "Address" - Addresses = "Addresses" Transport = "Transport" TLS = "TLS" Subsystems = "Subsystems" @@ -552,6 +570,7 @@ const ( Timing = "Timing" RQF = "RQF" Resource = "Resource" + IP = "IP" User = "User" Subscribers = "Subscribers" DerivedChargersV = "DerivedChargers" @@ -577,6 +596,8 @@ const ( UsageTTL = "UsageTTL" AllocationMessage = "AllocationMessage" Stored = "Stored" + AddressPool = "AddressPool" + Allocation = "Allocation" RatingSubject = "RatingSubject" Categories = "Categories" Blocker = "Blocker" @@ -1057,6 +1078,7 @@ const ( MetaActionTriggers = "*action_triggers" MetaActions = "*actions" MetaResourceProfile = "*resource_profiles" + MetaIPProfiles = "*ip_profiles" MetaStatQueueProfiles = "*statqueue_profiles" MetaStatQueues = "*statqueues" MetaRankingProfiles = "*ranking_profiles" @@ -1070,6 +1092,8 @@ const ( MetaThresholds = "*thresholds" MetaRoutes = "*routes" MetaAttributes = "*attributes" + MetaResources = "*resources" + MetaIPs = "*ips" MetaSessionsBackup = "*sessions_backup" MetaLoadIDs = "*load_ids" MetaNodeID = "*node_id" @@ -1122,6 +1146,7 @@ const ( TrendS = "TrendS" RankingS = "RankingS" ThresholdS = "ThresholdS" + IPs = "IPs" ) // Lower service names @@ -1217,6 +1242,7 @@ const ( MetaTpSharedGroups = "*tp_shared_groups" MetaTpRatingProfiles = "*tp_rating_profiles" MetaTpResources = "*tp_resources" + MetaTpIPs = "*tp_ips" MetaTpRates = "*tp_rates" MetaTpTimings = "*tp_timings" MetaTpDestinations = "*tp_destinations" @@ -1257,9 +1283,11 @@ const ( TpSharedGroups = "TpSharedGroups" TpRatingProfiles = "TpRatingProfiles" TpResources = "TpResources" + TpIPs = "TpIPs" TpRates = "TpRates" TpTiming = "TpTiming" TpResource = "TpResource" + TpIP = "TpIP" TpDestinations = "TpDestinations" TpRatingPlan = "TpRatingPlan" TpRatingProfile = "TpRatingProfile" @@ -1285,6 +1313,7 @@ const ( AttributeSv1 = "AttributeSv1" SessionSv1 = "SessionSv1" ChargerSv1 = "ChargerSv1" + IPsV1 = "IPsV1" MetaAuth = "*auth" APIMethods = "APIMethods" NestingSep = "." @@ -1307,7 +1336,6 @@ const ( MetaLessOrEqual = "*lte" MetaGreaterThan = "*gt" MetaGreaterOrEqual = "*gte" - MetaResources = "*resources" MetaEqual = "*eq" MetaIPNet = "*ipnet" MetaAPIBan = "*apiban" @@ -1360,6 +1388,8 @@ const ( ReplicatorSv1GetTiming = "ReplicatorSv1.GetTiming" ReplicatorSv1GetResource = "ReplicatorSv1.GetResource" ReplicatorSv1GetResourceProfile = "ReplicatorSv1.GetResourceProfile" + ReplicatorSv1GetIP = "ReplicatorSv1.GetIP" + ReplicatorSv1GetIPProfile = "ReplicatorSv1.GetIPProfile" ReplicatorSv1GetActionTriggers = "ReplicatorSv1.GetActionTriggers" ReplicatorSv1GetSharedGroup = "ReplicatorSv1.GetSharedGroup" ReplicatorSv1GetActions = "ReplicatorSv1.GetActions" @@ -1389,6 +1419,8 @@ const ( ReplicatorSv1SetTiming = "ReplicatorSv1.SetTiming" ReplicatorSv1SetResource = "ReplicatorSv1.SetResource" ReplicatorSv1SetResourceProfile = "ReplicatorSv1.SetResourceProfile" + ReplicatorSv1SetIP = "ReplicatorSv1.SetIP" + ReplicatorSv1SetIPProfile = "ReplicatorSv1.SetIPProfile" ReplicatorSv1SetActionTriggers = "ReplicatorSv1.SetActionTriggers" ReplicatorSv1SetSharedGroup = "ReplicatorSv1.SetSharedGroup" ReplicatorSv1SetActions = "ReplicatorSv1.SetActions" @@ -1418,6 +1450,8 @@ const ( ReplicatorSv1RemoveTiming = "ReplicatorSv1.RemoveTiming" ReplicatorSv1RemoveResource = "ReplicatorSv1.RemoveResource" ReplicatorSv1RemoveResourceProfile = "ReplicatorSv1.RemoveResourceProfile" + ReplicatorSv1RemoveIP = "ReplicatorSv1.RemoveIP" + ReplicatorSv1RemoveIPProfile = "ReplicatorSv1.RemoveIPProfile" ReplicatorSv1RemoveActionTriggers = "ReplicatorSv1.RemoveActionTriggers" ReplicatorSv1RemoveSharedGroup = "ReplicatorSv1.RemoveSharedGroup" ReplicatorSv1RemoveActions = "ReplicatorSv1.RemoveActions" @@ -1447,6 +1481,7 @@ const ( APIerSv1GetReverseFilterHealth = "APIerSv1.GetReverseFilterHealth" APIerSv1GetThresholdsIndexesHealth = "APIerSv1.GetThresholdsIndexesHealth" APIerSv1GetResourcesIndexesHealth = "APIerSv1.GetResourcesIndexesHealth" + APIerSv1GetIPsIndexesHealth = "APIerSv1.GetIPsIndexesHealth" APIerSv1GetStatsIndexesHealth = "APIerSv1.GetStatsIndexesHealth" APIerSv1GetRoutesIndexesHealth = "APIerSv1.GetRoutesIndexesHealth" APIerSv1GetChargersIndexesHealth = "APIerSv1.GetChargersIndexesHealth" @@ -1533,6 +1568,9 @@ const ( APIerSv1GetTPResource = "APIerSv1.GetTPResource" APIerSv1SetTPResource = "APIerSv1.SetTPResource" APIerSv1RemoveTPResource = "APIerSv1.RemoveTPResource" + APIerSv1GetTPIP = "APIerSv1.GetTPIP" + APIerSv1SetTPIP = "APIerSv1.SetTPIP" + APIerSv1RemoveTPIP = "APIerSv1.RemoveTPIP" APIerSv1SetTPRate = "APIerSv1.SetTPRate" APIerSv1GetTPRate = "APIerSv1.GetTPRate" APIerSv1RemoveTPRate = "APIerSv1.RemoveTPRate" @@ -1813,6 +1851,20 @@ const ( APIerSv1GetResourceProfileIDs = "APIerSv1.GetResourceProfileIDs" ) +// IPs APIs +const ( + IPsV1AuthorizeIPs = "IPsV1.AuthorizeIPs" + IPsV1GetIPsForEvent = "IPsV1.GetIPsForEvent" + IPsV1AllocateIPs = "IPsV1.AllocateIPs" + IPsV1ReleaseIPs = "IPsV1.ReleaseIPs" + IPsV1Ping = "IPsV1.Ping" + IPsV1GetIP = "IPsV1.GetIP" + APIerSv1SetIPProfile = "APIerSv1.SetIPProfile" + APIerSv1RemoveIPProfile = "APIerSv1.RemoveIPProfile" + APIerSv1GetIPProfile = "APIerSv1.GetIPProfile" + APIerSv1GetIPProfileIDs = "APIerSv1.GetIPProfileIDs" +) + // SessionS APIs const ( SessionSv1AuthorizeEvent = "SessionSv1.AuthorizeEvent" @@ -2012,6 +2064,7 @@ const ( ActionTriggersCsv = "ActionTriggers.csv" AccountActionsCsv = "AccountActions.csv" ResourcesCsv = "Resources.csv" + IPsCsv = "IPs.csv" StatsCsv = "Stats.csv" TrendsCsv = "Trends.csv" RankingsCsv = "Rankings.csv" @@ -2038,6 +2091,7 @@ const ( TBLTPActionTriggers = "tp_action_triggers" TBLTPAccountActions = "tp_account_actions" TBLTPResources = "tp_resources" + TBLTPIPs = "tp_ips" TBLTPStats = "tp_stats" TBLTPRankings = "tp_rankings" TBLTPTrends = "tp_trends" @@ -2067,8 +2121,11 @@ const ( CacheSharedGroups = "*shared_groups" CacheResources = "*resources" CacheResourceProfiles = "*resource_profiles" + CacheIPs = "*ips" + CacheIPProfiles = "*ip_profiles" CacheTimings = "*timings" CacheEventResources = "*event_resources" + CacheEventIPs = "*event_ips" CacheStatQueueProfiles = "*statqueue_profiles" CacheStatQueues = "*statqueues" CacheRankingProfiles = "*ranking_profiles" @@ -2087,6 +2144,7 @@ const ( CacheDispatcherRoutes = "*dispatcher_routes" CacheDispatcherLoads = "*dispatcher_loads" CacheResourceFilterIndexes = "*resource_filter_indexes" + CacheIPFilterIndexes = "*ip_filter_indexes" CacheStatFilterIndexes = "*stat_filter_indexes" CacheThresholdFilterIndexes = "*threshold_filter_indexes" CacheRankingFilterIndexes = "ranking_filter_indexes" @@ -2127,6 +2185,7 @@ const ( CacheTBLTPActionTriggers = "*tp_action_triggers" CacheTBLTPAccountActions = "*tp_account_actions" CacheTBLTPResources = "*tp_resources" + CacheTBLTPIPs = "*tp_ips" CacheTBLTPStats = "*tp_stats" CacheTBLTPTrends = "*tp_trends" CacheTBLTPRankings = "*tp_rankings" @@ -2144,6 +2203,7 @@ const ( // Prefix for indexing const ( ResourceFilterIndexes = "rfi_" + IPFilterIndexes = "ifi_" StatFilterIndexes = "sfi_" ThresholdFilterIndexes = "tfi_" AttributeFilterIndexes = "afi_" @@ -2553,6 +2613,9 @@ const ( MetaUsageTTLCfg = "*usageTTL" MetaUnitsCfg = "*units" + // IPsCfg + MetaTTLCfg = "*ttl" + // RoutesCfg MetaProfileCountCfg = "*profileCount" MetaIgnoreErrorsCfg = "*ignoreErrors" @@ -2756,8 +2819,8 @@ var CGROptionsSet = NewStringSet([]string{OptsSessionsTTL, OptsRoutesProfileCount, OptsDispatchersProfilesCount, OptsAttributesProfileRuns, OptsAttributesProfileIgnoreFilters, OptsStatsProfileIDs, OptsStatsProfileIgnoreFilters, OptsThresholdsProfileIDs, OptsThresholdsProfileIgnoreFilters, OptsResourcesUsageID, OptsResourcesUsageTTL, - OptsResourcesUnits, OptsAttributeS, OptsThresholdS, OptsChargerS, OptsStatS, OptsRALs, OptsRerate, - OptsRefund, MetaAccountID}) + OptsResourcesUnits, OptsIPsUsageID, OptsIPsTTL, OptsIPsUnits, OptsAttributeS, OptsThresholdS, OptsChargerS, + OptsStatS, OptsRALs, OptsRerate, OptsRefund, MetaAccountID}) // EventExporter metrics const ( @@ -2806,6 +2869,10 @@ const ( OptsResourcesUsageID = "*rsUsageID" OptsResourcesUsageTTL = "*rsUsageTTL" OptsResourcesUnits = "*rsUnits" + // IPs + OptsIPsUsageID = "*ipUsageID" + OptsIPsTTL = "*ipTTL" + OptsIPsUnits = "*ipUnits" // Routes OptsRoutesProfileCount = "*rouProfileCount" OptsRoutesLimit = "*rouLimit" diff --git a/utils/errors.go b/utils/errors.go index 0813f87b4..5c6185f6e 100644 --- a/utils/errors.go +++ b/utils/errors.go @@ -48,6 +48,8 @@ var ( ErrNotConvertible = errors.New("NOT_CONVERTIBLE") ErrResourceUnavailable = errors.New("RESOURCE_UNAVAILABLE") ErrResourceUnauthorized = errors.New("RESOURCE_UNAUTHORIZED") + ErrIPUnavailable = errors.New("IP_UNAVAILABLE") + ErrIPUnauthorized = errors.New("IP_UNAUTHORIZED") ErrNoActiveSession = errors.New("NO_ACTIVE_SESSION") ErrPartiallyExecuted = errors.New("PARTIALLY_EXECUTED") ErrMaxUsageExceeded = errors.New("MAX_USAGE_EXCEEDED") @@ -105,6 +107,8 @@ var ( ErrNotConvertible.Error(): ErrNotConvertible, ErrResourceUnavailable.Error(): ErrResourceUnavailable, ErrResourceUnauthorized.Error(): ErrResourceUnauthorized, + ErrIPUnavailable.Error(): ErrIPUnavailable, + ErrIPUnauthorized.Error(): ErrIPUnauthorized, ErrNoActiveSession.Error(): ErrNoActiveSession, ErrPartiallyExecuted.Error(): ErrPartiallyExecuted, ErrMaxUsageExceeded.Error(): ErrMaxUsageExceeded,