diff --git a/apier/v1/api_interfaces.go b/apier/v1/api_interfaces.go
index 6bf908ee7..a52c45269 100644
--- a/apier/v1/api_interfaces.go
+++ b/apier/v1/api_interfaces.go
@@ -56,11 +56,12 @@ type ResourceSv1Interface interface {
}
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
+ GetIPAllocationForEvent(*context.Context, *utils.CGREvent, *engine.IPAllocations) error
+ AuthorizeIP(*context.Context, *utils.CGREvent, *engine.AllocatedIP) error
+ AllocateIP(*context.Context, *utils.CGREvent, *engine.AllocatedIP) error
+ ReleaseIP(*context.Context, *utils.CGREvent, *string) error
+ GetIPAllocations(*context.Context, *utils.TenantIDWithAPIOpts, *engine.IPAllocations) error
+ ClearIPAllocations(*context.Context, *engine.ClearIPAllocationsArgs, *string) error
}
type RouteSv1Interface interface {
@@ -197,7 +198,7 @@ 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
+ GetIPAllocations(ctx *context.Context, tntID *utils.TenantIDWithAPIOpts, reply *engine.IPAllocations) 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
@@ -224,7 +225,7 @@ 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
+ SetIPAllocations(ctx *context.Context, rs *engine.IPAllocationsWithAPIOpts, 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
@@ -249,7 +250,7 @@ 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
+ RemoveIPAllocations(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
diff --git a/apier/v1/config_it_test.go b/apier/v1/config_it_test.go
index c12ed27e9..25ef6443c 100644
--- a/apier/v1/config_it_test.go
+++ b/apier/v1/config_it_test.go
@@ -161,6 +161,7 @@ func testConfigSSetConfigSessionS(t *testing.T) {
"rals_conns": []any{utils.MetaInternal},
"replication_conns": []any{},
"resources_conns": []any{utils.MetaLocalHost},
+ utils.IPsConnsCfg: []any{},
"routes_conns": []any{utils.MetaLocalHost},
"scheduler_conns": []any{},
"thresholds_conns": []any{},
@@ -192,6 +193,7 @@ func testConfigSSetConfigSessionS(t *testing.T) {
"chargers_conns": []string{utils.MetaInternal},
"rals_conns": []string{utils.MetaInternal},
"resources_conns": []string{utils.MetaLocalHost},
+ utils.IPsConnsCfg: empty,
"thresholds_conns": empty,
"stats_conns": empty,
"routes_conns": []string{utils.MetaLocalHost},
@@ -245,6 +247,7 @@ func testConfigSv1GetJSONSectionWithoutTenant(t *testing.T) {
"chargers_conns": []any{utils.MetaInternal},
"rals_conns": []any{utils.MetaInternal},
"resources_conns": []any{utils.MetaLocalHost},
+ utils.IPsConnsCfg: []any{},
"thresholds_conns": []any{},
"stats_conns": []any{},
"routes_conns": []any{utils.MetaLocalHost},
@@ -285,6 +288,7 @@ func testConfigSv1GetJSONSectionWithoutTenant(t *testing.T) {
"chargers_conns": []string{utils.MetaInternal},
"rals_conns": []string{utils.MetaInternal},
"resources_conns": []string{utils.MetaLocalHost},
+ utils.IPsConnsCfg: empty,
"thresholds_conns": empty,
"stats_conns": empty,
"routes_conns": []string{utils.MetaLocalHost},
diff --git a/apier/v1/dispatcher.go b/apier/v1/dispatcher.go
index 360301e73..423552471 100644
--- a/apier/v1/dispatcher.go
+++ b/apier/v1/dispatcher.go
@@ -429,29 +429,34 @@ func (dRs *DispatcherIPsV1) Ping(ctx *context.Context, args *utils.CGREvent, rep
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)
+// GetIPAllocationForEvent implements IPsV1GetIPAllocationForEvent
+func (dRs *DispatcherIPsV1) GetIPAllocationForEvent(ctx *context.Context, args *utils.CGREvent,
+ reply *engine.IPAllocations) error {
+ return dRs.dRs.IPsV1GetIPAllocationForEvent(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) GetIPAllocations(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *engine.IPAllocations) error {
+ return dRs.dRs.IPsV1GetIPAllocations(ctx, args, reply)
}
-func (dRs *DispatcherIPsV1) AuthorizeIPs(ctx *context.Context, args *utils.CGREvent,
+func (dRs *DispatcherIPsV1) AuthorizeIP(ctx *context.Context, args *utils.CGREvent,
+ reply *engine.AllocatedIP) error {
+ return dRs.dRs.IPsV1AuthorizeIP(ctx, args, reply)
+}
+
+func (dRs *DispatcherIPsV1) AllocateIP(ctx *context.Context, args *utils.CGREvent,
+ reply *engine.AllocatedIP) error {
+ return dRs.dRs.IPsV1AllocateIP(ctx, args, reply)
+}
+
+func (dRs *DispatcherIPsV1) ReleaseIP(ctx *context.Context, args *utils.CGREvent,
reply *string) error {
- return dRs.dRs.IPsV1AuthorizeIPs(ctx, args, reply)
+ return dRs.dRs.IPsV1ReleaseIP(ctx, args, reply)
}
-func (dRs *DispatcherIPsV1) AllocateIPs(ctx *context.Context, args *utils.CGREvent,
+func (dRs *DispatcherIPsV1) ClearIPAllocations(ctx *context.Context, args *engine.ClearIPAllocationsArgs,
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)
+ return dRs.dRs.IPsV1ClearIPAllocations(ctx, args, reply)
}
func NewDispatcherRouteSv1(dps *dispatchers.DispatcherService) *DispatcherRouteSv1 {
@@ -1171,9 +1176,9 @@ 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)
+// GetIPAllocations
+func (dS *DispatcherReplicatorSv1) GetIPAllocations(ctx *context.Context, tntID *utils.TenantIDWithAPIOpts, reply *engine.IPAllocations) error {
+ return dS.dS.ReplicatorSv1GetIPAllocations(ctx, tntID, reply)
}
// GetIPProfile
@@ -1308,9 +1313,9 @@ 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)
+// SetIPAllocations
+func (dS *DispatcherReplicatorSv1) SetIPAllocations(ctx *context.Context, args *engine.IPAllocationsWithAPIOpts, reply *string) error {
+ return dS.dS.ReplicatorSv1SetIPAllocations(ctx, args, reply)
}
// SetIPProfile
@@ -1433,9 +1438,9 @@ 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)
+// RemoveIPAllocations
+func (dS *DispatcherReplicatorSv1) RemoveIPAllocations(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *string) error {
+ return dS.dS.ReplicatorSv1RemoveIPAllocations(ctx, args, reply)
}
// RemoveIPProfile
diff --git a/apier/v1/ips.go b/apier/v1/ips.go
index 776565a74..5925020fd 100644
--- a/apier/v1/ips.go
+++ b/apier/v1/ips.go
@@ -27,37 +27,45 @@ import (
"github.com/cgrates/cgrates/utils"
)
-func NewIPsV1(rls *engine.IPService) *IPsV1 {
- return &IPsV1{rls: rls}
+// NewIPsV1 initializes the IPSv1 object.
+func NewIPsV1(ipS *engine.IPService) *IPsV1 {
+ return &IPsV1{ips: ipS}
}
+// IPsV1 represents the RPC object to register for ips v1 APIs.
type IPsV1 struct {
- rls *engine.IPService
+ ips *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)
+// GetIPAllocationForEvent returns the IPAllocations object matching the event.
+func (s *IPsV1) GetIPAllocationForEvent(ctx *context.Context, args *utils.CGREvent, reply *engine.IPAllocations) error {
+ return s.ips.V1GetIPAllocationForEvent(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)
+// AuthorizeIP checks if it's able to allocate an IP address for the given event.
+func (s *IPsV1) AuthorizeIP(ctx *context.Context, args *utils.CGREvent, reply *engine.AllocatedIP) error {
+ return s.ips.V1AuthorizeIP(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)
+// AllocateIP allocates an IP address for the given event.
+func (s *IPsV1) AllocateIP(ctx *context.Context, args *utils.CGREvent, reply *engine.AllocatedIP) error {
+ return s.ips.V1AllocateIP(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)
+// ReleaseIP releases an allocated IP address for the given event.
+func (s *IPsV1) ReleaseIP(ctx *context.Context, args *utils.CGREvent, reply *string) error {
+ return s.ips.V1ReleaseIP(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)
+// GetIPAllocations returns all IP allocations for a tenantID.
+func (s *IPsV1) GetIPAllocations(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *engine.IPAllocations) error {
+ return s.ips.V1GetIPAllocations(ctx, arg, reply)
+}
+
+// ClearIPAllocations clears IP allocations from an IPAllocations object.
+// If args.AllocationIDs is empty or nil, all allocations will be cleared.
+func (s *IPsV1) ClearIPAllocations(ctx *context.Context, arg *engine.ClearIPAllocationsArgs, reply *string) error {
+ return s.ips.V1ClearIPAllocations(ctx, arg, reply)
}
// GetIPProfile retrieves the specificed IPProfile from data_db.
@@ -115,7 +123,7 @@ func (a *APIerSv1) SetIPProfile(ctx *context.Context, arg *engine.IPProfileWithA
loadID := time.Now().UnixNano()
if err = a.DataManager.SetLoadIDs(
map[string]int64{utils.CacheIPProfiles: loadID,
- utils.CacheIPs: loadID}); err != nil {
+ utils.CacheIPAllocations: loadID}); err != nil {
return utils.APIErrorHandler(err)
}
// delay if needed before cache call
@@ -157,7 +165,7 @@ func (a *APIerSv1) RemoveIPProfile(ctx *context.Context, arg *utils.TenantIDWith
//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 {
+ if err := a.DataManager.SetLoadIDs(map[string]int64{utils.CacheIPProfiles: loadID, utils.CacheIPAllocations: loadID}); err != nil {
return utils.APIErrorHandler(err)
}
*reply = utils.OK
diff --git a/apier/v1/libapier.go b/apier/v1/libapier.go
index a4e0b2f30..1cb17efe2 100644
--- a/apier/v1/libapier.go
+++ b/apier/v1/libapier.go
@@ -81,7 +81,7 @@ func (apierSv1 *APIerSv1) CallCache(cacheopt string, tnt, cacheID, itemID, group
case utils.CacheResourceProfiles:
cacheIDs = append(cacheIDs, utils.CacheResources)
case utils.CacheIPProfiles:
- cacheIDs = append(cacheIDs, utils.CacheIPs)
+ cacheIDs = append(cacheIDs, utils.CacheIPAllocations)
case utils.CacheStatQueueProfiles:
cacheIDs = append(cacheIDs, utils.CacheStatQueues)
}
@@ -107,7 +107,7 @@ func (apierSv1 *APIerSv1) composeArgsReload(tnt, cacheID, itemID string, filterI
case utils.CacheResourceProfiles:
argCache[utils.CacheResources] = []string{itemID}
case utils.CacheIPProfiles:
- argCache[utils.CacheIPs] = []string{itemID}
+ argCache[utils.CacheIPAllocations] = []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 72cfc0368..59fe60cf8 100644
--- a/apier/v1/precache_it_test.go
+++ b/apier/v1/precache_it_test.go
@@ -195,7 +195,7 @@ func testPrecacheGetCacheStatsAfterRestart(t *testing.T) {
utils.CacheResourceProfiles: {Items: 3},
utils.CacheResources: {Items: 3},
utils.CacheIPProfiles: {},
- utils.CacheIPs: {},
+ utils.CacheIPAllocations: {},
utils.CacheReverseDestinations: {Items: 7},
utils.CacheRPCResponses: {},
utils.MetaSentryPeer: {},
diff --git a/apier/v1/replicator.go b/apier/v1/replicator.go
index 7561e2991..a6f988a92 100644
--- a/apier/v1/replicator.go
+++ b/apier/v1/replicator.go
@@ -218,10 +218,10 @@ 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)
+// GetIPAllocations is the remote method coresponding to the dataDb driver method
+func (rplSv1 *ReplicatorSv1) GetIPAllocations(ctx *context.Context, tntID *utils.TenantIDWithAPIOpts, reply *engine.IPAllocations) error {
+ engine.UpdateReplicationFilters(utils.IPAllocationsPrefix, tntID.TenantID.TenantID(), utils.IfaceAsString(tntID.APIOpts[utils.RemoteHostOpt]))
+ rcv, err := rplSv1.dm.DataDB().GetIPAllocationsDrv(tntID.Tenant, tntID.ID)
if err != nil {
return err
}
@@ -632,13 +632,13 @@ func (rplSv1 *ReplicatorSv1) SetIPProfile(ctx *context.Context, ipp *engine.IPPr
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 {
+// SetIPAllocations is the replication method coresponding to the dataDb driver method
+func (rplSv1 *ReplicatorSv1) SetIPAllocations(ctx *context.Context, ip *engine.IPAllocationsWithAPIOpts, reply *string) (err error) {
+ if err = rplSv1.dm.DataDB().SetIPAllocationsDrv(ip.IPAllocations); 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 {
+ ip.Tenant, utils.CacheIPAllocations, ip.TenantID(), utils.EmptyString, nil, nil, ip.APIOpts); err != nil {
return
}
*reply = utils.OK
@@ -1073,13 +1073,13 @@ 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 {
+// RemoveIPAllocations is the replication method coresponding to the dataDb driver method
+func (rplSv1 *ReplicatorSv1) RemoveIPAllocations(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *string) (err error) {
+ if err = rplSv1.dm.DataDB().RemoveIPAllocationsDrv(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 {
+ args.Tenant, utils.CacheIPAllocations, args.TenantID.TenantID(), utils.EmptyString, nil, nil, args.APIOpts); err != nil {
return
}
*reply = utils.OK
diff --git a/config/config_defaults.go b/config/config_defaults.go
index af0b299d0..e0185336a 100644
--- a/config/config_defaults.go
+++ b/config/config_defaults.go
@@ -114,7 +114,7 @@ const CGRATES_CFG_JSON = `
"*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},
+ "*ip_allocations": {"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},
@@ -312,7 +312,7 @@ const CGRATES_CFG_JSON = `
"*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
+ "*ip_allocations": {"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
diff --git a/config/config_json_test.go b/config/config_json_test.go
index c46e46831..e9b94c371 100644
--- a/config/config_json_test.go
+++ b/config/config_json_test.go
@@ -127,7 +127,7 @@ func TestCacheJsonCfg(t *testing.T) {
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),
+ utils.CacheIPAllocations: {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),
@@ -436,7 +436,7 @@ func TestDfDataDbJsonCfg(t *testing.T) {
Ttl: utils.StringPointer(utils.EmptyString),
Static_ttl: utils.BoolPointer(false),
},
- utils.MetaIPs: {
+ utils.MetaIPAllocations: {
Replicate: utils.BoolPointer(false),
Remote: utils.BoolPointer(false),
Limit: utils.IntPointer(-1),
diff --git a/config/config_test.go b/config/config_test.go
index 8d8a47ec7..200868dca 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -550,7 +550,7 @@ func TestCgrCfgJSONDefaultsCacheCFG(t *testing.T) {
TTL: 0, Remote: false, StaticTTL: false},
utils.CacheIPProfiles: {Limit: -1,
TTL: 0, Remote: false, StaticTTL: false, Precache: false},
- utils.CacheIPs: {Limit: -1,
+ utils.CacheIPAllocations: {Limit: -1,
TTL: 0, Remote: false, StaticTTL: false, Precache: false},
utils.CacheEventIPs: {Limit: -1,
TTL: 0, Remote: false, StaticTTL: false},
@@ -4889,7 +4889,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},"*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"}}`
+ 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_allocations":{"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},"*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)
@@ -4922,7 +4922,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_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":[]}}`
+ 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_allocations":{"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},"*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)
@@ -5412,7 +5412,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,"route_profile":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":"","conn_health_check_interval":"0s","conn_status_stat_queue_ids":[],"conn_status_threshold_ids":[],"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"],"stats_conns":[],"synced_conn_requests":false,"thresholds_conns":[],"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"],"stats_conns":[],"thresholds_conns":[],"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"}],"failed_posts":{"dir":"/var/spool/cgrates/failed_posts","static_ttl":true,"ttl":"5s"}},"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"],"stats_conns":[],"thresholds_conns":[]},"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","route_profile":false,"sched_transfer_extension":"CGRateS","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":",","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":{"*allocationID":"","*ttl":259200000000000},"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}],"route_profile":false,"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":{"apiers_conns":[],"cache_ids":[],"caches_conns":[],"collect_go_metrics":false,"collect_process_metrics":false,"cores_conns":[],"enabled":false,"path":"/prometheus","stat_queue_ids":[],"stats_conns":[]},"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"],"stats_conns":[],"thresholds_conns":[]},"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,"ips_conns":[],"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"],"stats_conns":[],"thresholds_conns":[],"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":[]}}`
+ 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,"route_profile":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_allocations":{"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},"*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_allocations":{"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},"*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":"","conn_health_check_interval":"0s","conn_status_stat_queue_ids":[],"conn_status_threshold_ids":[],"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"],"stats_conns":[],"synced_conn_requests":false,"thresholds_conns":[],"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"],"stats_conns":[],"thresholds_conns":[],"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"}],"failed_posts":{"dir":"/var/spool/cgrates/failed_posts","static_ttl":true,"ttl":"5s"}},"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"],"stats_conns":[],"thresholds_conns":[]},"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","route_profile":false,"sched_transfer_extension":"CGRateS","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":",","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":{"*allocationID":"","*ttl":259200000000000},"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}],"route_profile":false,"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":{"apiers_conns":[],"cache_ids":[],"caches_conns":[],"collect_go_metrics":false,"collect_process_metrics":false,"cores_conns":[],"enabled":false,"path":"/prometheus","stat_queue_ids":[],"stats_conns":[]},"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"],"stats_conns":[],"thresholds_conns":[]},"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,"ips_conns":[],"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"],"stats_conns":[],"thresholds_conns":[],"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)
}
diff --git a/config/sessionscfg.go b/config/sessionscfg.go
index 1fa4d5d20..1f1aa4269 100644
--- a/config/sessionscfg.go
+++ b/config/sessionscfg.go
@@ -174,7 +174,7 @@ func (scfg *SessionSCfg) loadFromJSONCfg(jsnCfg *SessionSJsonCfg) (err error) {
scfg.IPsConns = tagInternalConns(*jsnCfg.IPsConns, utils.MetaIPs)
}
if jsnCfg.ResourceSConns != nil {
- scfg.ResourceSConns = tagInternalConns(*jsnCfg.IPsConns, utils.MetaResources)
+ scfg.ResourceSConns = tagInternalConns(*jsnCfg.ResourceSConns, utils.MetaResources)
}
if jsnCfg.ThresholdSConns != nil {
scfg.ThresholdSConns = tagInternalConns(*jsnCfg.ThresholdSConns, utils.MetaThresholds)
diff --git a/dispatchers/caches_it_test.go b/dispatchers/caches_it_test.go
index 4e963b110..51adae451 100644
--- a/dispatchers/caches_it_test.go
+++ b/dispatchers/caches_it_test.go
@@ -180,7 +180,7 @@ func testDspChcPrecacheStatus(t *testing.T) {
utils.CacheResourceProfiles: utils.MetaReady,
utils.CacheResources: utils.MetaReady,
utils.CacheIPProfiles: utils.MetaReady,
- utils.CacheIPs: utils.MetaReady,
+ utils.CacheIPAllocations: utils.MetaReady,
utils.CacheTimings: utils.MetaReady,
utils.CacheStatQueueProfiles: utils.MetaReady,
utils.CacheStatQueues: utils.MetaReady,
diff --git a/dispatchers/ips.go b/dispatchers/ips.go
index ea92cfe58..145f9f9f8 100644
--- a/dispatchers/ips.go
+++ b/dispatchers/ips.go
@@ -40,73 +40,74 @@ func (dS *DispatcherService) IPsV1Ping(ctx *context.Context, args *utils.CGREven
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) {
+func (dS *DispatcherService) IPsV1GetIPAllocationForEvent(ctx *context.Context, args *utils.CGREvent,
+ reply *engine.IPAllocations) (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,
+ if err = dS.authorize(utils.IPsV1GetIPAllocationForEvent, tnt,
utils.IfaceAsString(args.APIOpts[utils.OptsAPIKey]), args.Time); err != nil {
return
}
}
- return dS.Dispatch(args, utils.MetaIPs, utils.IPsV1GetIPsForEvent, args, reply)
+ return dS.Dispatch(args, utils.MetaIPs, utils.IPsV1GetIPAllocationForEvent, args, reply)
}
-func (dS *DispatcherService) IPsV1AuthorizeIPs(ctx *context.Context, args *utils.CGREvent,
+func (dS *DispatcherService) IPsV1AuthorizeIP(ctx *context.Context, args *utils.CGREvent,
+ reply *engine.AllocatedIP) (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.IPsV1AuthorizeIP, tnt,
+ utils.IfaceAsString(args.APIOpts[utils.OptsAPIKey]), args.Time); err != nil {
+ return
+ }
+ }
+ return dS.Dispatch(args, utils.MetaIPs, utils.IPsV1AuthorizeIP, args, reply)
+}
+
+func (dS *DispatcherService) IPsV1AllocateIP(ctx *context.Context, args *utils.CGREvent,
+ reply *engine.AllocatedIP) (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.IPsV1AllocateIP, tnt,
+ utils.IfaceAsString(args.APIOpts[utils.OptsAPIKey]), args.Time); err != nil {
+ return
+ }
+ }
+ return dS.Dispatch(args, utils.MetaIPs, utils.IPsV1AllocateIP, args, reply)
+}
+
+func (dS *DispatcherService) IPsV1ReleaseIP(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,
+ if err = dS.authorize(utils.IPsV1ReleaseIP, tnt,
utils.IfaceAsString(args.APIOpts[utils.OptsAPIKey]), args.Time); err != nil {
return
}
}
- return dS.Dispatch(args, utils.MetaIPs, utils.IPsV1AuthorizeIPs, args, reply)
+ return dS.Dispatch(args, utils.MetaIPs, utils.IPsV1ReleaseIP, args, reply)
}
-func (dS *DispatcherService) IPsV1AllocateIPs(ctx *context.Context, args *utils.CGREvent,
+func (dS *DispatcherService) IPsV1ClearIPAllocations(ctx *context.Context, args *engine.ClearIPAllocationsArgs,
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,
+ if err = dS.authorize(utils.IPsV1ClearIPAllocations, tnt,
utils.IfaceAsString(args.APIOpts[utils.OptsAPIKey]), utils.TimePointer(time.Now())); err != nil {
return
}
@@ -115,5 +116,23 @@ func (dS *DispatcherService) IPsV1GetIP(ctx *context.Context, args *utils.Tenant
Tenant: tnt,
ID: args.ID,
APIOpts: args.APIOpts,
- }, utils.MetaIPs, utils.IPsV1GetIP, args, reply)
+ }, utils.MetaIPs, utils.IPsV1ClearIPAllocations, args, reply)
+}
+
+func (dS *DispatcherService) IPsV1GetIPAllocations(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *engine.IPAllocations) (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.IPsV1GetIPAllocations, 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.IPsV1GetIPAllocations, args, reply)
}
diff --git a/dispatchers/replicator.go b/dispatchers/replicator.go
index 3e5b175fa..c991f0288 100644
--- a/dispatchers/replicator.go
+++ b/dispatchers/replicator.go
@@ -234,13 +234,13 @@ 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) {
+func (dS *DispatcherService) ReplicatorSv1GetIPAllocations(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *engine.IPAllocations) (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,
+ if err = dS.authorize(utils.ReplicatorSv1GetIPAllocations, tnt,
utils.IfaceAsString(args.APIOpts[utils.OptsAPIKey]), utils.TimePointer(time.Now())); err != nil {
return
}
@@ -249,7 +249,7 @@ func (dS *DispatcherService) ReplicatorSv1GetIP(ctx *context.Context, args *util
Tenant: tnt,
ID: args.ID,
APIOpts: args.APIOpts,
- }, utils.MetaReplicator, utils.ReplicatorSv1GetIP, args, reply)
+ }, utils.MetaReplicator, utils.ReplicatorSv1GetIPAllocations, args, reply)
}
func (dS *DispatcherService) ReplicatorSv1GetIPProfile(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *engine.IPProfile) (err error) {
@@ -716,15 +716,15 @@ 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) {
+func (dS *DispatcherService) ReplicatorSv1SetIPAllocations(ctx *context.Context, args *engine.IPAllocationsWithAPIOpts, rpl *string) (err error) {
if args == nil {
- args = &engine.IPWithAPIOpts{
- IP: &engine.IP{},
+ args = &engine.IPAllocationsWithAPIOpts{
+ IPAllocations: &engine.IPAllocations{},
}
}
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,
+ if err = dS.authorize(utils.ReplicatorSv1SetIPAllocations, args.Tenant,
utils.IfaceAsString(args.APIOpts[utils.OptsAPIKey]), utils.TimePointer(time.Now())); err != nil {
return
}
@@ -732,7 +732,7 @@ func (dS *DispatcherService) ReplicatorSv1SetIP(ctx *context.Context, args *engi
return dS.Dispatch(&utils.CGREvent{
Tenant: args.Tenant,
APIOpts: args.APIOpts,
- }, utils.MetaReplicator, utils.ReplicatorSv1SetIP, args, rpl)
+ }, utils.MetaReplicator, utils.ReplicatorSv1SetIPAllocations, args, rpl)
}
func (dS *DispatcherService) ReplicatorSv1SetIPProfile(ctx *context.Context, args *engine.IPProfileWithAPIOpts, rpl *string) (err error) {
@@ -1169,7 +1169,7 @@ 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) {
+func (dS *DispatcherService) ReplicatorSv1RemoveIPAllocations(ctx *context.Context, args *utils.TenantIDWithAPIOpts, rpl *string) (err error) {
if args == nil {
args = &utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{},
@@ -1177,7 +1177,7 @@ func (dS *DispatcherService) ReplicatorSv1RemoveIP(ctx *context.Context, args *u
}
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,
+ if err = dS.authorize(utils.ReplicatorSv1RemoveIPAllocations, args.Tenant,
utils.IfaceAsString(args.APIOpts[utils.OptsAPIKey]), utils.TimePointer(time.Now())); err != nil {
return
}
@@ -1185,7 +1185,7 @@ func (dS *DispatcherService) ReplicatorSv1RemoveIP(ctx *context.Context, args *u
return dS.Dispatch(&utils.CGREvent{
Tenant: args.Tenant,
APIOpts: args.APIOpts,
- }, utils.MetaReplicator, utils.ReplicatorSv1RemoveIP, args, rpl)
+ }, utils.MetaReplicator, utils.ReplicatorSv1RemoveIPAllocations, args, rpl)
}
func (dS *DispatcherService) ReplicatorSv1RemoveIPProfile(ctx *context.Context, args *utils.TenantIDWithAPIOpts, rpl *string) (err error) {
diff --git a/engine/caches.go b/engine/caches.go
index 6669afcde..68d5d39f4 100644
--- a/engine/caches.go
+++ b/engine/caches.go
@@ -55,10 +55,10 @@ func init() {
gob.Register(new(ResourceWithAPIOpts))
gob.Register(new(utils.TPResourceProfile))
// IPs
- gob.Register(new(IP))
+ gob.Register(new(IPAllocations))
gob.Register(new(IPProfile))
gob.Register(new(IPProfileWithAPIOpts))
- gob.Register(new(IPWithAPIOpts))
+ gob.Register(new(IPAllocationsWithAPIOpts))
gob.Register(new(utils.TPIPProfile))
// StatS
gob.Register(new(StatQueue))
diff --git a/engine/datadbmock.go b/engine/datadbmock.go
index 40d33e25c..d243bce32 100644
--- a/engine/datadbmock.go
+++ b/engine/datadbmock.go
@@ -38,7 +38,7 @@ type DataDBMock struct {
GetIPProfileDrvF func(tnt, id string) (*IPProfile, error)
SetIPProfileDrvF func(ipp *IPProfile) error
RemoveIPProfileDrvF func(tnt, id string) error
- SetIPDrvF func(ip *IP) error
+ SetIPAllocationsDrvF func(ip *IPAllocations) error
GetStatQueueProfileDrvF func(tenant, id string) (sq *StatQueueProfile, err error)
SetStatQueueProfileDrvF func(sq *StatQueueProfile) (err error)
RemStatQueueProfileDrvF func(tenant, id string) (err error)
@@ -304,18 +304,18 @@ func (dbM *DataDBMock) RemoveIPProfileDrv(tnt, id string) error {
return utils.ErrNotImplemented
}
-func (dbM *DataDBMock) GetIPDrv(string, string) (*IP, error) {
+func (dbM *DataDBMock) GetIPAllocationsDrv(string, string) (*IPAllocations, error) {
return nil, utils.ErrNotImplemented
}
-func (dbM *DataDBMock) SetIPDrv(ip *IP) error {
- if dbM.SetIPDrvF != nil {
- return dbM.SetIPDrvF(ip)
+func (dbM *DataDBMock) SetIPAllocationsDrv(ip *IPAllocations) error {
+ if dbM.SetIPAllocationsDrvF != nil {
+ return dbM.SetIPAllocationsDrvF(ip)
}
return utils.ErrNotImplemented
}
-func (dbM *DataDBMock) RemoveIPDrv(string, string) error {
+func (dbM *DataDBMock) RemoveIPAllocationsDrv(string, string) error {
return utils.ErrNotImplemented
}
diff --git a/engine/datamanager.go b/engine/datamanager.go
index 6abc07098..de93ac9f0 100644
--- a/engine/datamanager.go
+++ b/engine/datamanager.go
@@ -57,7 +57,7 @@ var (
utils.IPProfilesPrefix: {},
utils.TimingsPrefix: {},
utils.ResourcesPrefix: {},
- utils.IPsPrefix: {},
+ utils.IPAllocationsPrefix: {},
utils.StatQueuePrefix: {},
utils.StatQueueProfilePrefix: {},
utils.ThresholdPrefix: {},
@@ -197,10 +197,10 @@ func (dm *DataManager) CacheDataFromDB(prfx string, ids []string, mustBeCached b
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:
+ case utils.IPAllocationsPrefix:
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)
+ lkID := guardian.Guardian.GuardIDs("", config.CgrConfig().GeneralCfg().LockingTimeout, ipAllocationsLockKey(tntID.Tenant, tntID.ID))
+ _, err = dm.GetIPAllocations(tntID.Tenant, tntID.ID, false, true, utils.NonTransactional, nil)
guardian.Guardian.UnguardIDs(lkID)
case utils.StatQueueProfilePrefix:
tntID := utils.NewTenantID(dataID)
@@ -865,7 +865,7 @@ func (dm *DataManager) RemoveThreshold(tenant, id string) (err error) {
}
itm := config.CgrConfig().DataDbCfg().Items[utils.MetaThresholds]
_ = dm.replicator.replicate(
- utils.ThresholdPrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache
+ utils.ThresholdPrefix, utils.ConcatenatedKey(tenant, id), // these are used to get the host IDs from cache
utils.ReplicatorSv1RemoveThreshold,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
@@ -2095,49 +2095,55 @@ 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) {
+func (dm *DataManager) GetIPAllocations(tenant, id string, cacheRead, cacheWrite bool,
+ transactionID string, prfl *IPProfile) (ip *IPAllocations, err error) {
tntID := utils.ConcatenatedKey(tenant, id)
if cacheRead {
- if x, ok := Cache.Get(utils.CacheIPs, tntID); ok {
+ if x, ok := Cache.Get(utils.CacheIPAllocations, tntID); ok {
if x == nil {
return nil, utils.ErrNotFound
}
- return x.(*IP), nil
+ ip = x.(*IPAllocations)
+ if err = ip.computeUnexported(prfl); err != nil {
+ return nil, err
+ }
+ return ip, nil
}
}
if dm == nil {
err = utils.ErrNoDatabaseConn
return
}
- rs, err = dm.dataDB.GetIPDrv(tenant, id)
+ ip, err = dm.dataDB.GetIPAllocationsDrv(tenant, id)
if err != nil {
- if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaIPs]; err == utils.ErrNotFound && itm.Remote {
+ if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaIPAllocations]; err == utils.ErrNotFound && itm.Remote {
if err = dm.connMgr.Call(context.TODO(), config.CgrConfig().DataDbCfg().RmtConns,
- utils.ReplicatorSv1GetIP,
+ utils.ReplicatorSv1GetIPAllocations,
&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)
+ }, &ip); err == nil {
+ err = dm.dataDB.SetIPAllocationsDrv(ip)
}
}
if err != nil {
err = utils.CastRPCErr(err)
if err == utils.ErrNotFound && cacheWrite {
- if errCh := Cache.Set(utils.CacheIPs, tntID, nil, nil,
+ if errCh := Cache.Set(utils.CacheIPAllocations, tntID, nil, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
-
}
return nil, err
}
}
+ if err = ip.computeUnexported(prfl); err != nil {
+ return nil, err
+ }
if cacheWrite {
- if errCh := Cache.Set(utils.CacheIPs, tntID, rs, nil,
+ if errCh := Cache.Set(utils.CacheIPAllocations, tntID, ip, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
@@ -2145,40 +2151,38 @@ func (dm *DataManager) GetIP(tenant, id string, cacheRead, cacheWrite bool,
return
}
-func (dm *DataManager) SetIP(rs *IP) (err error) {
+func (dm *DataManager) SetIPAllocations(ip *IPAllocations) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
- if err = dm.DataDB().SetIPDrv(rs); err != nil {
+ if err = dm.dataDB.SetIPAllocationsDrv(ip); err != nil {
return
}
- itm := config.CgrConfig().DataDbCfg().Items[utils.MetaIPs]
+ itm := config.CgrConfig().DataDbCfg().Items[utils.MetaIPAllocations]
return dm.replicator.replicate(
- utils.IPsPrefix, rs.TenantID(), // these are used to get the host IDs from cache
- utils.ReplicatorSv1SetIP,
- &IPWithAPIOpts{
- IP: rs,
+ utils.IPAllocationsPrefix, ip.TenantID(), // these are used to get the host IDs from cache
+ utils.ReplicatorSv1SetIPAllocations,
+ &IPAllocationsWithAPIOpts{
+ IPAllocations: ip,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
- config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString),
- }, itm)
+ config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}, itm)
}
-func (dm *DataManager) RemoveIP(tenant, id string) (err error) {
+func (dm *DataManager) RemoveIPAllocations(tenant, id string) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
- if err = dm.DataDB().RemoveIPDrv(tenant, id); err != nil {
+ if err = dm.dataDB.RemoveIPAllocationsDrv(tenant, id); err != nil {
return
}
- itm := config.CgrConfig().DataDbCfg().Items[utils.MetaIPs]
+ itm := config.CgrConfig().DataDbCfg().Items[utils.MetaIPAllocations]
_ = dm.replicator.replicate(
- utils.IPsPrefix, utils.ConcatenatedKey(tenant, id), // these are used to get the host IDs from cache
- utils.ReplicatorSv1RemoveIP,
+ utils.IPAllocationsPrefix, utils.ConcatenatedKey(tenant, id), // these are used to get the host IDs from cache
+ utils.ReplicatorSv1RemoveIPAllocations,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
- config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString),
- }, itm)
+ config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}, itm)
return
}
@@ -2236,23 +2240,23 @@ func (dm *DataManager) SetIPProfile(ipp *IPProfile, withIndex bool) (err error)
return utils.ErrNoDatabaseConn
}
if withIndex {
- if err = dm.checkFilters(ipp.Tenant, ipp.FilterIDs); err != nil {
+ 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)
+ oldIPP, 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 {
+ if err = dm.dataDB.SetIPProfileDrv(ipp); err != nil {
return err
}
if withIndex {
var oldFiltersIDs *[]string
- if oldIP != nil {
- oldFiltersIDs = &oldIP.FilterIDs
+ if oldIPP != nil {
+ oldFiltersIDs = &oldIPP.FilterIDs
}
if err := updatedIndexes(dm, utils.CacheIPFilterIndexes, ipp.Tenant,
utils.EmptyString, ipp.ID, oldFiltersIDs, ipp.FilterIDs, false); err != nil {
@@ -2271,20 +2275,20 @@ func (dm *DataManager) SetIPProfile(ipp *IPProfile, withIndex bool) (err error)
}, 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),
+ if oldIPP == nil || // create the resource if it didn't exist before
+ oldIPP.TTL != ipp.TTL ||
+ oldIPP.Stored != ipp.Stored && oldIPP.Stored { // reset the resource if the profile changed this fields
+ err = dm.SetIPAllocations(&IPAllocations{
+ Tenant: ipp.Tenant,
+ ID: ipp.ID,
+ Allocations: make(map[string]*PoolAllocation),
})
- } 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),
+ } else if _, errGet := dm.GetIPAllocations(ipp.Tenant, ipp.ID, // do not try to get the resource if the configuration changed
+ true, false, utils.NonTransactional, nil); errGet == utils.ErrNotFound { // the resource does not exist
+ err = dm.SetIPAllocations(&IPAllocations{
+ Tenant: ipp.Tenant,
+ ID: ipp.ID,
+ Allocations: make(map[string]*PoolAllocation),
})
}
return
@@ -2294,22 +2298,22 @@ func (dm *DataManager) RemoveIPProfile(tenant, id string, withIndex bool) (err e
if dm == nil {
return utils.ErrNoDatabaseConn
}
- oldRes, err := dm.GetIPProfile(tenant, id, true, false, utils.NonTransactional)
+ oldIPP, 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 {
+ if err = dm.dataDB.RemoveIPProfileDrv(tenant, id); err != nil {
return
}
- if oldRes == nil {
+ if oldIPP == nil {
return utils.ErrNotFound
}
if withIndex {
- if err = removeIndexFiltersItem(dm, utils.CacheIPFilterIndexes, tenant, id, oldRes.FilterIDs); err != nil {
+ if err = removeIndexFiltersItem(dm, utils.CacheIPFilterIndexes, tenant, id, oldIPP.FilterIDs); err != nil {
return
}
if err = removeItemFromFilterIndex(dm, utils.CacheIPFilterIndexes,
- tenant, utils.EmptyString, id, oldRes.FilterIDs); err != nil {
+ tenant, utils.EmptyString, id, oldIPP.FilterIDs); err != nil {
return
}
}
@@ -2322,7 +2326,7 @@ func (dm *DataManager) RemoveIPProfile(tenant, id string, withIndex bool) (err e
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString),
}, itm)
- return dm.RemoveIP(tenant, id)
+ return dm.RemoveIPAllocations(tenant, id)
}
func (dm *DataManager) GetActionTriggers(id string, skipCache bool,
diff --git a/engine/ips.go b/engine/ips.go
index b8ee19657..dff3a81db 100644
--- a/engine/ips.go
+++ b/engine/ips.go
@@ -19,10 +19,13 @@ along with this program. If not, see
package engine
import (
+ "cmp"
+ "errors"
"fmt"
+ "maps"
+ "net/netip"
"runtime"
"slices"
- "sort"
"sync"
"time"
@@ -32,44 +35,18 @@ import (
"github.com/cgrates/cgrates/utils"
)
-// IPProfile defines the configuration of the IP.
+// IPProfile defines the configuration of an IPAllocations object.
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
+ TTL time.Duration
+ Stored bool
+ Pools []*IPPool
- 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()
+ lockID string // reference ID of lock used when matching the IPProfile
}
// IPProfileWithAPIOpts wraps IPProfile with APIOpts.
@@ -79,439 +56,569 @@ type IPProfileWithAPIOpts struct {
}
// TenantID returns the concatenated tenant and ID.
-func (ip *IPProfile) TenantID() string {
- return utils.ConcatenatedKey(ip.Tenant, ip.ID)
+func (p *IPProfile) TenantID() string {
+ return utils.ConcatenatedKey(p.Tenant, p.ID)
}
-// ipProfileLockKey returns the ID used to lock an IPProfile with guardian.
+// Clone creates a deep copy of IPProfile for thread-safe use.
+func (p *IPProfile) Clone() *IPProfile {
+ if p == nil {
+ return nil
+ }
+ pools := make([]*IPPool, 0, len(p.Pools))
+ for _, pool := range p.Pools {
+ pools = append(pools, pool.Clone())
+ }
+ return &IPProfile{
+ Tenant: p.Tenant,
+ ID: p.ID,
+ FilterIDs: slices.Clone(p.FilterIDs),
+ ActivationInterval: p.ActivationInterval.Clone(),
+ Weight: p.Weight,
+ TTL: p.TTL,
+ Stored: p.Stored,
+ Pools: pools,
+ lockID: p.lockID,
+ }
+}
+
+// CacheClone returns a clone of IPProfile used by ltcache CacheCloner
+func (p *IPProfile) CacheClone() any {
+ return p.Clone()
+}
+
+// Lock acquires a guardian lock on the IPProfile and stores the lock ID.
+// Uses given lockID or creates a new lock.
+func (p *IPProfile) lock(lockID string) {
+ if lockID == "" {
+ lockID = guardian.Guardian.GuardIDs("",
+ config.CgrConfig().GeneralCfg().LockingTimeout,
+ ipProfileLockKey(p.Tenant, p.ID))
+ }
+ p.lockID = lockID
+}
+
+// Unlock releases the lock on the IPProfile and clears the stored lock ID.
+func (p *IPProfile) unlock() {
+ if p.lockID == "" {
+ return
+ }
+
+ // Store current lock ID before clearing to prevent race conditions.
+ id := p.lockID
+ p.lockID = ""
+ guardian.Guardian.UnguardIDs(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
+// IPPool defines a pool of IP addresses within an IPProfile.
+type IPPool struct {
+ ID string
+ FilterIDs []string
+ Type string
+ Range string
+ Strategy string
+ Message string
+ Weight float64
+ Blocker bool
}
-// unlock releases the lock on the IPProfile and clears the lock ID.
-func (ip *IPProfile) unlock() {
- if ip.lkID == utils.EmptyString {
- return
- }
- tmp := ip.lkID
- ip.lkID = utils.EmptyString
- guardian.Guardian.UnguardIDs(tmp)
-}
-
-// 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 {
+// Clone creates a deep copy of Pool for thread-safe use.
+func (p *IPPool) Clone() *IPPool {
+ if p == nil {
return nil
}
- clone := *u
+ return &IPPool{
+ ID: p.ID,
+ FilterIDs: slices.Clone(p.FilterIDs),
+ Type: p.Type,
+ Range: p.Range,
+ Strategy: p.Strategy,
+ Message: p.Message,
+ Weight: p.Weight,
+ Blocker: p.Blocker,
+ }
+}
+
+// PoolAllocation represents one allocation in the pool.
+type PoolAllocation struct {
+ PoolID string // pool ID within the IPProfile
+ Address netip.Addr // computed IP address
+ Time time.Time // when this allocation was created
+}
+
+// IsActive checks if the allocation is still active.
+func (a *PoolAllocation) isActive(ttl time.Duration) bool {
+ return time.Now().Before(a.Time.Add(ttl))
+}
+
+// Clone creates a deep copy of the PoolAllocation object.
+func (a *PoolAllocation) Clone() *PoolAllocation {
+ if a == nil {
+ return nil
+ }
+ clone := *a
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
+// AllocatedIP represents one IP allocated on a pool, together with the message.
+type AllocatedIP struct {
+ ProfileID string
+ PoolID string
+ Message string
+ Address netip.Addr
}
-// Clone clones *IP (lkID excluded)
-func (ip *IP) Clone() *IP {
- if ip == nil {
- return nil
+// AsNavigableMap implements engine.NavigableMapper.
+func (ip *AllocatedIP) AsNavigableMap() map[string]*utils.DataNode {
+ return map[string]*utils.DataNode{
+ utils.ProfileID: utils.NewLeafNode(ip.ProfileID),
+ utils.PoolID: utils.NewLeafNode(ip.PoolID),
+ utils.Message: utils.NewLeafNode(ip.Message),
+ utils.Address: utils.NewLeafNode(ip.Address.String()),
}
- 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()
+// IPAllocations represents IP allocations with usage tracking and TTL management.
+type IPAllocations struct {
+ Tenant string
+ ID string
+ Allocations map[string]*PoolAllocation // map[allocID]*PoolAllocation
+ TTLIndex []string // allocIDs ordered by allocation time for TTL expiry
+
+ prfl *IPProfile
+ poolRanges map[string]netip.Prefix // parsed CIDR ranges by pool ID
+ poolAllocs map[string]map[netip.Addr]string // IP to allocation ID mapping by pool (map[poolID]map[Addr]allocID)
+ lockID string
}
-// 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
- }
- tmp := ip.lkID
- ip.lkID = utils.EmptyString
- guardian.Guardian.UnguardIDs(tmp)
-
-}
-
-type IPWithAPIOpts struct {
- *IP
+// IPAllocationsWithAPIOpts wraps IPAllocations with APIOpts.
+type IPAllocationsWithAPIOpts struct {
+ *IPAllocations
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)
+// ClearIPAllocationsArgs contains arguments for clearing IP allocations.
+// If AllocationIDs is empty or nil, all allocations will be cleared.
+type ClearIPAllocationsArgs struct {
+ Tenant string
+ ID string
+ AllocationIDs []string
+ APIOpts map[string]any
}
-// 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++
+// computeUnexported sets up unexported fields based on the provided profile.
+// Safe to call multiple times with the same profile.
+func (a *IPAllocations) computeUnexported(prfl *IPProfile) error {
+ if prfl == nil {
+ return nil // nothing to compute without a profile
}
- if firstActive == 0 {
- return
+ if a.prfl == prfl {
+ return nil // already computed for this profile
}
- 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
- }
+ a.prfl = prfl
+ a.poolAllocs = make(map[string]map[netip.Addr]string)
+ for allocID, alloc := range a.Allocations {
+ if _, hasPool := a.poolAllocs[alloc.PoolID]; !hasPool {
+ a.poolAllocs[alloc.PoolID] = make(map[netip.Addr]string)
}
+ a.poolAllocs[alloc.PoolID][alloc.Address] = allocID
}
- ip.TTLIdx = ip.TTLIdx[firstActive:]
- ip.tUsage = nil
+ a.poolRanges = make(map[string]netip.Prefix)
+ for _, poolCfg := range a.prfl.Pools {
+ prefix, err := netip.ParsePrefix(poolCfg.Range)
+ if err != nil {
+ return err
+ }
+ a.poolRanges[poolCfg.ID] = prefix
+ }
+ return 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
+// releaseAllocation releases the allocation for an ID.
+func (a *IPAllocations) releaseAllocation(allocID string) error {
+ alloc, has := a.Allocations[allocID] // Get the allocation first
+ if !has {
+ return fmt.Errorf("cannot find allocation record with id: %s", allocID)
}
- if ip.tUsage == nil {
- return 0
+ if poolMap, hasPool := a.poolAllocs[alloc.PoolID]; hasPool {
+ delete(poolMap, alloc.Address)
}
- 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)
+ if a.prfl.TTL > 0 {
+ for i, refID := range a.TTLIndex {
+ if refID == allocID {
+ a.TTLIndex = slices.Delete(a.TTLIndex, i, i+1)
break
}
}
}
- if ip.tUsage != nil {
- *ip.tUsage -= ru.Units
+ delete(a.Allocations, allocID)
+ return nil
+}
+
+// clearAllocations clears specified IP allocations or all allocations if allocIDs is empty/nil.
+// Either all specified IDs exist and get cleared, or none are cleared and an error is returned.
+func (a *IPAllocations) clearAllocations(allocIDs []string) error {
+ if len(allocIDs) == 0 {
+ clear(a.Allocations)
+ clear(a.poolAllocs)
+ a.TTLIndex = a.TTLIndex[:0] // maintain capacity
+ return nil
}
- delete(ip.Usages, ruID)
- return
+
+ // Validate all IDs exist before clearing any.
+ var notFound []string
+ for _, allocID := range allocIDs {
+ if _, has := a.Allocations[allocID]; !has {
+ notFound = append(notFound, allocID)
+ }
+ }
+ if len(notFound) > 0 {
+ return fmt.Errorf("cannot find allocation records with ids: %v", notFound)
+ }
+
+ for _, allocID := range allocIDs {
+ alloc := a.Allocations[allocID]
+ if poolMap, hasPool := a.poolAllocs[alloc.PoolID]; hasPool {
+ delete(poolMap, alloc.Address)
+ }
+ if a.prfl.TTL > 0 {
+ for i, refID := range a.TTLIndex {
+ if refID == allocID {
+ a.TTLIndex = slices.Delete(a.TTLIndex, i, i+1)
+ break
+ }
+ }
+ }
+ delete(a.Allocations, allocID)
+ }
+
+ return nil
}
-// 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 })
+// allocateIPOnPool allocates an IP from the specified pool or refreshes
+// existing allocation. If dryRun is true, checks availability without
+// allocating.
+func (a *IPAllocations) allocateIPOnPool(allocID string, pool *IPPool,
+ dryRun bool) (*AllocatedIP, error) {
+ a.removeExpiredUnits()
+ if poolAlloc, has := a.Allocations[allocID]; has && !dryRun {
+ poolAlloc.Time = time.Now()
+ if a.prfl.TTL > 0 {
+ a.removeAllocFromTTLIndex(allocID)
+ }
+ a.TTLIndex = append(a.TTLIndex, allocID)
+ return &AllocatedIP{
+ ProfileID: a.ID,
+ PoolID: pool.ID,
+ Message: pool.Message,
+ Address: poolAlloc.Address,
+ }, nil
+ }
+ poolRange := a.poolRanges[pool.ID]
+ if !poolRange.IsSingleIP() {
+ return nil, errors.New("only single IP Pools are supported for now")
+ }
+ addr := poolRange.Addr()
+ if _, hasPool := a.poolAllocs[pool.ID]; hasPool {
+ if alcID, inUse := a.poolAllocs[pool.ID][addr]; inUse {
+ return nil, fmt.Errorf("allocation failed for pool %q, IP %q: %w (allocated to %q)",
+ pool.ID, addr, utils.ErrIPAlreadyAllocated, alcID)
+ }
+ }
+ allocIP := &AllocatedIP{
+ ProfileID: a.ID,
+ PoolID: pool.ID,
+ Message: pool.Message,
+ Address: addr,
+ }
+ if dryRun {
+ return allocIP, nil
+ }
+ a.Allocations[allocID] = &PoolAllocation{
+ PoolID: pool.ID,
+ Address: addr,
+ Time: time.Now(),
+ }
+ if _, hasPool := a.poolAllocs[pool.ID]; !hasPool {
+ a.poolAllocs[pool.ID] = make(map[netip.Addr]string)
+ }
+ a.poolAllocs[pool.ID][addr] = allocID
+ return allocIP, nil
}
-// 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()
+// removeExpiredUnits removes expired allocations.
+// It stops at first active since TTLIndex is sorted by expiration.
+func (a *IPAllocations) removeExpiredUnits() {
+ expiredCount := 0
+ for _, allocID := range a.TTLIndex {
+ alloc, exists := a.Allocations[allocID]
+ if exists && alloc.isActive(a.prfl.TTL) {
+ break
+ }
+ if alloc != nil {
+ if poolMap, hasPool := a.poolAllocs[alloc.PoolID]; hasPool {
+ delete(poolMap, alloc.Address)
+ }
+ }
+ delete(a.Allocations, allocID)
+ expiredCount++
+ }
+ if expiredCount > 0 {
+ a.TTLIndex = a.TTLIndex[expiredCount:]
+ }
+}
+
+// removeAllocFromTTLIndex removes an allocationID from TTL index.
+func (a *IPAllocations) removeAllocFromTTLIndex(allocID string) {
+ for i, alID := range a.TTLIndex {
+ if alID == allocID {
+ a.TTLIndex = slices.Delete(a.TTLIndex, i, i+1)
+ break
}
}
}
-// 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)
+// lock acquires a guardian lock on the IPAllocations and stores the lock ID.
+// Uses given lockID (assumes already acquired) or creates a new lock.
+func (a *IPAllocations) lock(lockID string) {
+ if lockID == "" {
+ lockID = guardian.Guardian.GuardIDs("",
+ config.CgrConfig().GeneralCfg().LockingTimeout,
+ ipAllocationsLockKey(a.Tenant, a.ID))
}
- return mp
+ a.lockID = lockID
}
-// 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,
+// unlock releases the lock on the IPAllocations and clears the stored lock ID.
+func (a *IPAllocations) unlock() {
+ if a.lockID == "" {
+ return
}
+ // Store current lock ID before clearing to prevent race conditions.
+ id := a.lockID
+ a.lockID = ""
+ guardian.Guardian.UnguardIDs(id)
+
+ if a.prfl != nil {
+ a.prfl.unlock()
+ }
}
-// IPService is the service handling ips
+// config returns the IPAllocations' profile configuration.
+func (a *IPAllocations) config() *IPProfile {
+ return a.prfl
+}
+
+// TenantID returns the unique ID in a multi-tenant environment
+func (a *IPAllocations) TenantID() string {
+ return utils.ConcatenatedKey(a.Tenant, a.ID)
+}
+
+// CacheClone returns a clone of IPAllocations object used by ltcache CacheCloner.
+func (a *IPAllocations) CacheClone() any {
+ return a.Clone()
+}
+
+// Clone creates a deep clone of the IPAllocations object (lockID excluded).
+func (a *IPAllocations) Clone() *IPAllocations {
+ if a == nil {
+ return nil
+ }
+ clone := &IPAllocations{
+ Tenant: a.Tenant,
+ ID: a.ID,
+ TTLIndex: slices.Clone(a.TTLIndex),
+ prfl: a.prfl.Clone(),
+ poolRanges: maps.Clone(a.poolRanges),
+ }
+ if a.poolAllocs != nil {
+ clone.poolAllocs = make(map[string]map[netip.Addr]string)
+ for poolID, allocs := range a.poolAllocs {
+ clone.poolAllocs[poolID] = maps.Clone(allocs)
+ }
+ }
+ if a.Allocations != nil {
+ clone.Allocations = make(map[string]*PoolAllocation, len(a.Allocations))
+ for id, alloc := range a.Allocations {
+ clone.Allocations[id] = alloc.Clone()
+ }
+ }
+ return clone
+}
+
+// IPAllocationsLockKey builds the guardian key for locking IP allocations.
+func ipAllocationsLockKey(tnt, id string) string {
+ return utils.ConcatenatedKey(utils.CacheIPAllocations, tnt, id)
+}
+
+// IPService is the service handling IP allocations
type IPService struct {
cfg *config.CGRConfig
+ dm *DataManager // So we can load the data in cache and index it
cm *ConnManager
- dm *DataManager
- fs *FilterS
+ fltrs *FilterS
storedIPsMux sync.RWMutex // protects storedIPs
- storedIPs utils.StringSet // keep a record of ips which need saving, map[ipID]bool
+ storedIPs utils.StringSet // keep a record of IP allocations which need saving, map[allocsID]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()
+// NewIPService returns a new IPService.
+func NewIPService(dm *DataManager, cfg *config.CGRConfig, fltrs *FilterS,
+ cm *ConnManager) *IPService {
+ return &IPService{dm: dm,
+ storedIPs: make(utils.StringSet),
+ cfg: cfg,
+ cm: cm,
+ fltrs: fltrs,
+ loopStopped: make(chan struct{}),
+ stopBackup: make(chan struct{}),
+ }
+}
+
+// Reload restarts the backup loop.
+func (s *IPService) Reload() {
+ close(s.stopBackup)
+ <-s.loopStopped // wait until the loop is done
+ s.stopBackup = make(chan struct{})
+ go s.runBackup()
}
// StartLoop starts the gorutine with the backup loop
-func (rS *IPService) StartLoop() {
- go rS.runBackup()
+func (s *IPService) StartLoop() {
+ go s.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")
+func (s *IPService) Shutdown() {
+ close(s.stopBackup)
+ s.storeIPAllocationsList()
}
-// backup will regularly store ips changed to dataDB
-func (rS *IPService) runBackup() {
- storeInterval := rS.cfg.IPsCfg().StoreInterval
+// runBackup will regularly update IP allocations stored in dataDB.
+func (s *IPService) runBackup() {
+ storeInterval := s.cfg.IPsCfg().StoreInterval
if storeInterval <= 0 {
- rS.loopStopped <- struct{}{}
+ s.loopStopped <- struct{}{}
return
}
for {
- rS.storeIPs()
+ s.storeIPAllocationsList()
select {
- case <-rS.stopBackup:
- rS.loopStopped <- struct{}{}
+ case <-s.stopBackup:
+ s.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)
+// storeIPAllocationsList represents one task of complete backup
+func (s *IPService) storeIPAllocationsList() {
+ var failedAllocIDs []string
+ for { // don't stop until we store all dirty IP allocations
+ s.storedIPsMux.Lock()
+ allocsID := s.storedIPs.GetOne()
+ if allocsID != "" {
+ s.storedIPs.Remove(allocsID)
}
- rS.storedIPsMux.Unlock()
- if rID == "" {
+ s.storedIPsMux.Unlock()
+ if allocsID == "" {
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))
+ allocIf, ok := Cache.Get(utils.CacheIPAllocations, allocsID)
+ if !ok || allocIf == nil {
+ utils.Logger.Warning(fmt.Sprintf(
+ "<%s> failed retrieving from cache IP allocations with ID %q", utils.IPs, allocsID))
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
+ allocs := allocIf.(*IPAllocations)
+ allocs.lock(utils.EmptyString)
+ if err := s.storeIPAllocations(allocs); err != nil {
+ utils.Logger.Warning(fmt.Sprintf("<%s> %v", utils.IPs, err))
+ failedAllocIDs = append(failedAllocIDs, allocsID) // record failure so we can schedule it for next backup
}
- r.unlock()
+ allocs.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()
+ if len(failedAllocIDs) != 0 { // there were errors on save, schedule the keys for next backup
+ s.storedIPsMux.Lock()
+ s.storedIPs.AddSlice(failedAllocIDs)
+ s.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
+// storeIPAllocations stores the IP allocations in DB.
+func (s *IPService) storeIPAllocations(allocs *IPAllocations) error {
+ if err := s.dm.SetIPAllocations(allocs); err != nil {
+ utils.Logger.Warning(fmt.Sprintf(
+ " could not save IP allocations %q: %v", allocs.ID, err))
+ return err
}
//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,
+ if tntID := allocs.TenantID(); Cache.HasItem(utils.CacheIPAllocations, tntID) { // only cache if previously there
+ if err := Cache.Set(utils.CacheIPAllocations, tntID, allocs, nil,
true, utils.NonTransactional); err != nil {
- utils.Logger.Warning(
- fmt.Sprintf(" failed caching IP with ID: %s, error: %s",
- tntID, err.Error()))
- return
+ utils.Logger.Warning(fmt.Sprintf(
+ " could not cache IP allocations %q: %v", tntID, err))
+ return err
}
}
- *r.dirty = false
- return
+ return nil
}
-// storeMatchedIPs will store the list of ips based on the StoreInterval
-func (s *IPService) storeMatchedIPs(matchedIPs IPs) (err error) {
+// storeMatchedIPAllocations will store the list of IP allocations based on the StoreInterval.
+func (s *IPService) storeMatchedIPAllocations(matched *IPAllocations) error {
if s.cfg.IPsCfg().StoreInterval == 0 {
- return
+ return nil
}
if s.cfg.IPsCfg().StoreInterval > 0 {
s.storedIPsMux.Lock()
- defer s.storedIPsMux.Unlock()
+ s.storedIPs.Add(matched.TenantID())
+ s.storedIPsMux.Unlock()
+ return nil
}
- 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
- }
- }
-
+ if err := s.storeIPAllocations(matched); err != nil {
+ return err
}
- return
+ return nil
}
-// 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
+// matchingIPAllocationsForEvent returns the IP allocation with the highest weight
+// matching the event.
+func (s *IPService) matchingIPAllocationsForEvent(tnt string,
+ ev *utils.CGREvent, evUUID string) (allocs *IPAllocations, err error) {
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}
+ var itemIDs []string
+ if x, ok := Cache.Get(utils.CacheEventIPs, evUUID); ok {
+ // IPIDs cached as utils.StringSet{"resID":bool}
if x == nil {
return nil, utils.ErrNotFound
}
- ipIDs = x.(utils.StringSet)
+ itemIDs = []string{x.(string)}
defer func() { // make sure we uncache if we find errors
if err != nil {
+ // TODO: Consider using RemoveWithoutReplicate instead, as
+ // partitions with Replicate=true call ReplicateRemove in
+ // onEvict by default.
if errCh := Cache.Remove(utils.CacheEventIPs, evUUID,
- cacheCommit(utils.NonTransactional), utils.NonTransactional); errCh != nil {
+ true, utils.NonTransactional); errCh != nil {
err = errCh
}
}
}()
-
- } else { // select the ipIDs out of dataDB
- ipIDs, err = MatchingItemIDsForEvent(evNm,
+ } else { // select the IP allocation IDs out of dataDB
+ matchedItemIDs, err := MatchingItemIDsForEvent(evNm,
s.cfg.IPsCfg().StringIndexedFields,
s.cfg.IPsCfg().PrefixIndexedFields,
s.cfg.IPsCfg().SuffixIndexedFields,
@@ -522,140 +629,150 @@ func (s *IPService) matchingIPsForEvent(tnt string, ev *utils.CGREvent,
)
if err != nil {
if err == utils.ErrNotFound {
- if errCh := Cache.Set(utils.CacheEventIPs, evUUID, nil, nil, true, ""); errCh != nil { // cache negative match
+ if errCh := Cache.Set(utils.CacheEventIPs, evUUID,
+ nil, nil, true, ""); errCh != nil { // cache negative match
return nil, errCh
}
}
- return
+ return nil, err
}
+ itemIDs = slices.Sorted(maps.Keys(matchedItemIDs))
}
- ips = make(IPs, 0, len(ipIDs))
- for id := range ipIDs {
+ var matchedPrfl *IPProfile
+ var maxWeight float64
+ for _, id := range itemIDs {
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 {
+ var prfl *IPProfile
+ if prfl, err = s.dm.GetIPProfile(tnt, id, true, true, utils.NonTransactional); err != nil {
guardian.Guardian.UnguardIDs(lkPrflID)
if err == utils.ErrNotFound {
continue
}
- ips.unlock()
- return
+ return nil, err
}
- profile.lock(lkPrflID)
- if profile.ActivationInterval != nil && ev.Time != nil &&
- !profile.ActivationInterval.IsActiveAtTime(*ev.Time) { // not active
- profile.unlock()
+ prfl.lock(lkPrflID)
+ if prfl.ActivationInterval != nil && ev.Time != nil &&
+ !prfl.ActivationInterval.IsActiveAtTime(*ev.Time) { // not active
+ prfl.unlock()
continue
}
var pass bool
- if pass, err = s.fs.Pass(tnt, profile.FilterIDs,
- evNm); err != nil {
- profile.unlock()
- ips.unlock()
+ if pass, err = s.fltrs.Pass(tnt, prfl.FilterIDs, evNm); err != nil {
+ prfl.unlock()
return nil, err
} else if !pass {
- profile.unlock()
+ prfl.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
+ if matchedPrfl == nil || maxWeight < prfl.Weight {
+ if matchedPrfl != nil {
+ matchedPrfl.unlock()
}
- } else if profile.TTL >= 0 {
- ip.ttl = utils.DurationPointer(profile.TTL)
+ matchedPrfl = prfl
+ maxWeight = prfl.Weight
+ } else {
+ prfl.unlock()
}
- ip.cfg = profile
- ips = append(ips, ip)
}
-
- if len(ips) == 0 {
+ if matchedPrfl == nil {
return nil, utils.ErrNotFound
}
- ips.Sort()
- if err = Cache.Set(utils.CacheEventIPs, evUUID, ips.ids(), nil, true, ""); err != nil {
- ips.unlock()
+ lkID := guardian.Guardian.GuardIDs(utils.EmptyString,
+ config.CgrConfig().GeneralCfg().LockingTimeout,
+ ipAllocationsLockKey(matchedPrfl.Tenant, matchedPrfl.ID))
+ allocs, err = s.dm.GetIPAllocations(matchedPrfl.Tenant, matchedPrfl.ID, true, true, "", matchedPrfl)
+ if err != nil {
+ guardian.Guardian.UnguardIDs(lkID)
+ matchedPrfl.unlock()
+ return nil, err
}
- return
+ allocs.lock(lkID)
+ if err = Cache.Set(utils.CacheEventIPs, evUUID, allocs.ID, nil, true, ""); err != nil {
+ allocs.unlock()
+ }
+ return allocs, nil
}
-// 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.AllocationID, 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
+// allocateFromPools attempts IP allocation across all pools in priority order.
+// Continues to next pool only if current pool returns ErrIPAlreadyAllocated.
+// Returns first successful allocation or the last allocation error.
+func (s *IPService) allocateFromPools(allocs *IPAllocations, allocID string,
+ poolIDs []string, dryRun bool) (*AllocatedIP, error) {
+ var err error
+ for _, poolID := range poolIDs {
+ pool := findPoolByID(allocs.config().Pools, poolID)
+ if pool == nil {
+ return nil, fmt.Errorf("pool %q: %w", poolID, utils.ErrNotFound)
+ }
+ var result *AllocatedIP
+ if result, err = allocs.allocateIPOnPool(allocID, pool, dryRun); err == nil {
+ return result, nil
+ }
+ if !errors.Is(err, utils.ErrIPAlreadyAllocated) {
+ return nil, err
}
- 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
+ return nil, err
}
-// V1AuthorizeIPs queries service to find if an Usage is allowed.
-func (s *IPService) V1AuthorizeIPs(ctx *context.Context, args *utils.CGREvent, reply *string) (err error) {
+func findPoolByID(pools []*IPPool, id string) *IPPool {
+ for _, pool := range pools {
+ if pool.ID == id {
+ return pool
+ }
+ }
+ return nil
+}
+
+// filterAndSortPools filters pools by their FilterIDs, sorts by weight
+// (highest first), and truncates at the first blocking pool.
+// TODO: check whether pre-allocating filteredPools & poolIDs improves
+// performance or wastes memory when filtering is aggressive.
+func filterAndSortPools(tenant string, pools []*IPPool,
+ fltrs *FilterS, ev utils.DataProvider) ([]string, error) {
+ var filteredPools []*IPPool
+ for _, pool := range pools {
+ pass, err := fltrs.Pass(tenant, pool.FilterIDs, ev)
+ if err != nil {
+ return nil, err
+ }
+ if !pass {
+ continue
+ }
+ filteredPools = append(filteredPools, pool)
+ }
+ if len(filteredPools) == 0 {
+ return nil, utils.ErrNotFound
+ }
+
+ // Sort by weight (higher values first).
+ slices.SortFunc(filteredPools, func(a, b *IPPool) int {
+ return cmp.Compare(b.Weight, a.Weight)
+ })
+
+ var poolIDs []string
+ for _, pool := range filteredPools {
+ poolIDs = append(poolIDs, pool.ID)
+ if pool.Blocker {
+ break
+ }
+ }
+ return poolIDs, nil
+}
+
+// V1GetIPAllocationForEvent returns the IPAllocations object matching the event.
+func (s *IPService) V1GetIPAllocationForEvent(ctx *context.Context, args *utils.CGREvent, reply *IPAllocations) (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.AllocationID, utils.OptsIPsUsageID)
- if usageID == utils.EmptyString {
- return utils.NewErrMandatoryIeMissing(utils.UsageID)
+ allocID := utils.GetStringOpts(args, s.cfg.IPsCfg().Opts.AllocationID, utils.OptsIPsAllocationID)
+ if allocID == utils.EmptyString {
+ return utils.NewErrMandatoryIeMissing(utils.AllocationID)
}
tnt := args.Tenant
if tnt == utils.EmptyString {
@@ -664,14 +781,14 @@ func (s *IPService) V1AuthorizeIPs(ctx *context.Context, args *utils.CGREvent, r
// RPC caching
if config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses].Limit != 0 {
- cacheKey := utils.ConcatenatedKey(utils.IPsV1AuthorizeIPs, utils.ConcatenatedKey(tnt, args.ID))
+ cacheKey := utils.ConcatenatedKey(utils.IPsV1GetIPAllocationForEvent, 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)
+ *reply = *cachedResp.Result.(*IPAllocations)
}
return cachedResp.Error
}
@@ -681,37 +798,26 @@ func (s *IPService) V1AuthorizeIPs(ctx *context.Context, args *utils.CGREvent, r
}
// 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 {
+ var allocs *IPAllocations
+ if allocs, err = s.matchingIPAllocationsForEvent(tnt, args, allocID); err != nil {
return err
}
- defer ips.unlock()
-
- /*
- authorize logic
- ...
- */
-
- *reply = utils.OK
+ defer allocs.unlock()
+ *reply = *allocs
return
}
-// V1AllocateIPs is called when a ip requires allocation.
-func (s *IPService) V1AllocateIPs(ctx *context.Context, args *utils.CGREvent, reply *string) (err error) {
+// V1AuthorizeIP checks if it's able to allocate an IP address for the given event.
+func (s *IPService) V1AuthorizeIP(ctx *context.Context, args *utils.CGREvent, reply *AllocatedIP) (err error) {
if args == nil {
return utils.NewErrMandatoryIeMissing(utils.Event)
}
- if missing := utils.MissingStructFields(args, []string{utils.ID, utils.Event}); len(missing) != 0 {
+ 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.AllocationID, utils.OptsIPsUsageID)
- if usageID == utils.EmptyString {
- return utils.NewErrMandatoryIeMissing(utils.UsageID)
+ allocID := utils.GetStringOpts(args, s.cfg.IPsCfg().Opts.AllocationID, utils.OptsIPsAllocationID)
+ if allocID == utils.EmptyString {
+ return utils.NewErrMandatoryIeMissing(utils.AllocationID)
}
tnt := args.Tenant
if tnt == utils.EmptyString {
@@ -720,14 +826,14 @@ func (s *IPService) V1AllocateIPs(ctx *context.Context, args *utils.CGREvent, re
// RPC caching
if config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses].Limit != 0 {
- cacheKey := utils.ConcatenatedKey(utils.IPsV1AllocateIPs, utils.ConcatenatedKey(tnt, args.ID))
+ cacheKey := utils.ConcatenatedKey(utils.IPsV1AuthorizeIP, 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)
+ *reply = *cachedResp.Result.(*AllocatedIP)
}
return cachedResp.Error
}
@@ -737,42 +843,102 @@ func (s *IPService) V1AllocateIPs(ctx *context.Context, args *utils.CGREvent, re
}
// 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 {
+ var allocs *IPAllocations
+ if allocs, err = s.matchingIPAllocationsForEvent(tnt, args, allocID); err != nil {
return err
}
- defer ips.unlock()
+ defer allocs.unlock()
- /*
- allocate logic
- ...
- */
+ var poolIDs []string
+ if poolIDs, err = filterAndSortPools(tnt, allocs.config().Pools, s.fltrs,
+ args.AsDataProvider()); err != nil {
+ return err
+ }
+
+ var allocIP *AllocatedIP
+ if allocIP, err = s.allocateFromPools(allocs, allocID, poolIDs, true); err != nil {
+ if errors.Is(err, utils.ErrIPAlreadyAllocated) {
+ return utils.ErrIPUnauthorized
+ }
+ return err
+ }
+
+ *reply = *allocIP
+ return nil
+}
+
+// V1AllocateIP allocates an IP address for the given event.
+func (s *IPService) V1AllocateIP(ctx *context.Context, args *utils.CGREvent, reply *AllocatedIP) (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...)
+ }
+ allocID := utils.GetStringOpts(args, s.cfg.IPsCfg().Opts.AllocationID, utils.OptsIPsAllocationID)
+ if allocID == utils.EmptyString {
+ return utils.NewErrMandatoryIeMissing(utils.AllocationID)
+ }
+ 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.IPsV1AllocateIP, 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.(*AllocatedIP)
+ }
+ return cachedResp.Error
+ }
+ defer Cache.Set(utils.CacheRPCResponses, cacheKey,
+ &utils.CachedRPCResponse{Result: reply, Error: err},
+ nil, true, utils.NonTransactional)
+ }
+ // end of RPC caching
+
+ var allocs *IPAllocations
+ if allocs, err = s.matchingIPAllocationsForEvent(tnt, args, allocID); err != nil {
+ return err
+ }
+ defer allocs.unlock()
+
+ var poolIDs []string
+ if poolIDs, err = filterAndSortPools(tnt, allocs.config().Pools, s.fltrs,
+ args.AsDataProvider()); err != nil {
+ return err
+ }
+
+ var allocIP *AllocatedIP
+ if allocIP, err = s.allocateFromPools(allocs, allocID, poolIDs, false); err != nil {
+ return err
+ }
// index it for storing
- if err = s.storeMatchedIPs(ips); err != nil {
- return
+ if err = s.storeMatchedIPAllocations(allocs); err != nil {
+ return err
}
- *reply = utils.OK
- return
+ *reply = *allocIP
+ return nil
}
-// V1ReleaseIPs is called when we need to clear an allocation.
-func (s *IPService) V1ReleaseIPs(ctx *context.Context, args *utils.CGREvent, reply *string) (err error) {
+// V1ReleaseIP releases an allocated IP address for the given event.
+func (s *IPService) V1ReleaseIP(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 {
+ 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.AllocationID, utils.OptsIPsUsageID)
- if usageID == utils.EmptyString {
- return utils.NewErrMandatoryIeMissing(utils.UsageID)
+ allocID := utils.GetStringOpts(args, s.cfg.IPsCfg().Opts.AllocationID, utils.OptsIPsAllocationID)
+ if allocID == utils.EmptyString {
+ return utils.NewErrMandatoryIeMissing(utils.AllocationID)
}
tnt := args.Tenant
if tnt == utils.EmptyString {
@@ -781,7 +947,7 @@ func (s *IPService) V1ReleaseIPs(ctx *context.Context, args *utils.CGREvent, rep
// RPC caching
if config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses].Limit != 0 {
- cacheKey := utils.ConcatenatedKey(utils.IPsV1ReleaseIPs, utils.ConcatenatedKey(tnt, args.ID))
+ cacheKey := utils.ConcatenatedKey(utils.IPsV1ReleaseIP, 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)
@@ -798,33 +964,29 @@ func (s *IPService) V1ReleaseIPs(ctx *context.Context, args *utils.CGREvent, rep
}
// 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 allocs *IPAllocations
+ if allocs, err = s.matchingIPAllocationsForEvent(tnt, args, allocID); err != nil {
+ return err
}
- var ips IPs
- if ips, err = s.matchingIPsForEvent(tnt, args, usageID,
- ttl); err != nil {
- return
- }
- defer ips.unlock()
+ defer allocs.unlock()
- /*
- release logic
- ...
- */
-
- if err = s.storeMatchedIPs(ips); err != nil {
- return
+ if err = allocs.releaseAllocation(allocID); err != nil {
+ utils.Logger.Warning(fmt.Sprintf(
+ "<%s> failed to remove allocation from IPAllocations with ID %q: %v", utils.IPs, allocs.TenantID(), err))
}
+
+ // Handle storing
+ if err = s.storeMatchedIPAllocations(allocs); err != nil {
+ return err
+ }
+
*reply = utils.OK
- return
+ return nil
}
-// 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 {
+// V1GetIPAllocations returns all IP allocations for a tenantID.
+func (s *IPService) V1GetIPAllocations(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *IPAllocations) error {
+ if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing
return utils.NewErrMandatoryIeMissing(missing...)
}
tnt := arg.Tenant
@@ -832,15 +994,48 @@ func (s *IPService) V1GetIP(ctx *context.Context, arg *utils.TenantIDWithAPIOpts
tnt = s.cfg.GeneralCfg().DefaultTenant
}
+ // make sure resource is locked at process level
lkID := guardian.Guardian.GuardIDs(utils.EmptyString,
config.CgrConfig().GeneralCfg().LockingTimeout,
- ipLockKey(tnt, arg.ID))
+ ipAllocationsLockKey(tnt, arg.ID))
defer guardian.Guardian.UnguardIDs(lkID)
- ip, err := s.dm.GetIP(tnt, arg.ID, true, true, utils.NonTransactional)
+ ip, err := s.dm.GetIPAllocations(tnt, arg.ID, true, true, utils.NonTransactional, nil)
if err != nil {
return err
}
*reply = *ip
return nil
}
+
+// V1ClearIPAllocations clears IP allocations from an IPAllocations object.
+// If args.AllocationIDs is empty or nil, all allocations will be cleared.
+func (s *IPService) V1ClearIPAllocations(ctx *context.Context, args *ClearIPAllocationsArgs, reply *string) error {
+ if missing := utils.MissingStructFields(args, []string{utils.ID}); len(missing) != 0 {
+ return utils.NewErrMandatoryIeMissing(missing...)
+ }
+
+ tnt := args.Tenant
+ if tnt == utils.EmptyString {
+ tnt = s.cfg.GeneralCfg().DefaultTenant
+ }
+
+ lkID := guardian.Guardian.GuardIDs(utils.EmptyString,
+ config.CgrConfig().GeneralCfg().LockingTimeout,
+ ipAllocationsLockKey(tnt, args.ID))
+ defer guardian.Guardian.UnguardIDs(lkID)
+
+ allocs, err := s.dm.GetIPAllocations(tnt, args.ID, true, true, utils.NonTransactional, nil)
+ if err != nil {
+ return err
+ }
+ if err := allocs.clearAllocations(args.AllocationIDs); err != nil {
+ return err
+ }
+ if err := s.storeIPAllocations(allocs); err != nil {
+ return err
+ }
+
+ *reply = utils.OK
+ return nil
+}
diff --git a/engine/ips_test.go b/engine/ips_test.go
deleted file mode 100644
index 45060108c..000000000
--- a/engine/ips_test.go
+++ /dev/null
@@ -1,833 +0,0 @@
-/*
-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 Affero 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 Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see
-*/
-
-package engine
-
-import (
- "reflect"
- "slices"
- "testing"
- "time"
-
- "github.com/cgrates/cgrates/config"
- "github.com/cgrates/cgrates/utils"
-)
-
-func TestIPProfileClone(t *testing.T) {
- activation := time.Date(2025, 7, 21, 10, 0, 0, 0, time.UTC)
- expiry := time.Date(2025, 12, 31, 23, 59, 59, 0, time.UTC)
-
- ip := &IPProfile{
- Tenant: "cgrates.org",
- ID: "ip_profile_1",
- FilterIDs: []string{"flt1", "flt2"},
- ActivationInterval: &utils.ActivationInterval{
- ActivationTime: activation,
- ExpiryTime: expiry,
- },
- TTL: 10 * time.Minute,
- Type: "ipv4",
- AddressPool: "main_pool",
- Allocation: "dynamic",
- Stored: true,
- Weight: 2.5,
- }
-
- cloned := ip.Clone()
-
- if cloned == ip {
- t.Error("Clone returned the same reference as original")
- }
- if cloned.Tenant != ip.Tenant {
- t.Error("Tenant mismatch")
- }
- if cloned.ID != ip.ID {
- t.Error("ID mismatch")
- }
- if cloned.Type != ip.Type {
- t.Error("Type mismatch")
- }
- if cloned.AddressPool != ip.AddressPool {
- t.Error("AddressPool mismatch")
- }
- if cloned.Allocation != ip.Allocation {
- t.Error("Allocation mismatch")
- }
- if cloned.Stored != ip.Stored {
- t.Error("Stored mismatch")
- }
- if cloned.Weight != ip.Weight {
- t.Error("Weight mismatch")
- }
- if cloned.TTL != ip.TTL {
- t.Error("TTL mismatch")
- }
-
- if cloned.ActivationInterval == ip.ActivationInterval {
- t.Error("ActivationInterval not deeply cloned")
- }
- if cloned.ActivationInterval.ActivationTime != ip.ActivationInterval.ActivationTime {
- t.Error("ActivationTime mismatch")
- }
- if cloned.ActivationInterval.ExpiryTime != ip.ActivationInterval.ExpiryTime {
- t.Error("ExpiryTime mismatch")
- }
-
- if len(cloned.FilterIDs) != len(ip.FilterIDs) {
- t.Error("FilterIDs length mismatch")
- }
- for i := range cloned.FilterIDs {
- if cloned.FilterIDs[i] != ip.FilterIDs[i] {
- t.Errorf("FilterIDs[%d] mismatch", i)
- }
- }
-
- cloned.FilterIDs[0] = "changed"
- if ip.FilterIDs[0] == "changed" {
- t.Error("Original FilterIDs changed after modifying clone")
- }
-
- var nilIP *IPProfile
- clonedNil := nilIP.Clone()
- if clonedNil != nil {
- t.Error("Clone of nil IPProfile should return nil")
- }
-}
-
-func TestIPProfileCacheClone(t *testing.T) {
- ip := &IPProfile{
- Tenant: "cgrates.org",
- ID: "ip_cache_test",
- FilterIDs: []string{"fltA"},
- ActivationInterval: &utils.ActivationInterval{
- ActivationTime: time.Date(2025, 7, 21, 0, 0, 0, 0, time.UTC),
- ExpiryTime: time.Date(2025, 12, 31, 23, 59, 59, 0, time.UTC),
- },
- Type: "ipv6",
- TTL: 5 * time.Minute,
- AddressPool: "test_pool",
- Allocation: "static",
- Stored: true,
- Weight: 1.0,
- }
-
- clonedAny := ip.CacheClone()
- cloned, ok := clonedAny.(*IPProfile)
- if !ok {
- t.Error("CacheClone did not return *IPProfile")
- }
- if cloned == ip {
- t.Error("CacheClone returned the same reference instead of a clone")
- }
- if cloned.ID != ip.ID || cloned.Tenant != ip.Tenant {
- t.Error("Cloned fields do not match original")
- }
- cloned.FilterIDs[0] = "modified"
- if ip.FilterIDs[0] == "modified" {
- t.Error("Original FilterIDs modified")
- }
-
- var nilIP *IPProfile
- clonedNil := nilIP.CacheClone()
- if clonedNil != nil {
- if _, ok := clonedNil.(*IPProfile); !ok {
- t.Error("CacheClone on nil receiver should return nil")
- }
- }
-}
-
-func TestIPProfileLockKey(t *testing.T) {
- tenant := "cgrates.org"
- id := "1001"
-
- got := ipProfileLockKey(tenant, id)
- want := utils.CacheIPProfiles + ":" + tenant + ":" + id
- if got != want {
- t.Errorf("ipProfileLockKey() = %q; want %q", got, want)
- }
- got = ipProfileLockKey("", "")
- want = utils.CacheIPProfiles + "::"
- if got != want {
- t.Errorf("ipProfileLockKey() with empty strings = %q; want %q", got, want)
- }
-}
-
-func TestIPProfilelock(t *testing.T) {
- ip := &IPProfile{
- Tenant: "cgrates.org",
- ID: "profile123",
- }
-
- ip.lock("IDlock")
- if ip.lkID != "IDlock" {
- t.Errorf("expected lkID 'IDlock', got %q", ip.lkID)
- }
-
- ip.lock(utils.EmptyString)
- if ip.lkID == "" {
- t.Error("expected lkID to be set by guardian but got empty string")
- }
-}
-
-func TestIPProfileUnlock(t *testing.T) {
- ip := &IPProfile{}
- ip.lkID = utils.EmptyString
- ip.unlock()
- if ip.lkID != utils.EmptyString {
- t.Errorf("expected lkID to remain empty, got %q", ip.lkID)
- }
- ip.lkID = "Id"
- ip.unlock()
- if ip.lkID != utils.EmptyString {
- t.Errorf("expected lkID to be cleared, got %q", ip.lkID)
- }
-}
-
-func TestIPUsageTenantID(t *testing.T) {
- u := &IPUsage{
- Tenant: "cgrates.org",
- ID: "usage01",
- }
- got := u.TenantID()
- want := "cgrates.org:usage01"
- if got != want {
- t.Errorf("TenantID() = %q; want %q", got, want)
- }
-
- u.Tenant = ""
- u.ID = ""
- got = u.TenantID()
- want = ":"
- if got != want {
- t.Errorf("TenantID() with empty = %q; want %q", got, want)
- }
-}
-
-func TestIPUsageIsActive(t *testing.T) {
- now := time.Now()
-
- u := &IPUsage{
- ExpiryTime: now.Add(1 * time.Hour),
- }
- if !u.isActive(now) {
- t.Errorf("Expected active usage, got inactive")
- }
-
- u.ExpiryTime = now.Add(-1 * time.Hour)
- if u.isActive(now) {
- t.Errorf("Expected inactive usage, got active")
- }
-
- u.ExpiryTime = time.Time{}
- if !u.isActive(now) {
- t.Errorf("Expected active usage for zero expiry time, got inactive")
- }
-}
-
-func TestIPTotalUsage(t *testing.T) {
- ip := &IP{
- Usages: map[string]*IPUsage{
- "u1": {Units: 1.5},
- "u2": {Units: 2.0},
- "u3": {Units: 0.5},
- },
- }
-
- expected := 4.0
- got := ip.TotalUsage()
- if got != expected {
- t.Errorf("TotalUsage() = %v, want %v", got, expected)
- }
-
- gotCached := ip.TotalUsage()
- if gotCached != expected {
- t.Errorf("TotalUsage() after cache = %v, want %v", gotCached, expected)
- }
-}
-
-func TestIPUsageClone(t *testing.T) {
- original := &IPUsage{
- Tenant: "cgrates.org",
- ID: "ID1001",
- ExpiryTime: time.Now().Add(24 * time.Hour),
- }
-
- cloned := original.Clone()
-
- if cloned == nil {
- t.Fatal("expected clone not to be nil")
- }
- if cloned == original {
- t.Error("expected clone to be a different instance")
- }
- if *cloned != *original {
- t.Errorf("expected clone to have same content, got %+v, want %+v", cloned, original)
- }
-
- var nilUsage *IPUsage
- nilClone := nilUsage.Clone()
- if nilClone != nil {
- t.Error("expected nil clone for nil input")
- }
-}
-
-func TestIPClone(t *testing.T) {
- ttl := 5 * time.Minute
- tUsage := 123.45
- dirty := true
- expTime := time.Now().Add(time.Hour)
-
- original := &IP{
- Tenant: "cgrates.org",
- ID: "ip01",
- TTLIdx: []string{"idx1", "idx2"},
- cfg: &IPProfile{
- Tenant: "cgrates.org",
- ID: "profile1",
- FilterIDs: []string{"f1", "f2"},
- TTL: time.Minute * 10,
- Type: "dynamic",
- },
- Usages: map[string]*IPUsage{
- "u1": {
- Tenant: "cgrates.org",
- ID: "u1",
- ExpiryTime: expTime,
- Units: 50.0,
- },
- },
- ttl: &ttl,
- tUsage: &tUsage,
- dirty: &dirty,
- }
-
- cloned := original.Clone()
-
- t.Run("nil IP returns nil", func(t *testing.T) {
- var ip *IP = nil
- cloned := ip.Clone()
- if cloned != nil {
- t.Errorf("expected nil, got %+v", cloned)
- }
- })
-
- if !reflect.DeepEqual(original, cloned) {
- t.Errorf("Expected clone to be deeply equal to original, got difference:\noriginal: %+v\nclone: %+v", original, cloned)
- }
-
- if cloned == original {
- t.Error("Clone should return a different pointer than the original")
- }
- if cloned.cfg == original.cfg {
- t.Error("cfg field was not deeply cloned")
- }
- if cloned.Usages["u1"] == original.Usages["u1"] {
- t.Error("Usages map content was not deeply cloned")
- }
- if cloned.ttl == original.ttl {
- t.Error("ttl pointer was not deeply cloned")
- }
- if cloned.tUsage == original.tUsage {
- t.Error("tUsage pointer was not deeply cloned")
- }
- if cloned.dirty == original.dirty {
- t.Error("dirty pointer was not deeply cloned")
- }
-}
-
-func TestIpLockKey(t *testing.T) {
- tnt := "cgrates.org"
- id := "192.168.0.1"
- expected := utils.ConcatenatedKey(utils.CacheIPs, tnt, id)
-
- got := ipLockKey(tnt, id)
- if got != expected {
- t.Errorf("Expected %s, got %s", expected, got)
- }
-}
-
-func TestIPlock(t *testing.T) {
- ip := &IP{Tenant: "cgrates.org", ID: "1001"}
-
- ip.lock("customLockID")
- if ip.lkID != "customLockID" {
- t.Errorf("Expected lkID to be 'customLockID', got %s", ip.lkID)
- }
-
- ip2 := &IP{Tenant: "cgrates.org2", ID: "1002"}
- ip2.lock(utils.EmptyString)
- if ip2.lkID == utils.EmptyString {
- t.Error("Expected lkID to be set by Guardian.GuardIDs, got empty string")
- }
-}
-
-func TestIPUunlock(t *testing.T) {
- ip := &IP{lkID: "LockID"}
-
- ip.unlock()
-
- if ip.lkID != utils.EmptyString {
- t.Errorf("Expected lkID to be cleared, got %s", ip.lkID)
- }
- ip.unlock()
-}
-
-func TestIPRemoveExpiredUnits(t *testing.T) {
- now := time.Now()
- expiredID := "expired"
- activeID := "active"
-
- ip := &IP{
- ID: "ip-test",
- Usages: map[string]*IPUsage{},
- TTLIdx: []string{expiredID, activeID},
- tUsage: utils.Float64Pointer(30.0),
- }
-
- ip.Usages[expiredID] = &IPUsage{
- ID: expiredID,
- ExpiryTime: now.Add(-10 * time.Minute),
- Units: 10.0,
- }
-
- ip.Usages[activeID] = &IPUsage{
- ID: activeID,
- ExpiryTime: now.Add(10 * time.Minute),
- Units: 20.0,
- }
-
- ip.removeExpiredUnits()
-
- if _, ok := ip.Usages[expiredID]; ok {
- t.Errorf("Expected expired usage to be removed")
- }
- if _, ok := ip.Usages[activeID]; !ok {
- t.Errorf("Expected active usage to be retained")
- }
- if ip.tUsage != nil {
- t.Errorf("Expected tUsage to be set to nil after recalculation")
- }
- if len(ip.TTLIdx) != 1 || ip.TTLIdx[0] != activeID {
- t.Errorf("Expected TTLIdx to only contain activeID")
- }
-}
-
-func TestIPRecordUsage(t *testing.T) {
- t.Run("record new usage with ttl set", func(t *testing.T) {
- ttl := 10 * time.Minute
- ip := &IP{
- Usages: make(map[string]*IPUsage),
- TTLIdx: []string{},
- ttl: &ttl,
- tUsage: new(float64),
- }
- usage := &IPUsage{Tenant: "cgrates.org", ID: "usage1", Units: 5}
- err := ip.recordUsage(usage)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if got, want := len(ip.Usages), 1; got != want {
- t.Fatalf("unexpected number of usages: got %d, want %d", got, want)
- }
- if _, ok := ip.Usages[usage.ID]; !ok {
- t.Fatal("usage not recorded")
- }
- if len(ip.TTLIdx) != 1 || ip.TTLIdx[0] != usage.ID {
- t.Fatal("TTLIdx not updated properly")
- }
- if ip.tUsage == nil || *ip.tUsage != usage.Units {
- t.Fatalf("tUsage not updated properly, got %v", ip.tUsage)
- }
- })
-
- t.Run("duplicate usage id", func(t *testing.T) {
- ip := &IP{
- Usages: map[string]*IPUsage{
- "usage1": {Tenant: "cgrates.org", ID: "usage1", Units: 5},
- },
- }
- usage := &IPUsage{Tenant: "cgrates.org", ID: "usage1", Units: 10}
- err := ip.recordUsage(usage)
- if err == nil {
- t.Fatal("expected error on duplicate usage id, got nil")
- }
- })
-
- t.Run("ttl zero disables expiry setting", func(t *testing.T) {
- zeroTTL := time.Duration(0)
- ip := &IP{
- Usages: make(map[string]*IPUsage),
- ttl: &zeroTTL,
- }
- usage := &IPUsage{Tenant: "cgrates.org", ID: "noexpiry", Units: 2}
- err := ip.recordUsage(usage)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if len(ip.Usages) != 0 {
- t.Fatal("usage should NOT be recorded when ttl is 0")
- }
- })
-}
-
-func TestIPClearUsage(t *testing.T) {
- t.Run("usage not found", func(t *testing.T) {
- ip := &IP{Usages: make(map[string]*IPUsage)}
- err := ip.clearUsage("missing")
- if err == nil {
- t.Fatal("expected error, got nil")
- }
- expected := "cannot find usage record with id: missing"
- if err.Error() != expected {
- t.Errorf("expected error %q, got %q", expected, err.Error())
- }
- })
-
- t.Run("usage found with zero expiry time", func(t *testing.T) {
- totalUsage := 10.0
- ip := &IP{
- Usages: map[string]*IPUsage{
- "id1": {Units: 5.0},
- },
- tUsage: &totalUsage,
- TTLIdx: []string{"id2"},
- }
-
- err := ip.clearUsage("id1")
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
-
- if _, exists := ip.Usages["id1"]; exists {
- t.Error("expected id1 to be deleted from Usages")
- }
- if *ip.tUsage != 5.0 {
- t.Errorf("expected tUsage=5.0, got %v", *ip.tUsage)
- }
- if !slices.Equal(ip.TTLIdx, []string{"id2"}) {
- t.Errorf("TTLIdx changed unexpectedly: %v", ip.TTLIdx)
- }
- })
-
- t.Run("usage found with non-zero expiry time", func(t *testing.T) {
- totalUsage := 20.0
- expTime := time.Now().Add(time.Hour)
- ip := &IP{
- Usages: map[string]*IPUsage{
- "id2": {ExpiryTime: expTime, Units: 7.0},
- },
- TTLIdx: []string{"id2", "other"},
- tUsage: &totalUsage,
- }
-
- err := ip.clearUsage("id2")
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
-
- if _, exists := ip.Usages["id2"]; exists {
- t.Error("expected id2 to be deleted from Usages")
- }
- if *ip.tUsage != 13.0 {
- t.Errorf("expected tUsage=13.0, got %v", *ip.tUsage)
- }
- if slices.Contains(ip.TTLIdx, "id2") {
- t.Errorf("expected id2 to be removed from TTLIdx, got %v", ip.TTLIdx)
- }
- })
-}
-
-func TestIPsSort(t *testing.T) {
- ip1 := &IP{cfg: &IPProfile{Weight: 1.0}}
- ip2 := &IP{cfg: &IPProfile{Weight: 3.0}}
- ip3 := &IP{cfg: &IPProfile{Weight: 2.0}}
-
- ips := IPs{ip1, ip2, ip3}
- ips.Sort()
-
- expected := IPs{ip2, ip3, ip1}
- for i := range ips {
- if ips[i] != expected[i] {
- t.Errorf("expected index %d to be %+v, got %+v", i, expected[i], ips[i])
- }
- }
-}
-
-func TestIPsUnlock(t *testing.T) {
- ip1 := &IP{lkID: "lock1", cfg: &IPProfile{lkID: "cfgLock1"}}
- ip2 := &IP{lkID: "lock2", cfg: &IPProfile{lkID: "cfgLock2"}}
- ip3 := &IP{lkID: "lock3", cfg: nil}
-
- ips := IPs{ip1, ip2, ip3}
- ips.unlock()
-
- if ip1.lkID != "" {
- t.Errorf("expected ip1.lkID to be empty, got %q", ip1.lkID)
- }
- if ip2.lkID != "" {
- t.Errorf("expected ip2.lkID to be empty, got %q", ip2.lkID)
- }
- if ip3.lkID != "" {
- t.Errorf("expected ip3.lkID to be empty, got %q", ip3.lkID)
- }
-
- if ip1.cfg.lkID != "" {
- t.Errorf("expected ip1.cfg.lkID to be empty, got %q", ip1.cfg.lkID)
- }
- if ip2.cfg.lkID != "" {
- t.Errorf("expected ip2.cfg.lkID to be empty, got %q", ip2.cfg.lkID)
- }
-}
-
-func TestIPsIds(t *testing.T) {
- ip1 := &IP{ID: "ip1"}
- ip2 := &IP{ID: "ip2"}
- ip3 := &IP{ID: "ip3"}
-
- ips := IPs{ip1, ip2, ip3}
-
- got := ips.ids()
-
- if len(got) != 3 {
- t.Errorf("expected 3 IDs in set, got %d", len(got))
- }
-
- expectedIDs := []string{"ip1", "ip2", "ip3"}
- for _, id := range expectedIDs {
- if _, exists := got[id]; !exists {
- t.Errorf("expected ID %q in set, but it was missing", id)
- }
- }
-}
-
-func TestIPCacheClone(t *testing.T) {
- ttl := 10 * time.Minute
- tUsage := 123.45
- dirty := true
-
- original := &IP{
- Tenant: "cgrates.org",
- ID: "ip01",
- TTLIdx: []string{"idx1", "idx2"},
- Usages: map[string]*IPUsage{
- "u1": {Tenant: "cgrates.org", ID: "u1", Units: 50.0},
- },
- ttl: &ttl,
- tUsage: &tUsage,
- dirty: &dirty,
- cfg: &IPProfile{
- Tenant: "cgrates.org",
- ID: "profile1",
- Weight: 1.5,
- },
- }
-
- cloneAny := original.CacheClone()
- if cloneAny == nil {
- t.Fatal("CacheClone returned nil")
- }
-
- clone, ok := cloneAny.(*IP)
- if !ok {
- t.Fatalf("expected type *IP, got %T", cloneAny)
- }
-
- if clone == original {
- t.Error("expected clone to be a different pointer than original")
- }
-
- if !reflect.DeepEqual(clone, original) {
- t.Errorf("expected clone to be deeply equal to original:\noriginal: %+v\nclone: %+v", original, clone)
- }
-}
-
-func TestNewIPService(t *testing.T) {
- dm := &DataManager{}
- cgrcfg := &config.CGRConfig{}
- filterS := &FilterS{}
- connMgr := &ConnManager{}
-
- service := NewIPService(dm, cgrcfg, filterS, connMgr)
- if service == nil {
- t.Fatal("expected NewIPService to return non-nil")
- }
-
- if service.dm != dm {
- t.Errorf("expected dm=%+v, got %+v", dm, service.dm)
- }
- if service.cfg != cgrcfg {
- t.Errorf("expected cfg=%+v, got %+v", cgrcfg, service.cfg)
- }
- if service.fs != filterS {
- t.Errorf("expected fs=%+v, got %+v", filterS, service.fs)
- }
- if service.cm != connMgr {
- t.Errorf("expected cm=%+v, got %+v", connMgr, service.cm)
- }
-
- if service.storedIPs == nil {
- t.Error("expected storedIPs map to be initialized")
- }
- if service.loopStopped == nil {
- t.Error("expected loopStopped channel to be initialized")
- }
- if service.stopBackup == nil {
- t.Error("expected stopBackup channel to be initialized")
- }
-}
-
-func TestIPServiceStoreMatchedIPs(t *testing.T) {
- cfg := config.NewDefaultCGRConfig()
- data, err := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items)
- if err != nil {
- t.Error(err)
- }
- dm := NewDataManager(data, config.CgrConfig().CacheCfg(), nil)
-
- t.Run("StoreInterval=0", func(t *testing.T) {
- cfg.IPsCfg().StoreInterval = 0
- svc := &IPService{
- cfg: cfg,
- dm: dm,
- storedIPs: make(utils.StringSet),
- }
-
- dirty := false
- ip := &IP{Tenant: "cgrates.org", ID: "ip01", dirty: &dirty}
-
- err := svc.storeMatchedIPs(IPs{ip})
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if dirty {
- t.Errorf("expected dirty=false, got true")
- }
- if len(svc.storedIPs) != 0 {
- t.Errorf("expected storedIPs empty, got %+v", svc.storedIPs)
- }
- })
-
- t.Run("StoreInterval>0 marks dirty and schedules for backup", func(t *testing.T) {
- cfg.IPsCfg().StoreInterval = 10
- svc := &IPService{
- cfg: cfg,
- dm: dm,
- storedIPs: make(utils.StringSet),
- }
-
- dirty := false
- ip := &IP{Tenant: "cgrates.org", ID: "ip02", dirty: &dirty}
-
- err := svc.storeMatchedIPs(IPs{ip})
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if !dirty {
- t.Errorf("expected dirty=true, got false")
- }
- if _, exists := svc.storedIPs[ip.TenantID()]; !exists {
- t.Errorf("expected ip02 in storedIPs, got %+v", svc.storedIPs)
- }
- })
-
- t.Run("StoreInterval<0 stores immediately", func(t *testing.T) {
- cfg.IPsCfg().StoreInterval = -1
- svc := &IPService{
- cfg: cfg,
- dm: dm,
- storedIPs: make(utils.StringSet),
- }
-
- dirty := true
- ip := &IP{Tenant: "cgrates.org", ID: "ip03", dirty: &dirty}
-
- Cache.Set(utils.CacheIPs, ip.TenantID(), ip, nil, true, utils.NonTransactional)
-
- err := svc.storeMatchedIPs(IPs{ip})
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if dirty {
- t.Errorf("expected dirty=false after storeIP, got true")
- }
- })
-}
-
-func TestIPServiceMatchingIPsForEvent(t *testing.T) {
- cfg := config.NewDefaultCGRConfig()
- data, err := NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items)
- if err != nil {
- t.Error(err)
- }
- dm := NewDataManager(data, cfg.CacheCfg(), nil)
- fs := NewFilterS(cfg, nil, dm)
-
- svc := &IPService{
- cfg: cfg,
- dm: dm,
- fs: fs,
- }
-
- profile := &IPProfile{
- Tenant: "cgrates.org",
- ID: "ip01",
- Stored: true,
- TTL: 5 * time.Minute,
- }
- if err := dm.SetIPProfile(profile, true); err != nil {
- t.Fatalf("failed to set IPProfile: %v", err)
- }
-
- ip := &IP{
- Tenant: "cgrates.org",
- ID: "ip01",
- }
- Cache.Set(utils.CacheIPs, ip.TenantID(), ip, nil, true, utils.NonTransactional)
- if err := dm.SetIP(ip); err != nil {
- t.Fatalf("failed to set IP: %v", err)
- }
-
- ev := &utils.CGREvent{
- Event: map[string]interface{}{
- "type": "testEvent",
- },
- Time: func() *time.Time { t := time.Now(); return &t }(),
- }
- evUUID := "event-uuid-001"
-
- ips, err := svc.matchingIPsForEvent("cgrates.org", ev, evUUID, nil)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
-
- if len(ips) != 1 {
- t.Errorf("expected 1 IP, got %d", len(ips))
- }
- if ips[0].ID != "ip01" {
- t.Errorf("expected IP ID 'ip01', got '%s'", ips[0].ID)
- }
-
- if ips[0].ttl == nil || *ips[0].ttl != profile.TTL {
- t.Errorf("expected IP TTL %v, got %v", profile.TTL, ips[0].ttl)
- }
-
- cached, ok := Cache.Get(utils.CacheEventIPs, evUUID)
- if !ok || cached == nil {
- t.Errorf("expected cached IPIDs for event UUID")
- }
-}
diff --git a/engine/libtest.go b/engine/libtest.go
index be775d325..f1647572d 100644
--- a/engine/libtest.go
+++ b/engine/libtest.go
@@ -280,7 +280,7 @@ func GetDefaultEmptyCacheStats() map[string]*ltcache.CacheStats {
utils.CacheResources: {},
utils.CacheIPFilterIndexes: {},
utils.CacheIPProfiles: {},
- utils.CacheIPs: {},
+ utils.CacheIPAllocations: {},
utils.CacheReverseDestinations: {},
utils.CacheRPCResponses: {},
utils.CacheSharedGroups: {},
diff --git a/engine/libtest_test.go b/engine/libtest_test.go
index cbbf5a468..f30888d1e 100644
--- a/engine/libtest_test.go
+++ b/engine/libtest_test.go
@@ -58,7 +58,7 @@ func TestGetDefaultEmptyCacheStats(t *testing.T) {
utils.CacheResources,
utils.CacheIPFilterIndexes,
utils.CacheIPProfiles,
- utils.CacheIPs,
+ utils.CacheIPAllocations,
utils.CacheReverseDestinations,
utils.CacheRPCResponses,
utils.CacheSharedGroups,
diff --git a/engine/model_helpers.go b/engine/model_helpers.go
index e13635753..19ad6a0d9 100644
--- a/engine/model_helpers.go
+++ b/engine/model_helpers.go
@@ -1238,63 +1238,98 @@ func (tps IPMdls) CSVHeader() []string {
utils.FilterIDs,
utils.ActivationIntervalString,
utils.TTL,
- utils.Limit,
- utils.AllocationMessage,
- utils.Type,
- utils.AddressPool,
- utils.Allocation,
- utils.Blocker,
utils.Stored,
utils.Weight,
+ utils.PoolID,
+ utils.PoolFilterIDs,
+ utils.PoolType,
+ utils.PoolRange,
+ utils.PoolStrategy,
+ utils.PoolMessage,
+ utils.PoolWeight,
+ utils.PoolBlocker,
}
}
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]
+ mst := make(map[string]*utils.TPIPProfile)
+ poolMap := make(map[string]map[string]*utils.TPIPPool)
+ for _, mdl := range tps {
+ tenID := (&utils.TenantID{Tenant: mdl.Tenant, ID: mdl.ID}).TenantID()
+ tpip, found := mst[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,
+ tpip = &utils.TPIPProfile{
+ TPid: mdl.Tpid,
+ Tenant: mdl.Tenant,
+ ID: mdl.ID,
+ Stored: mdl.Stored,
}
}
- if tp.TTL != utils.EmptyString {
- rl.TTL = tp.TTL
+ // Handle Pool
+ if mdl.PoolID != utils.EmptyString {
+ if _, has := poolMap[tenID]; !has {
+ poolMap[tenID] = make(map[string]*utils.TPIPPool)
+ }
+ poolID := mdl.PoolID
+ if mdl.PoolFilterIDs != utils.EmptyString {
+ poolID = utils.ConcatenatedKey(poolID,
+ utils.NewStringSet(strings.Split(mdl.PoolFilterIDs, utils.InfieldSep)).Sha1())
+ }
+ pool, found := poolMap[tenID][poolID]
+ if !found {
+ pool = &utils.TPIPPool{
+ ID: mdl.PoolID,
+ Type: mdl.PoolType,
+ Range: mdl.PoolRange,
+ Strategy: mdl.PoolStrategy,
+ Message: mdl.PoolMessage,
+ Weight: mdl.PoolWeight,
+ Blocker: mdl.PoolBlocker,
+ }
+ }
+ if mdl.PoolFilterIDs != utils.EmptyString {
+ poolFilterSplit := strings.Split(mdl.PoolFilterIDs, utils.InfieldSep)
+ pool.FilterIDs = append(pool.FilterIDs, poolFilterSplit...)
+ }
+ poolMap[tenID][poolID] = pool
}
- if tp.Weight != 0 {
- rl.Weight = tp.Weight
+ // Profile-level fields
+ if mdl.TTL != utils.EmptyString {
+ tpip.TTL = mdl.TTL
}
- rl.Stored = tp.Stored
- if len(tp.ActivationInterval) != 0 {
- rl.ActivationInterval = new(utils.TPActivationInterval)
- aiSplt := strings.Split(tp.ActivationInterval, utils.InfieldSep)
+ if mdl.Weight != 0 {
+ tpip.Weight = mdl.Weight
+ }
+ if mdl.Stored {
+ tpip.Stored = mdl.Stored
+ }
+ if len(mdl.ActivationInterval) != 0 {
+ tpip.ActivationInterval = new(utils.TPActivationInterval)
+ aiSplt := strings.Split(mdl.ActivationInterval, utils.InfieldSep)
if len(aiSplt) == 2 {
- rl.ActivationInterval.ActivationTime = aiSplt[0]
- rl.ActivationInterval.ExpiryTime = aiSplt[1]
+ tpip.ActivationInterval.ActivationTime = aiSplt[0]
+ tpip.ActivationInterval.ExpiryTime = aiSplt[1]
} else if len(aiSplt) == 1 {
- rl.ActivationInterval.ActivationTime = aiSplt[0]
+ tpip.ActivationInterval.ActivationTime = aiSplt[0]
}
}
- if tp.FilterIDs != utils.EmptyString {
+ if mdl.FilterIDs != utils.EmptyString {
if _, has := filterMap[tenID]; !has {
filterMap[tenID] = make(utils.StringSet)
}
- filterMap[tenID].AddSlice(strings.Split(tp.FilterIDs, utils.InfieldSep))
+ filterMap[tenID].AddSlice(strings.Split(mdl.FilterIDs, utils.InfieldSep))
}
- mrl[tenID] = rl
+ mst[tenID] = tpip
}
- result := make([]*utils.TPIPProfile, len(mrl))
+ // Build result with Pools
+ result := make([]*utils.TPIPProfile, len(mst))
i := 0
- for tntID, rl := range mrl {
- result[i] = rl
+ for tntID, tpip := range mst {
+ result[i] = tpip
+ for _, poolData := range poolMap[tntID] {
+ result[i].Pools = append(result[i].Pools, poolData)
+ }
result[i].FilterIDs = filterMap[tntID].AsSlice()
i++
}
@@ -1306,18 +1341,15 @@ func APItoModelIP(tp *utils.TPIPProfile) IPMdls {
return nil
}
var mdls IPMdls
- // In case that TPIPProfile don't have filter
- if len(tp.FilterIDs) == 0 {
+ // Handle case with no pools
+ if len(tp.Pools) == 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,
+ Tpid: tp.TPid,
+ Tenant: tp.Tenant,
+ ID: tp.ID,
+ TTL: tp.TTL,
+ Stored: tp.Stored,
+ Weight: tp.Weight,
}
if tp.ActivationInterval != nil {
if tp.ActivationInterval.ActivationTime != utils.EmptyString {
@@ -1327,9 +1359,16 @@ func APItoModelIP(tp *utils.TPIPProfile) IPMdls {
mdl.ActivationInterval += utils.InfieldSep + tp.ActivationInterval.ExpiryTime
}
}
+ for i, val := range tp.FilterIDs {
+ if i != 0 {
+ mdl.FilterIDs += utils.InfieldSep
+ }
+ mdl.FilterIDs += val
+ }
mdls = append(mdls, mdl)
+ return mdls
}
- for i, fltr := range tp.FilterIDs {
+ for i, pool := range tp.Pools {
mdl := &IPMdl{
Tpid: tp.TPid,
Tenant: tp.Tenant,
@@ -1337,11 +1376,15 @@ func APItoModelIP(tp *utils.TPIPProfile) IPMdls {
Stored: tp.Stored,
}
if i == 0 {
- mdl.Type = tp.Type
- mdl.AddressPool = tp.AddressPool
- mdl.Allocation = tp.Allocation
+ // Profile-level fields only on first row
mdl.TTL = tp.TTL
mdl.Weight = tp.Weight
+ for j, val := range tp.FilterIDs {
+ if j != 0 {
+ mdl.FilterIDs += utils.InfieldSep
+ }
+ mdl.FilterIDs += val
+ }
if tp.ActivationInterval != nil {
if tp.ActivationInterval.ActivationTime != utils.EmptyString {
mdl.ActivationInterval = tp.ActivationInterval.ActivationTime
@@ -1351,7 +1394,20 @@ func APItoModelIP(tp *utils.TPIPProfile) IPMdls {
}
}
}
- mdl.FilterIDs = fltr
+ // Pool fields on every row
+ mdl.PoolID = pool.ID
+ mdl.PoolType = pool.Type
+ mdl.PoolRange = pool.Range
+ mdl.PoolStrategy = pool.Strategy
+ mdl.PoolMessage = pool.Message
+ mdl.PoolWeight = pool.Weight
+ mdl.PoolBlocker = pool.Blocker
+ for j, val := range pool.FilterIDs {
+ if j != 0 {
+ mdl.PoolFilterIDs += utils.InfieldSep
+ }
+ mdl.PoolFilterIDs += val
+ }
mdls = append(mdls, mdl)
}
return mdls
@@ -1359,14 +1415,12 @@ func APItoModelIP(tp *utils.TPIPProfile) IPMdls {
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)),
+ Tenant: tp.Tenant,
+ ID: tp.ID,
+ Weight: tp.Weight,
+ Stored: tp.Stored,
+ FilterIDs: make([]string, len(tp.FilterIDs)),
+ Pools: make([]*IPPool, len(tp.Pools)),
}
if tp.TTL != utils.EmptyString {
var err error
@@ -1383,6 +1437,19 @@ func APItoIP(tp *utils.TPIPProfile, timezone string) (*IPProfile, error) {
return nil, err
}
}
+
+ for i, pool := range tp.Pools {
+ ipp.Pools[i] = &IPPool{
+ ID: pool.ID,
+ FilterIDs: pool.FilterIDs,
+ Type: pool.Type,
+ Range: pool.Range,
+ Strategy: pool.Strategy,
+ Message: pool.Message,
+ Weight: pool.Weight,
+ Blocker: pool.Blocker,
+ }
+ }
return ipp, nil
}
@@ -1392,11 +1459,9 @@ func IPProfileToAPI(ipp *IPProfile) *utils.TPIPProfile {
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,
+ Pools: make([]*utils.TPIPPool, len(ipp.Pools)),
}
if ipp.TTL != time.Duration(0) {
tp.TTL = ipp.TTL.String()
@@ -1404,6 +1469,19 @@ func IPProfileToAPI(ipp *IPProfile) *utils.TPIPProfile {
copy(tp.FilterIDs, ipp.FilterIDs)
+ for i, pool := range ipp.Pools {
+ tp.Pools[i] = &utils.TPIPPool{
+ ID: pool.ID,
+ FilterIDs: pool.FilterIDs,
+ Type: pool.Type,
+ Range: pool.Range,
+ Strategy: pool.Strategy,
+ Message: pool.Message,
+ Weight: pool.Weight,
+ Blocker: pool.Blocker,
+ }
+ }
+
if ipp.ActivationInterval != nil {
if !ipp.ActivationInterval.ActivationTime.IsZero() {
tp.ActivationInterval.ActivationTime = ipp.ActivationInterval.ActivationTime.Format(time.RFC3339)
diff --git a/engine/models.go b/engine/models.go
index b5957d1ec..9c9459a88 100644
--- a/engine/models.go
+++ b/engine/models.go
@@ -268,11 +268,16 @@ type IPMdl struct {
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:".*"`
+ Stored bool `index:"5" re:".*"`
+ Weight float64 `index:"6" re:".*"`
+ PoolID string `index:"7" re:".*"`
+ PoolFilterIDs string `index:"8" re:".*"`
+ PoolType string `index:"9" re:".*"`
+ PoolRange string `index:"10" re:".*"`
+ PoolStrategy string `index:"11" re:".*"`
+ PoolMessage string `index:"12" re:".*"`
+ PoolWeight float64 `index:"13" re:".*"`
+ PoolBlocker bool `index:"14" re:".*"`
CreatedAt time.Time
}
diff --git a/engine/storage_interface.go b/engine/storage_interface.go
index d06396f1b..e8ec18551 100644
--- a/engine/storage_interface.go
+++ b/engine/storage_interface.go
@@ -89,9 +89,9 @@ type DataDB interface {
GetIPProfileDrv(string, string) (*IPProfile, error)
SetIPProfileDrv(*IPProfile) error
RemoveIPProfileDrv(string, string) error
- GetIPDrv(string, string) (*IP, error)
- SetIPDrv(*IP) error
- RemoveIPDrv(string, string) error
+ GetIPAllocationsDrv(string, string) (*IPAllocations, error)
+ SetIPAllocationsDrv(*IPAllocations) error
+ RemoveIPAllocationsDrv(string, string) error
GetTimingDrv(string) (*utils.TPTiming, error)
SetTimingDrv(*utils.TPTiming) error
RemoveTimingDrv(string) error
diff --git a/engine/storage_internal_datadb.go b/engine/storage_internal_datadb.go
index 2943a1363..ea50e6309 100644
--- a/engine/storage_internal_datadb.go
+++ b/engine/storage_internal_datadb.go
@@ -216,7 +216,7 @@ 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.IPsPrefix,
+ case utils.ResourcesPrefix, utils.ResourceProfilesPrefix, utils.IPAllocationsPrefix,
utils.IPProfilesPrefix, utils.StatQueuePrefix, utils.StatQueueProfilePrefix,
utils.ThresholdPrefix, utils.ThresholdProfilePrefix, utils.FilterPrefix,
utils.RouteProfilePrefix, utils.AttributeProfilePrefix, utils.ChargerProfilePrefix,
@@ -553,21 +553,21 @@ func (iDB *InternalDB) RemoveIPProfileDrv(tenant, id string) error {
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
+func (iDB *InternalDB) GetIPAllocationsDrv(tenant, id string) (*IPAllocations, error) {
+ if x, ok := iDB.db.Get(utils.CacheIPAllocations, utils.ConcatenatedKey(tenant, id)); ok && x != nil {
+ return x.(*IPAllocations), nil
}
return nil, utils.ErrNotFound
}
-func (iDB *InternalDB) SetIPDrv(ip *IP) error {
- iDB.db.Set(utils.CacheIPs, ip.TenantID(), ip, nil,
+func (iDB *InternalDB) SetIPAllocationsDrv(ip *IPAllocations) error {
+ iDB.db.Set(utils.CacheIPAllocations, 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),
+func (iDB *InternalDB) RemoveIPAllocationsDrv(tenant, id string) error {
+ iDB.db.Remove(utils.CacheIPAllocations, utils.ConcatenatedKey(tenant, id),
true, utils.NonTransactional)
return nil
}
diff --git a/engine/storage_mongo_datadb.go b/engine/storage_mongo_datadb.go
index 041700d6d..665dbe4b9 100644
--- a/engine/storage_mongo_datadb.go
+++ b/engine/storage_mongo_datadb.go
@@ -435,7 +435,7 @@ func (ms *MongoStorage) RemoveKeysForPrefix(prefix string) error {
colName = ColRes
case utils.ResourceProfilesPrefix:
colName = ColRsP
- case utils.IPsPrefix:
+ case utils.IPAllocationsPrefix:
colName = ColIPs
case utils.IPProfilesPrefix:
colName = ColIPp
@@ -616,8 +616,8 @@ func (ms *MongoStorage) GetKeysForPrefix(prefix string) (keys []string, err erro
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.IPAllocationsPrefix:
+ keys, qryErr = ms.getAllKeysMatchingTenantID(sctx, ColIPs, utils.IPAllocationsPrefix, subject, tntID)
case utils.StatQueuePrefix:
keys, qryErr = ms.getAllKeysMatchingTenantID(sctx, ColSqs, utils.StatQueuePrefix, subject, tntID)
case utils.RankingsProfilePrefix:
@@ -698,7 +698,7 @@ 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:
+ case utils.IPAllocationsPrefix:
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})
@@ -1480,8 +1480,8 @@ func (ms *MongoStorage) RemoveIPProfileDrv(tenant, id string) error {
})
}
-func (ms *MongoStorage) GetIPDrv(tenant, id string) (*IP, error) {
- ip := new(IP)
+func (ms *MongoStorage) GetIPAllocationsDrv(tenant, id string) (*IPAllocations, error) {
+ ip := new(IPAllocations)
err := ms.query(func(sctx mongo.SessionContext) error {
sr := ms.getCol(ColIPs).FindOne(sctx, bson.M{"tenant": tenant, "id": id})
decodeErr := sr.Decode(ip)
@@ -1493,7 +1493,7 @@ func (ms *MongoStorage) GetIPDrv(tenant, id string) (*IP, error) {
return ip, err
}
-func (ms *MongoStorage) SetIPDrv(ip *IP) error {
+func (ms *MongoStorage) SetIPAllocationsDrv(ip *IPAllocations) 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},
@@ -1503,7 +1503,7 @@ func (ms *MongoStorage) SetIPDrv(ip *IP) error {
})
}
-func (ms *MongoStorage) RemoveIPDrv(tenant, id string) error {
+func (ms *MongoStorage) RemoveIPAllocationsDrv(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 {
diff --git a/engine/storage_redis.go b/engine/storage_redis.go
index ec7f75d05..3077864d0 100644
--- a/engine/storage_redis.go
+++ b/engine/storage_redis.go
@@ -321,7 +321,7 @@ 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.IPsPrefix,
+ case utils.ResourcesPrefix, utils.ResourceProfilesPrefix, utils.IPAllocationsPrefix,
utils.IPProfilesPrefix, utils.StatQueuePrefix, utils.StatQueueProfilePrefix,
utils.ThresholdPrefix, utils.ThresholdProfilePrefix, utils.FilterPrefix,
utils.RouteProfilePrefix, utils.AttributeProfilePrefix, utils.ChargerProfilePrefix,
@@ -859,31 +859,31 @@ 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) {
+func (rs *RedisStorage) GetIPAllocationsDrv(tenant, id string) (*IPAllocations, error) {
var values []byte
- if err := rs.Cmd(&values, redis_GET, utils.IPsPrefix+utils.ConcatenatedKey(tenant, id)); err != nil {
+ if err := rs.Cmd(&values, redis_GET, utils.IPAllocationsPrefix+utils.ConcatenatedKey(tenant, id)); err != nil {
return nil, err
}
if len(values) == 0 {
return nil, utils.ErrNotFound
}
- var ip *IP
+ var ip *IPAllocations
if err := rs.ms.Unmarshal(values, &ip); err != nil {
return nil, err
}
return ip, nil
}
-func (rs *RedisStorage) SetIPDrv(ip *IP) error {
+func (rs *RedisStorage) SetIPAllocationsDrv(ip *IPAllocations) error {
result, err := rs.ms.Marshal(ip)
if err != nil {
return err
}
- return rs.Cmd(nil, redis_SET, utils.IPsPrefix+ip.TenantID(), string(result))
+ return rs.Cmd(nil, redis_SET, utils.IPAllocationsPrefix+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) RemoveIPAllocationsDrv(tenant, id string) error {
+ return rs.Cmd(nil, redis_DEL, utils.IPAllocationsPrefix+utils.ConcatenatedKey(tenant, id))
}
func (rs *RedisStorage) GetTimingDrv(id string) (t *utils.TPTiming, err error) {
diff --git a/engine/tpreader.go b/engine/tpreader.go
index 1cabc3deb..841abc993 100644
--- a/engine/tpreader.go
+++ b/engine/tpreader.go
@@ -1636,7 +1636,7 @@ func (tpr *TpReader) WriteToDatabase(verbose, disableReverse bool) (err error) {
}
if len(tpr.ipProfiles) != 0 {
loadIDs[utils.CacheIPProfiles] = loadID
- loadIDs[utils.CacheIPs] = loadID
+ loadIDs[utils.CacheIPAllocations] = loadID
}
if verbose {
log.Print("StatQueueProfiles:")
@@ -2352,7 +2352,7 @@ func (tpr *TpReader) RemoveFromDatabase(verbose, disableReverse bool) (err error
}
if len(tpr.ipProfiles) != 0 {
loadIDs[utils.CacheIPProfiles] = loadID
- loadIDs[utils.CacheIPs] = loadID
+ loadIDs[utils.CacheIPAllocations] = loadID
}
if len(tpr.sqProfiles) != 0 {
loadIDs[utils.CacheStatQueueProfiles] = loadID
@@ -2428,7 +2428,7 @@ func (tpr *TpReader) ReloadCache(caching string, verbose bool, opts map[string]a
utils.CacheResourceProfiles: rspIDs,
utils.CacheResources: rspIDs,
utils.CacheIPProfiles: ippIDs,
- utils.CacheIPs: ippIDs,
+ utils.CacheIPAllocations: ippIDs,
utils.CacheActionTriggers: aatIDs,
utils.CacheStatQueues: stqpIDs,
utils.CacheStatQueueProfiles: stqpIDs,
diff --git a/loaders/loader.go b/loaders/loader.go
index 43eda8509..465062ab4 100644
--- a/loaders/loader.go
+++ b/loaders/loader.go
@@ -377,7 +377,7 @@ func (ldr *Loader) storeLoadedData(loaderType string,
return err
}
cacheArgs[utils.CacheIPProfiles] = ids
- cacheArgs[utils.CacheIPs] = ids
+ cacheArgs[utils.CacheIPAllocations] = ids
}
}
case utils.MetaFilters:
diff --git a/services/datadb_it_test.go b/services/datadb_it_test.go
index 4a80fbb97..c147109e4 100644
--- a/services/datadb_it_test.go
+++ b/services/datadb_it_test.go
@@ -126,7 +126,7 @@ func TestDataDBReload(t *testing.T) {
utils.MetaIPProfiles: {Limit: -1},
utils.MetaStatQueues: {Limit: -1},
utils.MetaResources: {Limit: -1},
- utils.MetaIPs: {Limit: -1},
+ utils.MetaIPAllocations: {Limit: -1},
utils.MetaStatQueueProfiles: {Limit: -1},
utils.MetaRankings: {Limit: -1},
utils.MetaRankingProfiles: {Limit: -1},
diff --git a/services/ips.go b/services/ips.go
index b34923f0e..378621e0f 100644
--- a/services/ips.go
+++ b/services/ips.go
@@ -75,7 +75,7 @@ func (s *IPService) Start() error {
}
s.srvDep[utils.DataDB].Add(1)
<-s.cache.GetPrecacheChannel(utils.CacheIPProfiles)
- <-s.cache.GetPrecacheChannel(utils.CacheIPs)
+ <-s.cache.GetPrecacheChannel(utils.CacheIPAllocations)
<-s.cache.GetPrecacheChannel(utils.CacheIPFilterIndexes)
fltrs := <-s.fsChan
diff --git a/utils/apitpdata.go b/utils/apitpdata.go
index fea588b8e..4cb5c77dc 100644
--- a/utils/apitpdata.go
+++ b/utils/apitpdata.go
@@ -1422,6 +1422,35 @@ func (trp *TPResourceProfile) CacheClone() any {
return trp.Clone()
}
+// TPIPPool is used in TPIPProfile
+type TPIPPool struct {
+ ID string
+ FilterIDs []string
+ Type string
+ Range string
+ Strategy string
+ Message string
+ Weight float64
+ Blocker bool
+}
+
+// Clone method for TPIPPool
+func (p *TPIPPool) Clone() *TPIPPool {
+ if p == nil {
+ return nil
+ }
+ return &TPIPPool{
+ ID: p.ID,
+ FilterIDs: slices.Clone(p.FilterIDs),
+ Type: p.Type,
+ Range: p.Range,
+ Strategy: p.Strategy,
+ Message: p.Message,
+ Weight: p.Weight,
+ Blocker: p.Blocker,
+ }
+}
+
// TPIPProfile is used in APIs to manage remotely offline IPProfile
type TPIPProfile struct {
TPid string
@@ -1430,11 +1459,9 @@ type TPIPProfile struct {
FilterIDs []string
ActivationInterval *TPActivationInterval
TTL string
- Type string
- AddressPool string
- Allocation string
Stored bool
Weight float64
+ Pools []*TPIPPool
}
// Clone method for TPIPProfile
@@ -1442,6 +1469,10 @@ func (tp *TPIPProfile) Clone() *TPIPProfile {
if tp == nil {
return nil
}
+ pools := make([]*TPIPPool, len(tp.Pools))
+ for i, pool := range tp.Pools {
+ pools[i] = pool.Clone()
+ }
return &TPIPProfile{
TPid: tp.TPid,
Tenant: tp.Tenant,
@@ -1451,6 +1482,7 @@ func (tp *TPIPProfile) Clone() *TPIPProfile {
TTL: tp.TTL,
Stored: tp.Stored,
Weight: tp.Weight,
+ Pools: pools,
}
}
@@ -2396,7 +2428,7 @@ func NewAttrReloadCacheWithOptsFromMap(arg map[string][]string, tnt string, opts
ResourceProfileIDs: arg[CacheResourceProfiles],
ResourceIDs: arg[CacheResources],
IPProfileIDs: arg[CacheIPProfiles],
- IPIDs: arg[CacheIPs],
+ IPIDs: arg[CacheIPAllocations],
StatsQueueIDs: arg[CacheStatQueues],
StatsQueueProfileIDs: arg[CacheStatQueueProfiles],
RankingIDs: arg[CacheRankings],
@@ -2482,7 +2514,7 @@ func (a *AttrReloadCacheWithAPIOpts) Map() map[string][]string {
CacheResourceProfiles: a.ResourceProfileIDs,
CacheResources: a.ResourceIDs,
CacheIPProfiles: a.IPProfileIDs,
- CacheIPs: a.IPIDs,
+ CacheIPAllocations: a.IPIDs,
CacheStatQueues: a.StatsQueueIDs,
CacheStatQueueProfiles: a.StatsQueueProfileIDs,
CacheThresholds: a.ThresholdIDs,
diff --git a/utils/consts.go b/utils/consts.go
index 64e5db149..b02ade0a5 100644
--- a/utils/consts.go
+++ b/utils/consts.go
@@ -53,7 +53,7 @@ var (
CacheRatingProfiles, CacheDispatcherProfiles, CacheDispatcherHosts,
CacheChargerProfiles, CacheActions, CacheActionTriggers, CacheSharedGroups,
CacheTimings, CacheResourceProfiles, CacheResources, CacheEventResources,
- CacheIPProfiles, CacheIPs, CacheEventIPs, CacheStatQueueProfiles,
+ CacheIPProfiles, CacheIPAllocations, CacheEventIPs, CacheStatQueueProfiles,
CacheRankingProfiles, CacheRankings, CacheStatQueues, CacheThresholdProfiles,
CacheThresholds, CacheFilters, CacheRouteProfiles, CacheAttributeProfiles,
CacheTrendProfiles, CacheTrends, CacheResourceFilterIndexes, CacheIPFilterIndexes,
@@ -90,7 +90,7 @@ var (
CacheResourceProfiles: ResourceProfilesPrefix,
CacheResources: ResourcesPrefix,
CacheIPProfiles: IPProfilesPrefix,
- CacheIPs: IPsPrefix,
+ CacheIPAllocations: IPAllocationsPrefix,
CacheTimings: TimingsPrefix,
CacheStatQueueProfiles: StatQueueProfilePrefix,
CacheStatQueues: StatQueuePrefix,
@@ -325,7 +325,7 @@ const (
UsersPrefix = "usr_"
ResourcesPrefix = "res_"
ResourceProfilesPrefix = "rsp_"
- IPsPrefix = "ips_"
+ IPAllocationsPrefix = "ips_"
IPProfilesPrefix = "ipp_"
ThresholdPrefix = "thd_"
TrendPrefix = "trd_"
@@ -601,6 +601,7 @@ const (
Weight = "Weight"
Limit = "Limit"
UsageTTL = "UsageTTL"
+ Message = "Message"
AllocationMessage = "AllocationMessage"
Stored = "Stored"
AddressPool = "AddressPool"
@@ -850,6 +851,7 @@ const (
InitS = "InitS"
TLSNoCaps = "tls"
UsageID = "UsageID"
+ AllocationID = "AllocationID"
Replacement = "Replacement"
Regexp = "Regexp"
Order = "Order"
@@ -926,6 +928,14 @@ const (
MetaFD = "*fd"
SortingData = "SortingData"
ProfileID = "ProfileID"
+ PoolID = "PoolID"
+ PoolFilterIDs = "PoolFilterIDs"
+ PoolType = "PoolType"
+ PoolRange = "PoolRange"
+ PoolStrategy = "PoolStrategy"
+ PoolMessage = "PoolMessage"
+ PoolWeight = "PoolWeight"
+ PoolBlocker = "PoolBlocker"
SortedRoutes = "SortedRoutes"
MetaMonthly = "*monthly"
MetaYearly = "*yearly"
@@ -1134,6 +1144,7 @@ const (
MetaActionTriggers = "*action_triggers"
MetaActions = "*actions"
MetaResourceProfile = "*resource_profiles"
+ MetaIPAllocations = "*ip_allocations"
MetaIPProfiles = "*ip_profiles"
MetaStatQueueProfiles = "*statqueue_profiles"
MetaStatQueues = "*statqueues"
@@ -1148,8 +1159,8 @@ const (
MetaThresholds = "*thresholds"
MetaRoutes = "*routes"
MetaAttributes = "*attributes"
- MetaResources = "*resources"
MetaIPs = "*ips"
+ MetaResources = "*resources"
MetaSessionsBackup = "*sessions_backup"
MetaLoadIDs = "*load_ids"
MetaNodeID = "*node_id"
@@ -1464,7 +1475,7 @@ const (
ReplicatorSv1GetTiming = "ReplicatorSv1.GetTiming"
ReplicatorSv1GetResource = "ReplicatorSv1.GetResource"
ReplicatorSv1GetResourceProfile = "ReplicatorSv1.GetResourceProfile"
- ReplicatorSv1GetIP = "ReplicatorSv1.GetIP"
+ ReplicatorSv1GetIPAllocations = "ReplicatorSv1.GetIPAllocations"
ReplicatorSv1GetIPProfile = "ReplicatorSv1.GetIPProfile"
ReplicatorSv1GetActionTriggers = "ReplicatorSv1.GetActionTriggers"
ReplicatorSv1GetSharedGroup = "ReplicatorSv1.GetSharedGroup"
@@ -1495,7 +1506,7 @@ const (
ReplicatorSv1SetTiming = "ReplicatorSv1.SetTiming"
ReplicatorSv1SetResource = "ReplicatorSv1.SetResource"
ReplicatorSv1SetResourceProfile = "ReplicatorSv1.SetResourceProfile"
- ReplicatorSv1SetIP = "ReplicatorSv1.SetIP"
+ ReplicatorSv1SetIPAllocations = "ReplicatorSv1.SetIPAllocations"
ReplicatorSv1SetIPProfile = "ReplicatorSv1.SetIPProfile"
ReplicatorSv1SetActionTriggers = "ReplicatorSv1.SetActionTriggers"
ReplicatorSv1SetSharedGroup = "ReplicatorSv1.SetSharedGroup"
@@ -1526,7 +1537,7 @@ const (
ReplicatorSv1RemoveTiming = "ReplicatorSv1.RemoveTiming"
ReplicatorSv1RemoveResource = "ReplicatorSv1.RemoveResource"
ReplicatorSv1RemoveResourceProfile = "ReplicatorSv1.RemoveResourceProfile"
- ReplicatorSv1RemoveIP = "ReplicatorSv1.RemoveIP"
+ ReplicatorSv1RemoveIPAllocations = "ReplicatorSv1.RemoveIPAllocations"
ReplicatorSv1RemoveIPProfile = "ReplicatorSv1.RemoveIPProfile"
ReplicatorSv1RemoveActionTriggers = "ReplicatorSv1.RemoveActionTriggers"
ReplicatorSv1RemoveSharedGroup = "ReplicatorSv1.RemoveSharedGroup"
@@ -1936,16 +1947,17 @@ const (
// IPs APIs
const (
- IPsV1Ping = "IPsV1.Ping"
- IPsV1GetIP = "IPsV1.GetIP"
- IPsV1GetIPsForEvent = "IPsV1.GetIPsForEvent"
- IPsV1AuthorizeIPs = "IPsV1.AuthorizeIPs"
- IPsV1AllocateIPs = "IPsV1.AllocateIPs"
- IPsV1ReleaseIPs = "IPsV1.ReleaseIPs"
- APIerSv1SetIPProfile = "APIerSv1.SetIPProfile"
- APIerSv1RemoveIPProfile = "APIerSv1.RemoveIPProfile"
- APIerSv1GetIPProfile = "APIerSv1.GetIPProfile"
- APIerSv1GetIPProfileIDs = "APIerSv1.GetIPProfileIDs"
+ IPsV1Ping = "IPsV1.Ping"
+ IPsV1GetIPAllocations = "IPsV1.GetIPAllocations"
+ IPsV1GetIPAllocationForEvent = "IPsV1.GetIPAllocationForEvent"
+ IPsV1AuthorizeIP = "IPsV1.AuthorizeIP"
+ IPsV1AllocateIP = "IPsV1.AllocateIP"
+ IPsV1ReleaseIP = "IPsV1.ReleaseIP"
+ IPsV1ClearIPAllocations = "IPsV1.ClearIPAllocations"
+ APIerSv1SetIPProfile = "APIerSv1.SetIPProfile"
+ APIerSv1RemoveIPProfile = "APIerSv1.RemoveIPProfile"
+ APIerSv1GetIPProfile = "APIerSv1.GetIPProfile"
+ APIerSv1GetIPProfileIDs = "APIerSv1.GetIPProfileIDs"
)
// SessionS APIs
@@ -2205,7 +2217,7 @@ const (
CacheSharedGroups = "*shared_groups"
CacheResources = "*resources"
CacheResourceProfiles = "*resource_profiles"
- CacheIPs = "*ips"
+ CacheIPAllocations = "*ip_allocations"
CacheIPProfiles = "*ip_profiles"
CacheTimings = "*timings"
CacheEventResources = "*event_resources"
@@ -2914,7 +2926,7 @@ var CGROptionsSet = NewStringSet([]string{OptsSessionsTTL,
OptsRoutesProfileCount, OptsDispatchersProfilesCount, OptsAttributesProfileRuns,
OptsAttributesProfileIgnoreFilters, OptsStatsProfileIDs, OptsStatsProfileIgnoreFilters,
OptsThresholdsProfileIDs, OptsThresholdsProfileIgnoreFilters, OptsResourcesUsageID, OptsResourcesUsageTTL,
- OptsResourcesUnits, OptsIPsUsageID, OptsIPsTTL, OptsIPsUnits, OptsAttributeS, OptsThresholdS, OptsChargerS,
+ OptsResourcesUnits, OptsIPsAllocationID, OptsIPsTTL, OptsAttributeS, OptsThresholdS, OptsChargerS,
OptsStatS, OptsRALs, OptsRerate, OptsRefund, MetaAccountID})
// EventExporter metrics
@@ -2960,14 +2972,17 @@ const (
OptsDispatchersProfilesCount = "*dispatchersProfilesCount"
// EEs
OptsEEsVerbose = "*eesVerbose"
+
// Resources
OptsResourcesUsageID = "*rsUsageID"
OptsResourcesUsageTTL = "*rsUsageTTL"
OptsResourcesUnits = "*rsUnits"
+
// IPs
- OptsIPsUsageID = "*ipUsageID"
- OptsIPsTTL = "*ipTTL"
- OptsIPsUnits = "*ipUnits"
+ OptsIPsAllocationID = "*ipAllocationID"
+ OptsIPsTTL = "*ipTTL"
+ MetaAllocationID = "*allocationID"
+
// Routes
OptsRoutesProfileCount = "*rouProfileCount"
OptsRoutesLimit = "*rouLimit"
diff --git a/utils/errors.go b/utils/errors.go
index 68ffc1274..85d5e6534 100644
--- a/utils/errors.go
+++ b/utils/errors.go
@@ -50,6 +50,7 @@ var (
ErrResourceUnauthorized = errors.New("RESOURCE_UNAUTHORIZED")
ErrIPUnavailable = errors.New("IP_UNAVAILABLE")
ErrIPUnauthorized = errors.New("IP_UNAUTHORIZED")
+ ErrIPAlreadyAllocated = errors.New("IP_ALREADY_ALLOCATED")
ErrNoActiveSession = errors.New("NO_ACTIVE_SESSION")
ErrPartiallyExecuted = errors.New("PARTIALLY_EXECUTED")
ErrMaxUsageExceeded = errors.New("MAX_USAGE_EXCEEDED")
@@ -109,6 +110,7 @@ var (
ErrResourceUnauthorized.Error(): ErrResourceUnauthorized,
ErrIPUnavailable.Error(): ErrIPUnavailable,
ErrIPUnauthorized.Error(): ErrIPUnauthorized,
+ ErrIPAlreadyAllocated.Error(): ErrIPAlreadyAllocated,
ErrNoActiveSession.Error(): ErrNoActiveSession,
ErrPartiallyExecuted.Error(): ErrPartiallyExecuted,
ErrMaxUsageExceeded.Error(): ErrMaxUsageExceeded,