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,