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,