From eed6b8a51ae7dad0b0b7e2933edc50e5cbcbe574 Mon Sep 17 00:00:00 2001 From: ionutboangiu Date: Thu, 29 May 2025 11:41:34 +0300 Subject: [PATCH] add implementation for new ips module --- admins/ips.go | 177 ++++++++ admins/libadmin.go | 4 + apis/ips.go | 177 ++++++++ apis/libadmin.go | 4 + apis/loaders_it_test.go | 61 +++ apis/replicator.go | 94 +++++ cmd/cgr-engine/cgr-engine.go | 1 + config/config.go | 26 +- config/config_defaults.go | 58 +++ config/config_json.go | 3 + config/config_json_test.go | 78 ++++ config/config_test.go | 75 +++- config/configsanity.go | 2 +- config/ips.go | 268 ++++++++++++ config/loaderscfg_test.go | 118 ++++++ data/tariffplans/loadRateTest/IPs.csv | 0 data/tariffplans/testit/IPs.csv | 2 + data/tariffplans/testtp/IPs.csv | 0 engine/datadbmock.go | 48 +++ engine/datamanager.go | 255 ++++++++++++ engine/libengine.go | 3 + engine/libtest.go | 5 + engine/storage_interface.go | 6 + engine/storage_internal_datadb.go | 38 ++ engine/storage_mongo_datadb.go | 85 +++- engine/storage_redis.go | 54 +++ flaky_test.sh | 2 +- general_tests/offline_internal_it_test.go | 10 +- general_tests/service_toggle_it_test.go | 1 + general_tests/tut_smgeneric_it_test.go | 6 +- integration_test.sh | 2 +- ips/apis.go | 327 +++++++++++++++ ips/ips.go | 475 ++++++++++++++++++++++ loaders/libloader.go | 4 + loaders/loader.go | 10 + services/commonlisteners.go | 1 + services/connmanager.go | 4 + services/datadb.go | 1 + services/ips.go | 121 ++++++ services/prometheus.go | 4 +- servmanager/servmanager.go | 3 + utils/apitpdata.go | 12 + utils/apitpdata_test.go | 3 + utils/consts.go | 76 +++- utils/ips.go | 267 ++++++++++++ 45 files changed, 2935 insertions(+), 36 deletions(-) create mode 100644 admins/ips.go create mode 100644 apis/ips.go create mode 100644 config/ips.go create mode 100644 data/tariffplans/loadRateTest/IPs.csv create mode 100644 data/tariffplans/testit/IPs.csv create mode 100644 data/tariffplans/testtp/IPs.csv create mode 100644 ips/apis.go create mode 100644 ips/ips.go create mode 100644 services/ips.go create mode 100644 utils/ips.go diff --git a/admins/ips.go b/admins/ips.go new file mode 100644 index 000000000..d0320b981 --- /dev/null +++ b/admins/ips.go @@ -0,0 +1,177 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package admins + +import ( + "fmt" + "time" + + "github.com/cgrates/birpc/context" + "github.com/cgrates/cgrates/utils" +) + +// GetIPProfile returns an IP configuration +func (s *AdminS) V1GetIPProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *utils.IPProfile) error { + if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + tnt := arg.Tenant + if tnt == utils.EmptyString { + tnt = s.cfg.GeneralCfg().DefaultTenant + } + ipp, err := s.dm.GetIPProfile(ctx, tnt, arg.ID, true, true, utils.NonTransactional) + if err != nil { + return utils.APIErrorHandler(err) + } + *reply = *ipp + return nil +} + +// GetIPProfileIDs returns list of IPProfile IDs registered for a tenant +func (s *AdminS) V1GetIPProfileIDs(ctx *context.Context, args *utils.ArgsItemIDs, ippIDs *[]string) error { + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = s.cfg.GeneralCfg().DefaultTenant + } + prfx := utils.IPProfilesPrefix + tnt + utils.ConcatenatedKeySep + lenPrfx := len(prfx) + prfx += args.ItemsPrefix + keys, err := s.dm.DataDB().GetKeysForPrefix(ctx, prfx) + if err != nil { + return err + } + if len(keys) == 0 { + return utils.ErrNotFound + } + retIDs := make([]string, len(keys)) + for i, key := range keys { + retIDs[i] = key[lenPrfx:] + } + limit, offset, maxItems, err := utils.GetPaginateOpts(args.APIOpts) + if err != nil { + return err + } + *ippIDs, err = utils.Paginate(retIDs, limit, offset, maxItems) + return err +} + +// GetIPProfiles returns a list of IPProfiles registered for a tenant. +func (s *AdminS) V1GetIPProfiles(ctx *context.Context, args *utils.ArgsItemIDs, ipps *[]*utils.IPProfile) error { + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = s.cfg.GeneralCfg().DefaultTenant + } + var ippIDs []string + if err := s.V1GetIPProfileIDs(ctx, args, &ippIDs); err != nil { + return err + } + *ipps = make([]*utils.IPProfile, 0, len(ippIDs)) + for _, ippID := range ippIDs { + ipp, err := s.dm.GetIPProfile(ctx, tnt, ippID, true, true, utils.NonTransactional) + if err != nil { + return utils.APIErrorHandler(err) + } + *ipps = append(*ipps, ipp) + } + return nil +} + +// GetIPProfilesCount returns the total number of IPProfileIDs registered for a tenant +// returns ErrNotFound in case of 0 IPProfileIDs +func (s *AdminS) V1GetIPProfilesCount(ctx *context.Context, args *utils.ArgsItemIDs, reply *int) error { + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = s.cfg.GeneralCfg().DefaultTenant + } + prfx := utils.IPProfilesPrefix + tnt + utils.ConcatenatedKeySep + args.ItemsPrefix + keys, err := s.dm.DataDB().GetKeysForPrefix(ctx, prfx) + if err != nil { + return err + } + if len(keys) == 0 { + return utils.ErrNotFound + } + *reply = len(keys) + return nil +} + +// SetIPProfile adds a new IP configuration. +func (s *AdminS) V1SetIPProfile(ctx *context.Context, arg *utils.IPProfileWithAPIOpts, reply *string) error { + if missing := utils.MissingStructFields(arg.IPProfile, []string{utils.ID}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + if arg.Tenant == utils.EmptyString { + arg.Tenant = s.cfg.GeneralCfg().DefaultTenant + } + if err := s.dm.SetIPProfile(ctx, arg.IPProfile, true); err != nil { + return utils.APIErrorHandler(err) + } + //generate a loadID for CacheIPProfiles and CacheIPs and store it in database + //make 1 insert for both IPProfile and IPs instead of 2 + loadID := time.Now().UnixNano() + if err := s.dm.SetLoadIDs(ctx, + map[string]int64{utils.CacheIPProfiles: loadID, + utils.CacheIPs: loadID}); err != nil { + return utils.APIErrorHandler(err) + } + // delay if needed before cache call + if s.cfg.GeneralCfg().CachingDelay != 0 { + utils.Logger.Info(fmt.Sprintf(" Delaying cache call for %v", s.cfg.GeneralCfg().CachingDelay)) + time.Sleep(s.cfg.GeneralCfg().CachingDelay) + } + //handle caching for IPProfile + if err := s.CallCache(ctx, utils.IfaceAsString(arg.APIOpts[utils.MetaCache]), arg.Tenant, utils.CacheIPProfiles, + arg.TenantID(), utils.EmptyString, &arg.FilterIDs, arg.APIOpts); err != nil { + return utils.APIErrorHandler(err) + } + *reply = utils.OK + return nil +} + +// RemoveIPProfile remove a specific IP configuration. +func (s *AdminS) V1RemoveIPProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *string) error { + if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + tnt := arg.Tenant + if tnt == utils.EmptyString { + tnt = s.cfg.GeneralCfg().DefaultTenant + } + if err := s.dm.RemoveIPProfile(ctx, tnt, arg.ID, true); err != nil { + return utils.APIErrorHandler(err) + } + // delay if needed before cache call + if s.cfg.GeneralCfg().CachingDelay != 0 { + utils.Logger.Info(fmt.Sprintf(" Delaying cache call for %v", s.cfg.GeneralCfg().CachingDelay)) + time.Sleep(s.cfg.GeneralCfg().CachingDelay) + } + //handle caching for IPProfile + if err := s.CallCache(ctx, utils.IfaceAsString(arg.APIOpts[utils.MetaCache]), tnt, utils.CacheIPProfiles, + utils.ConcatenatedKey(tnt, arg.ID), utils.EmptyString, nil, arg.APIOpts); err != nil { + return utils.APIErrorHandler(err) + } + //generate a loadID for CacheIPProfiles and CacheIPs and store it in database + //make 1 insert for both IPProfile and IPs instead of 2 + loadID := time.Now().UnixNano() + if err := s.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheIPProfiles: loadID, utils.CacheIPs: loadID}); err != nil { + return utils.APIErrorHandler(err) + } + *reply = utils.OK + return nil +} diff --git a/admins/libadmin.go b/admins/libadmin.go index d4703f77a..cfca4466a 100644 --- a/admins/libadmin.go +++ b/admins/libadmin.go @@ -79,6 +79,8 @@ func (admS *AdminS) CallCache(ctx *context.Context, cacheopt string, tnt, cacheI cacheIDs = append(cacheIDs, utils.CacheThresholds) case utils.CacheResourceProfiles: cacheIDs = append(cacheIDs, utils.CacheResources) + case utils.CacheIPProfiles: + cacheIDs = append(cacheIDs, utils.CacheIPs) case utils.CacheStatQueueProfiles: cacheIDs = append(cacheIDs, utils.CacheStatQueues) } @@ -103,6 +105,8 @@ func (admS *AdminS) composeArgsReload(ctx *context.Context, tnt, cacheID, itemID argCache[utils.CacheThresholds] = []string{itemID} case utils.CacheResourceProfiles: argCache[utils.CacheResources] = []string{itemID} + case utils.CacheIPProfiles: + argCache[utils.CacheIPs] = []string{itemID} case utils.CacheStatQueueProfiles: argCache[utils.CacheStatQueues] = []string{itemID} } diff --git a/apis/ips.go b/apis/ips.go new file mode 100644 index 000000000..079da9297 --- /dev/null +++ b/apis/ips.go @@ -0,0 +1,177 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package apis + +import ( + "fmt" + "time" + + "github.com/cgrates/birpc/context" + "github.com/cgrates/cgrates/utils" +) + +// GetIPProfile returns an IP configuration +func (s *AdminSv1) GetIPProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *utils.IPProfile) error { + if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + tnt := arg.Tenant + if tnt == utils.EmptyString { + tnt = s.cfg.GeneralCfg().DefaultTenant + } + ipp, err := s.dm.GetIPProfile(ctx, tnt, arg.ID, true, true, utils.NonTransactional) + if err != nil { + return utils.APIErrorHandler(err) + } + *reply = *ipp + return nil +} + +// GetIPProfileIDs returns list of IPProfile IDs registered for a tenant +func (s *AdminSv1) GetIPProfileIDs(ctx *context.Context, args *utils.ArgsItemIDs, ippIDs *[]string) error { + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = s.cfg.GeneralCfg().DefaultTenant + } + prfx := utils.IPProfilesPrefix + tnt + utils.ConcatenatedKeySep + lenPrfx := len(prfx) + prfx += args.ItemsPrefix + keys, err := s.dm.DataDB().GetKeysForPrefix(ctx, prfx) + if err != nil { + return err + } + if len(keys) == 0 { + return utils.ErrNotFound + } + retIDs := make([]string, len(keys)) + for i, key := range keys { + retIDs[i] = key[lenPrfx:] + } + limit, offset, maxItems, err := utils.GetPaginateOpts(args.APIOpts) + if err != nil { + return err + } + *ippIDs, err = utils.Paginate(retIDs, limit, offset, maxItems) + return err +} + +// GetIPProfiles returns a list of IPProfiles registered for a tenant. +func (s *AdminSv1) GetIPProfiles(ctx *context.Context, args *utils.ArgsItemIDs, ipps *[]*utils.IPProfile) error { + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = s.cfg.GeneralCfg().DefaultTenant + } + var ippIDs []string + if err := s.GetIPProfileIDs(ctx, args, &ippIDs); err != nil { + return err + } + *ipps = make([]*utils.IPProfile, 0, len(ippIDs)) + for _, ippID := range ippIDs { + ipp, err := s.dm.GetIPProfile(ctx, tnt, ippID, true, true, utils.NonTransactional) + if err != nil { + return utils.APIErrorHandler(err) + } + *ipps = append(*ipps, ipp) + } + return nil +} + +// GetIPProfilesCount returns the total number of IPProfileIDs registered for a tenant +// returns ErrNotFound in case of 0 IPProfileIDs +func (s *AdminSv1) GetIPProfilesCount(ctx *context.Context, args *utils.ArgsItemIDs, reply *int) error { + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = s.cfg.GeneralCfg().DefaultTenant + } + prfx := utils.IPProfilesPrefix + tnt + utils.ConcatenatedKeySep + args.ItemsPrefix + keys, err := s.dm.DataDB().GetKeysForPrefix(ctx, prfx) + if err != nil { + return err + } + if len(keys) == 0 { + return utils.ErrNotFound + } + *reply = len(keys) + return nil +} + +// SetIPProfile adds a new IP configuration. +func (s *AdminSv1) SetIPProfile(ctx *context.Context, arg *utils.IPProfileWithAPIOpts, reply *string) error { + if missing := utils.MissingStructFields(arg.IPProfile, []string{utils.ID}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + if arg.Tenant == utils.EmptyString { + arg.Tenant = s.cfg.GeneralCfg().DefaultTenant + } + if err := s.dm.SetIPProfile(ctx, arg.IPProfile, true); err != nil { + return utils.APIErrorHandler(err) + } + //generate a loadID for CacheIPProfiles and CacheIPs and store it in database + //make 1 insert for both IPProfile and IPs instead of 2 + loadID := time.Now().UnixNano() + if err := s.dm.SetLoadIDs(ctx, + map[string]int64{utils.CacheIPProfiles: loadID, + utils.CacheIPs: loadID}); err != nil { + return utils.APIErrorHandler(err) + } + // delay if needed before cache call + if s.cfg.GeneralCfg().CachingDelay != 0 { + utils.Logger.Info(fmt.Sprintf(" Delaying cache call for %v", s.cfg.GeneralCfg().CachingDelay)) + time.Sleep(s.cfg.GeneralCfg().CachingDelay) + } + //handle caching for IPProfile + if err := s.CallCache(ctx, utils.IfaceAsString(arg.APIOpts[utils.MetaCache]), arg.Tenant, utils.CacheIPProfiles, + arg.TenantID(), utils.EmptyString, &arg.FilterIDs, arg.APIOpts); err != nil { + return utils.APIErrorHandler(err) + } + *reply = utils.OK + return nil +} + +// RemoveIPProfile remove a specific IP configuration. +func (s *AdminSv1) RemoveIPProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *string) error { + if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + tnt := arg.Tenant + if tnt == utils.EmptyString { + tnt = s.cfg.GeneralCfg().DefaultTenant + } + if err := s.dm.RemoveIPProfile(ctx, tnt, arg.ID, true); err != nil { + return utils.APIErrorHandler(err) + } + // delay if needed before cache call + if s.cfg.GeneralCfg().CachingDelay != 0 { + utils.Logger.Info(fmt.Sprintf(" Delaying cache call for %v", s.cfg.GeneralCfg().CachingDelay)) + time.Sleep(s.cfg.GeneralCfg().CachingDelay) + } + //handle caching for IPProfile + if err := s.CallCache(ctx, utils.IfaceAsString(arg.APIOpts[utils.MetaCache]), tnt, utils.CacheIPProfiles, + utils.ConcatenatedKey(tnt, arg.ID), utils.EmptyString, nil, arg.APIOpts); err != nil { + return utils.APIErrorHandler(err) + } + //generate a loadID for CacheIPProfiles and CacheIPs and store it in database + //make 1 insert for both IPProfile and IPs instead of 2 + loadID := time.Now().UnixNano() + if err := s.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheIPProfiles: loadID, utils.CacheIPs: loadID}); err != nil { + return utils.APIErrorHandler(err) + } + *reply = utils.OK + return nil +} diff --git a/apis/libadmin.go b/apis/libadmin.go index 9e5636f2d..d32ffedd2 100644 --- a/apis/libadmin.go +++ b/apis/libadmin.go @@ -79,6 +79,8 @@ func (admS *AdminSv1) CallCache(ctx *context.Context, cacheopt string, tnt, cach cacheIDs = append(cacheIDs, utils.CacheThresholds) case utils.CacheResourceProfiles: cacheIDs = append(cacheIDs, utils.CacheResources) + case utils.CacheIPProfiles: + cacheIDs = append(cacheIDs, utils.CacheIPs) case utils.CacheStatQueueProfiles: cacheIDs = append(cacheIDs, utils.CacheStatQueues) } @@ -103,6 +105,8 @@ func (admS *AdminSv1) composeArgsReload(ctx *context.Context, tnt, cacheID, item argCache[utils.CacheThresholds] = []string{itemID} case utils.CacheResourceProfiles: argCache[utils.CacheResources] = []string{itemID} + case utils.CacheIPProfiles: + argCache[utils.CacheIPs] = []string{itemID} case utils.CacheStatQueueProfiles: argCache[utils.CacheStatQueues] = []string{itemID} } diff --git a/apis/loaders_it_test.go b/apis/loaders_it_test.go index 67097c45a..33cc7de7d 100644 --- a/apis/loaders_it_test.go +++ b/apis/loaders_it_test.go @@ -63,6 +63,7 @@ var ( testLoadersGetFilters, testLoadersGetRateProfiles, testLoadersGetResourceProfiles, + testLoadersGetIPProfiles, testLoadersGetRouteProfiles, testLoadersGetStatQueueProfiles, testLoadersGetThresholdProfiles, @@ -275,6 +276,17 @@ cgrates.org,ResGroup22,*string:~*req.Account:dan,;10,3600s,2,premium_call,true,t t.Fatal(err) } + // Create and populate IPs.csv + if err := writeFile(utils.IPsCsv, ` +#Tenant[0],Id[1],FilterIDs[2],Weights[3],TTL[4],Type[5],AddressPool[6],Allocation[7],Stored[8] +cgrates.org,IPs1,*string:~*req.Account:1001,;10,1s,ipv4,172.16.1.1/24,alloc_success,true +cgrates.org,IPs1,,,,,,, +cgrates.org,IPs2,,,,,,, +cgrates.org,IPs2,*string:~*req.Account:1002,;20,1h,ipv4,127.0.0.1/24,alloc_new,false +`); err != nil { + t.Fatal(err) + } + // Create and populate Routes.csv if err := writeFile(utils.RoutesCsv, ` #Tenant[0],ID[1],FilterIDs[2],Weights[3],Blockers[4],Sorting[5],SortingParameters[6],RouteID[7],RouteFilterIDs[8],RouteAccountIDs[9],RouteRateProfileIDs[10],RouteResourceIDs[11],RouteStatIDs[12],RouteWeights[13],RouteBlockers[14],RouteParameters[15] @@ -973,6 +985,55 @@ func testLoadersGetResourceProfiles(t *testing.T) { } } +func testLoadersGetIPProfiles(t *testing.T) { + expIPPs := []*utils.IPProfile{ + { + Tenant: "cgrates.org", + ID: "IPs1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + Weights: utils.DynamicWeights{ + { + Weight: 10, + }, + }, + TTL: time.Second, + Type: "ipv4", + AddressPool: "172.16.1.1/24", + Allocation: "alloc_success", + Stored: true, + }, + { + Tenant: "cgrates.org", + ID: "IPs2", + FilterIDs: []string{"*string:~*req.Account:1002"}, + Weights: utils.DynamicWeights{ + { + Weight: 20, + }, + }, + TTL: time.Hour, + Type: "ipv4", + AddressPool: "127.0.0.1/24", + Allocation: "alloc_new", + Stored: false, + }, + } + var ipps []*utils.IPProfile + if err := ldrRPC.Call(context.Background(), utils.AdminSv1GetIPProfiles, + &utils.ArgsItemIDs{ + Tenant: "cgrates.org", + }, &ipps); err != nil { + t.Error(err) + } else { + sort.Slice(ipps, func(i, j int) bool { + return ipps[i].ID < ipps[j].ID + }) + if !reflect.DeepEqual(ipps, expIPPs) { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ToJSON(expIPPs), utils.ToJSON(ipps)) + } + } +} + func testLoadersGetRouteProfiles(t *testing.T) { expRouPrfs := []*utils.RouteProfile{ { diff --git a/apis/replicator.go b/apis/replicator.go index 40aabffb1..2882ed94e 100644 --- a/apis/replicator.go +++ b/apis/replicator.go @@ -156,6 +156,28 @@ func (rplSv1 *ReplicatorSv1) GetResourceProfile(ctx *context.Context, tntID *uti return nil } +// GetIP is the remote method coresponding to the dataDb driver method +func (rplSv1 *ReplicatorSv1) GetIP(ctx *context.Context, tntID *utils.TenantIDWithAPIOpts, reply *utils.IP) error { + engine.UpdateReplicationFilters(utils.IPsPrefix, tntID.TenantID.TenantID(), utils.IfaceAsString(tntID.APIOpts[utils.RemoteHostOpt])) + rcv, err := rplSv1.dm.DataDB().GetIPDrv(ctx, tntID.Tenant, tntID.ID) + if err != nil { + return err + } + *reply = *rcv + return nil +} + +// GetIPProfile is the remote method coresponding to the dataDb driver method +func (rplSv1 *ReplicatorSv1) GetIPProfile(ctx *context.Context, tntID *utils.TenantIDWithAPIOpts, reply *utils.IPProfile) error { + engine.UpdateReplicationFilters(utils.IPProfilesPrefix, tntID.TenantID.TenantID(), utils.IfaceAsString(tntID.APIOpts[utils.RemoteHostOpt])) + rcv, err := rplSv1.dm.DataDB().GetIPProfileDrv(ctx, tntID.Tenant, tntID.ID) + if err != nil { + return err + } + *reply = *rcv + return nil +} + // GetRouteProfile is the remote method coresponding to the dataDb driver method func (rplSv1 *ReplicatorSv1) GetRouteProfile(ctx *context.Context, tntID *utils.TenantIDWithAPIOpts, reply *utils.RouteProfile) error { engine.UpdateReplicationFilters(utils.RouteProfilePrefix, tntID.TenantID.TenantID(), utils.IfaceAsString(tntID.APIOpts[utils.RemoteHostOpt])) @@ -383,6 +405,42 @@ func (rplSv1 *ReplicatorSv1) SetResource(ctx *context.Context, rs *utils.Resourc return } +// SetIPProfile is the replication method coresponding to the dataDb driver method +func (rplSv1 *ReplicatorSv1) SetIPProfile(ctx *context.Context, ipp *utils.IPProfileWithAPIOpts, reply *string) (err error) { + if err = rplSv1.dm.DataDB().SetIPProfileDrv(ctx, ipp.IPProfile); err != nil { + return + } + // delay if needed before cache call + if rplSv1.v1.cfg.GeneralCfg().CachingDelay != 0 { + utils.Logger.Info(fmt.Sprintf(" Delaying cache call for %v", rplSv1.v1.cfg.GeneralCfg().CachingDelay)) + time.Sleep(rplSv1.v1.cfg.GeneralCfg().CachingDelay) + } + if err = rplSv1.v1.CallCache(ctx, utils.IfaceAsString(ipp.APIOpts[utils.MetaCache]), + ipp.Tenant, utils.CacheIPProfiles, ipp.TenantID(), utils.EmptyString, &ipp.FilterIDs, ipp.APIOpts); err != nil { + return + } + *reply = utils.OK + return +} + +// SetIP is the replication method coresponding to the dataDb driver method +func (rplSv1 *ReplicatorSv1) SetIP(ctx *context.Context, ip *utils.IPWithAPIOpts, reply *string) (err error) { + if err = rplSv1.dm.DataDB().SetIPDrv(ctx, ip.IP); err != nil { + return + } + // delay if needed before cache call + if rplSv1.v1.cfg.GeneralCfg().CachingDelay != 0 { + utils.Logger.Info(fmt.Sprintf(" Delaying cache call for %v", rplSv1.v1.cfg.GeneralCfg().CachingDelay)) + time.Sleep(rplSv1.v1.cfg.GeneralCfg().CachingDelay) + } + if err = rplSv1.v1.CallCache(ctx, utils.IfaceAsString(ip.APIOpts[utils.MetaCache]), + ip.Tenant, utils.CacheIPs, ip.TenantID(), utils.EmptyString, nil, ip.APIOpts); err != nil { + return + } + *reply = utils.OK + return +} + // SetRouteProfile is the replication method coresponding to the dataDb driver method func (rplSv1 *ReplicatorSv1) SetRouteProfile(ctx *context.Context, sp *utils.RouteProfileWithAPIOpts, reply *string) (err error) { if err = rplSv1.dm.DataDB().SetRouteProfileDrv(ctx, sp.RouteProfile); err != nil { @@ -643,6 +701,42 @@ 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(ctx, args.Tenant, args.ID); err != nil { + return + } + // delay if needed before cache call + if rplSv1.v1.cfg.GeneralCfg().CachingDelay != 0 { + utils.Logger.Info(fmt.Sprintf(" Delaying cache call for %v", rplSv1.v1.cfg.GeneralCfg().CachingDelay)) + time.Sleep(rplSv1.v1.cfg.GeneralCfg().CachingDelay) + } + if err = rplSv1.v1.CallCache(ctx, utils.IfaceAsString(args.APIOpts[utils.MetaCache]), + args.Tenant, utils.CacheIPs, args.TenantID.TenantID(), utils.EmptyString, nil, args.APIOpts); err != nil { + return + } + *reply = utils.OK + return +} + +// RemoveIPProfile is the replication method coresponding to the dataDb driver method +func (rplSv1 *ReplicatorSv1) RemoveIPProfile(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *string) (err error) { + if err = rplSv1.dm.DataDB().RemoveIPProfileDrv(ctx, args.Tenant, args.ID); err != nil { + return + } + // delay if needed before cache call + if rplSv1.v1.cfg.GeneralCfg().CachingDelay != 0 { + utils.Logger.Info(fmt.Sprintf(" Delaying cache call for %v", rplSv1.v1.cfg.GeneralCfg().CachingDelay)) + time.Sleep(rplSv1.v1.cfg.GeneralCfg().CachingDelay) + } + if err = rplSv1.v1.CallCache(ctx, utils.IfaceAsString(args.APIOpts[utils.MetaCache]), + args.Tenant, utils.CacheIPProfiles, args.TenantID.TenantID(), utils.EmptyString, nil, args.APIOpts); err != nil { + return + } + *reply = utils.OK + return +} + // RemoveRouteProfile is the replication method coresponding to the dataDb driver method func (rplSv1 *ReplicatorSv1) RemoveRouteProfile(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *string) (err error) { if err = rplSv1.dm.DataDB().RemoveRouteProfileDrv(ctx, args.Tenant, args.ID); err != nil { diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go index c263c4492..61d591a25 100644 --- a/cmd/cgr-engine/cgr-engine.go +++ b/cmd/cgr-engine/cgr-engine.go @@ -217,6 +217,7 @@ func runCGREngine(fs []string) (err error) { services.NewChargerService(cfg), services.NewRouteService(cfg), services.NewResourceService(cfg), + services.NewIPService(cfg), services.NewTrendService(cfg), services.NewRankingService(cfg), services.NewThresholdService(cfg), diff --git a/config/config.go b/config/config.go index b2c0b5ad8..3d6414e8a 100644 --- a/config/config.go +++ b/config/config.go @@ -201,6 +201,11 @@ func newCGRConfig(config []byte) (cfg *CGRConfig, err error) { UsageTTL: []*DynamicDurationOpt{{value: ResourcesUsageTTLDftOpt}}, Units: []*DynamicFloat64Opt{{value: ResourcesUnitsDftOpt}}, }}, + ipsCfg: &IPsCfg{Opts: &IPsOpts{ + UsageID: []*DynamicStringOpt{{value: IPsUsageIDDftOpt}}, + TTL: []*DynamicDurationOpt{{value: IPsTTLDftOpt}}, + Units: []*DynamicFloat64Opt{{value: IPsUnitsDftOpt}}, + }}, trendSCfg: new(TrendSCfg), rankingSCfg: new(RankingSCfg), statsCfg: &StatSCfg{Opts: &StatsOpts{ @@ -356,6 +361,7 @@ type CGRConfig struct { attributeSCfg *AttributeSCfg // AttributeS config chargerSCfg *ChargerSCfg // ChargerS config resourceSCfg *ResourceSConfig // ResourceS config + ipsCfg *IPsCfg // IPs config statsCfg *StatSCfg // StatS config thresholdSCfg *ThresholdSCfg // ThresholdS config routeSCfg *RouteSCfg // RouteS config @@ -393,11 +399,11 @@ func (cfg *CGRConfig) ConfigDB() ConfigDB { return nil } -var posibleLoaderTypes = utils.NewStringSet([]string{utils.MetaAttributes, - utils.MetaResources, utils.MetaFilters, utils.MetaStats, utils.MetaTrends, - utils.MetaRoutes, utils.MetaThresholds, utils.MetaChargers, utils.MetaRankings, - utils.MetaRateProfiles, - utils.MetaAccounts, utils.MetaActionProfiles}) +var possibleLoaderTypes = utils.NewStringSet([]string{utils.MetaAttributes, + utils.MetaResources, utils.MetaIPs, utils.MetaFilters, utils.MetaStats, + utils.MetaTrends, utils.MetaRoutes, utils.MetaThresholds, utils.MetaChargers, + utils.MetaRankings, utils.MetaRateProfiles, utils.MetaAccounts, + utils.MetaActionProfiles}) var possibleReaderTypes = utils.NewStringSet([]string{utils.MetaFileCSV, utils.MetaKafkajsonMap, utils.MetaFileXML, utils.MetaSQL, utils.MetaFileFWV, @@ -487,6 +493,13 @@ func (cfg *CGRConfig) ResourceSCfg() *ResourceSConfig { // not done return cfg.resourceSCfg } +// IPsCfg returns the config for IPs. +func (cfg *CGRConfig) IPsCfg() *IPsCfg { + cfg.lks[IPsJSON].Lock() + defer cfg.lks[IPsJSON].Unlock() + return cfg.ipsCfg +} + // StatSCfg returns the config for StatS func (cfg *CGRConfig) StatSCfg() *StatSCfg { // not done cfg.lks[StatSJSON].Lock() @@ -1002,7 +1015,7 @@ func (cfg *CGRConfig) initChanels() { func (cfg *CGRConfig) reloadSections(sections ...string) { subsystemsThatNeedDataDB := utils.NewStringSet([]string{DataDBJSON, CDRsJSON, SessionSJSON, AttributeSJSON, - ChargerSJSON, ResourceSJSON, StatSJSON, ThresholdSJSON, + ChargerSJSON, ResourceSJSON, IPsJSON, StatSJSON, ThresholdSJSON, RouteSJSON, LoaderSJSON, RateSJSON, AdminSJSON, AccountSJSON, ActionSJSON}) subsystemsThatNeedStorDB := utils.NewStringSet([]string{StorDBJSON, CDRsJSON}) @@ -1068,6 +1081,7 @@ func (cfg *CGRConfig) Clone() (cln *CGRConfig) { attributeSCfg: cfg.attributeSCfg.Clone(), chargerSCfg: cfg.chargerSCfg.Clone(), resourceSCfg: cfg.resourceSCfg.Clone(), + ipsCfg: cfg.ipsCfg.Clone(), statsCfg: cfg.statsCfg.Clone(), thresholdSCfg: cfg.thresholdSCfg.Clone(), trendSCfg: cfg.trendSCfg.Clone(), diff --git a/config/config_defaults.go b/config/config_defaults.go index 62fc34afb..9dbf293c1 100644 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -123,6 +123,8 @@ const CGRATES_CFG_JSON = ` "*actions": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, "*resource_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, "*resources": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, + "*ip_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, + "*ips": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, "*statqueue_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, "*statqueues": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, "*threshold_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, @@ -136,6 +138,7 @@ const CGRATES_CFG_JSON = ` "*load_ids": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, "*versions": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, "*resource_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, + "*ip_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, "*stat_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, "*threshold_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, "*ranking_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate":false}, @@ -268,6 +271,9 @@ const CGRATES_CFG_JSON = ` "*resource_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control resource profiles caching "*resources": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control resources caching "*event_resources": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // matching resources to events + "*ip_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control ip profiles caching + "*ips": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control ips caching + "*event_ips": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // matching ips to events "*statqueue_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // statqueue profiles "*statqueues": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // statqueues with metrics "*threshold_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control threshold profiles caching @@ -282,6 +288,7 @@ const CGRATES_CFG_JSON = ` "*action_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control action profile caching "*accounts": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control account profile caching "*resource_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // control resource filter indexes caching + "*ip_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // control ip filter indexes caching "*stat_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // control stat filter indexes caching "*threshold_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // control threshold filter indexes caching "*route_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // control route filter indexes caching @@ -1136,6 +1143,41 @@ const CGRATES_CFG_JSON = ` } }, +"ips": { + "enabled": false, // enables the IPs service: + "store_interval": "", // dump cache regularly to dataDB, 0 - dump at start/shutdown: <""|$dur> + "indexed_selects": true, // enable profile matching exclusively on indexes + //"string_indexed_fields": [], // query indexes based on these fields for faster processing + "prefix_indexed_fields": [], // query indexes based on these fields for faster processing + "suffix_indexed_fields": [], // query indexes based on these fields for faster processing + "exists_indexed_fields": [], // query indexes based on these fields for faster processing + "notexists_indexed_fields": [], // query indexes based on these fields for faster processing + "nested_fields": false, // determines which field is checked when matching indexed filters(true: all; false: only the one on the first level) + "opts":{ + // "*usageID": [ + // { + // "Tenant": "*any", + // "FilterIDs": [], + // "Value": "" + // } + // ], + // "*ttl": [ + // { + // "Tenant": "*any", + // "FilterIDs": [], + // "Value": "72h" + // } + // ], + // "*units": [ + // { + // "Tenant": "*any", + // "FilterIDs": [], + // "Value": 1 + // } + // ] + } +}, + "stats": { "enabled": false, // starts Stat service: @@ -1328,6 +1370,7 @@ const CGRATES_CFG_JSON = ` "*filters":{"limit": -1, "ttl": "5s", "static_ttl": false}, "*attributes":{"limit": -1, "ttl": "5s", "static_ttl": false}, "*resources":{"limit": -1, "ttl": "5s", "static_ttl": false}, + "*ips":{"limit": -1, "ttl": "5s", "static_ttl": false}, "*stats":{"limit": -1, "ttl": "5s", "static_ttl": false}, "*thresholds":{"limit": -1, "ttl": "5s", "static_ttl": false}, "*routes":{"limit": -1, "ttl": "5s", "static_ttl": false}, @@ -1382,6 +1425,21 @@ const CGRATES_CFG_JSON = ` {"tag": "ThresholdIDs", "path": "ThresholdIDs", "type": "*variable", "value": "~*req.9"} ] }, + { + "type": "*ips", // data source type + "file_name": "IPs.csv", // file name in the tp_in_dir + "fields": [ + {"tag": "Tenant", "path": "Tenant", "type": "*variable", "value": "~*req.0", "mandatory": true}, + {"tag": "ID", "path": "ID", "type": "*variable", "value": "~*req.1", "mandatory": true}, + {"tag": "FilterIDs", "path": "FilterIDs", "type": "*variable", "value": "~*req.2"}, + {"tag": "Weights", "path": "Weights", "type": "*variable", "value": "~*req.3"}, + {"tag": "TTL", "path": "TTL", "type": "*variable", "value": "~*req.4"}, + {"tag": "Type", "path": "Type", "type": "*variable", "value": "~*req.5"}, + {"tag": "AddressPool", "path": "AddressPool", "type": "*variable", "value": "~*req.6"}, + {"tag": "Allocation", "path": "Allocation", "type": "*variable", "value": "~*req.7"}, + {"tag": "Stored", "path": "Stored", "type": "*variable", "value": "~*req.8"}, + ] + }, { "type": "*stats", // data source type "file_name": "Stats.csv", // file name in the tp_in_dir diff --git a/config/config_json.go b/config/config_json.go index eea6016f7..88072fc25 100644 --- a/config/config_json.go +++ b/config/config_json.go @@ -45,6 +45,7 @@ const ( PrometheusAgentJSON = "prometheus_agent" AttributeSJSON = "attributes" ResourceSJSON = "resources" + IPsJSON = "ips" JanusAgentJSON = "janus_agent" StatSJSON = "stats" ThresholdSJSON = "thresholds" @@ -88,6 +89,7 @@ var ( RankingSJSON: utils.RankingS, StatSJSON: utils.StatS, ResourceSJSON: utils.ResourceS, + IPsJSON: utils.IPs, RouteSJSON: utils.RouteS, AdminSJSON: utils.AdminS, CDRsJSON: utils.CDRServer, @@ -193,6 +195,7 @@ func newSections(cfg *CGRConfig) Sections { cfg.attributeSCfg, cfg.chargerSCfg, cfg.resourceSCfg, + cfg.ipsCfg, cfg.statsCfg, cfg.thresholdSCfg, cfg.rankingSCfg, diff --git a/config/config_json_test.go b/config/config_json_test.go index a017ca2f5..a9e1485c5 100644 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -100,6 +100,15 @@ func TestCacheJsonCfg(t *testing.T) { utils.CacheEventResources: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + utils.CacheIPProfiles: {Limit: utils.IntPointer(-1), + Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + utils.CacheIPs: {Limit: utils.IntPointer(-1), + Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + utils.CacheEventIPs: {Limit: utils.IntPointer(-1), + Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheStatQueueProfiles: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, @@ -142,6 +151,9 @@ func TestCacheJsonCfg(t *testing.T) { utils.CacheResourceFilterIndexes: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + utils.CacheIPFilterIndexes: {Limit: utils.IntPointer(-1), + Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheStatFilterIndexes: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, @@ -318,6 +330,13 @@ func TestDfDataDbJsonCfg(t *testing.T) { Replicate: utils.BoolPointer(false), Remote: utils.BoolPointer(false), }, + utils.MetaIPProfiles: { + Ttl: utils.StringPointer(utils.EmptyString), + Static_ttl: utils.BoolPointer(false), + Limit: utils.IntPointer(-1), + Replicate: utils.BoolPointer(false), + Remote: utils.BoolPointer(false), + }, utils.MetaStatQueues: { Ttl: utils.StringPointer(utils.EmptyString), Static_ttl: utils.BoolPointer(false), @@ -332,6 +351,13 @@ func TestDfDataDbJsonCfg(t *testing.T) { Replicate: utils.BoolPointer(false), Remote: utils.BoolPointer(false), }, + utils.MetaIPs: { + Ttl: utils.StringPointer(utils.EmptyString), + Static_ttl: utils.BoolPointer(false), + Limit: utils.IntPointer(-1), + Replicate: utils.BoolPointer(false), + Remote: utils.BoolPointer(false), + }, utils.MetaStatQueueProfiles: { Ttl: utils.StringPointer(utils.EmptyString), Static_ttl: utils.BoolPointer(false), @@ -416,6 +442,13 @@ func TestDfDataDbJsonCfg(t *testing.T) { Replicate: utils.BoolPointer(false), Remote: utils.BoolPointer(false), }, + utils.CacheIPFilterIndexes: { + Ttl: utils.StringPointer(utils.EmptyString), + Static_ttl: utils.BoolPointer(false), + Limit: utils.IntPointer(-1), + Replicate: utils.BoolPointer(false), + Remote: utils.BoolPointer(false), + }, utils.CacheStatFilterIndexes: { Ttl: utils.StringPointer(utils.EmptyString), Static_ttl: utils.BoolPointer(false), @@ -1114,6 +1147,50 @@ func TestDfLoaderJsonCfg(t *testing.T) { Value: utils.StringPointer("~*req.9")}, }, }, + { + Type: utils.StringPointer(utils.MetaIPs), + File_name: utils.StringPointer(utils.IPsCsv), + Fields: &[]*FcTemplateJsonCfg{ + {Tag: utils.StringPointer(utils.Tenant), + Path: utils.StringPointer(utils.Tenant), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.0"), + Mandatory: utils.BoolPointer(true)}, + {Tag: utils.StringPointer(utils.ID), + Path: utils.StringPointer(utils.ID), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.1"), + Mandatory: utils.BoolPointer(true)}, + {Tag: utils.StringPointer("FilterIDs"), + Path: utils.StringPointer("FilterIDs"), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.2")}, + {Tag: utils.StringPointer("Weights"), + Path: utils.StringPointer("Weights"), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.3")}, + {Tag: utils.StringPointer("TTL"), + Path: utils.StringPointer("TTL"), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.4")}, + {Tag: utils.StringPointer("Type"), + Path: utils.StringPointer("Type"), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.5")}, + {Tag: utils.StringPointer("AddressPool"), + Path: utils.StringPointer("AddressPool"), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.6")}, + {Tag: utils.StringPointer("Allocation"), + Path: utils.StringPointer("Allocation"), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.7")}, + {Tag: utils.StringPointer("Stored"), + Path: utils.StringPointer("Stored"), + Type: utils.StringPointer(utils.MetaVariable), + Value: utils.StringPointer("~*req.8")}, + }, + }, { Type: utils.StringPointer(utils.MetaStats), File_name: utils.StringPointer(utils.StatsCsv), @@ -1692,6 +1769,7 @@ func TestDfLoaderJsonCfg(t *testing.T) { utils.MetaFilters: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer("5s"), Static_ttl: utils.BoolPointer(false)}, utils.MetaAttributes: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer("5s"), Static_ttl: utils.BoolPointer(false)}, utils.MetaResources: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer("5s"), Static_ttl: utils.BoolPointer(false)}, + utils.MetaIPs: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer("5s"), Static_ttl: utils.BoolPointer(false)}, utils.MetaStats: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer("5s"), Static_ttl: utils.BoolPointer(false)}, utils.MetaThresholds: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer("5s"), Static_ttl: utils.BoolPointer(false)}, utils.MetaRoutes: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer("5s"), Static_ttl: utils.BoolPointer(false)}, diff --git a/config/config_test.go b/config/config_test.go index a55822241..46c6d6e1a 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -435,10 +435,12 @@ func TestCgrCfgJSONDefaultsSMGenericCfg(t *testing.T) { func TestCgrCfgJSONDefaultsCacheCFG(t *testing.T) { eCacheCfg := &CacheCfg{ Partitions: map[string]*CacheParamCfg{ - utils.CacheResourceProfiles: {Limit: -1}, - utils.CacheResources: {Limit: -1}, - utils.CacheEventResources: {Limit: -1, - TTL: 0}, + utils.CacheResourceProfiles: {Limit: -1}, + utils.CacheResources: {Limit: -1}, + utils.CacheEventResources: {Limit: -1, TTL: 0}, + utils.CacheIPProfiles: {Limit: -1}, + utils.CacheIPs: {Limit: -1}, + utils.CacheEventIPs: {Limit: -1, TTL: 0}, utils.CacheStatQueueProfiles: {Limit: -1}, utils.CacheStatQueues: {Limit: -1}, utils.CacheThresholdProfiles: {Limit: -1}, @@ -453,6 +455,7 @@ func TestCgrCfgJSONDefaultsCacheCFG(t *testing.T) { utils.CacheActionProfiles: {Limit: -1}, utils.CacheAccounts: {Limit: -1}, utils.CacheResourceFilterIndexes: {Limit: -1}, + utils.CacheIPFilterIndexes: {Limit: -1}, utils.CacheStatFilterIndexes: {Limit: -1}, utils.CacheThresholdFilterIndexes: {Limit: -1}, utils.CacheRouteFilterIndexes: {Limit: -1}, @@ -1865,6 +1868,7 @@ func TestLoaderConfig(t *testing.T) { utils.MetaFilters: {Limit: -1, TTL: 5 * time.Second}, utils.MetaAttributes: {Limit: -1, TTL: 5 * time.Second}, utils.MetaResources: {Limit: -1, TTL: 5 * time.Second}, + utils.MetaIPs: {Limit: -1, TTL: 5 * time.Second}, utils.MetaStats: {Limit: -1, TTL: 5 * time.Second}, utils.MetaThresholds: {Limit: -1, TTL: 5 * time.Second}, utils.MetaRoutes: {Limit: -1, TTL: 5 * time.Second}, @@ -2219,6 +2223,7 @@ func TestCgrLoaderCfgITDefaults(t *testing.T) { utils.MetaFilters: {Limit: -1, TTL: 5 * time.Second}, utils.MetaAttributes: {Limit: -1, TTL: 5 * time.Second}, utils.MetaResources: {Limit: -1, TTL: 5 * time.Second}, + utils.MetaIPs: {Limit: -1, TTL: 5 * time.Second}, utils.MetaStats: {Limit: -1, TTL: 5 * time.Second}, utils.MetaThresholds: {Limit: -1, TTL: 5 * time.Second}, utils.MetaRoutes: {Limit: -1, TTL: 5 * time.Second}, @@ -2394,6 +2399,59 @@ func TestCgrLoaderCfgITDefaults(t *testing.T) { Layout: time.RFC3339}, }, }, + { + Type: utils.MetaIPs, + Filename: utils.IPsCsv, + Fields: []*FCTemplate{ + {Tag: "Tenant", + Path: "Tenant", + Type: utils.MetaVariable, + Value: utils.NewRSRParsersMustCompile("~*req.0", utils.InfieldSep), + Mandatory: true, + Layout: time.RFC3339}, + {Tag: "ID", + Path: "ID", + Type: utils.MetaVariable, + Value: utils.NewRSRParsersMustCompile("~*req.1", utils.InfieldSep), + Mandatory: true, + Layout: time.RFC3339}, + {Tag: "FilterIDs", + Path: "FilterIDs", + Type: utils.MetaVariable, + Value: utils.NewRSRParsersMustCompile("~*req.2", utils.InfieldSep), + Layout: time.RFC3339}, + {Tag: "Weights", + Path: "Weights", + Type: utils.MetaVariable, + Value: utils.NewRSRParsersMustCompile("~*req.3", utils.InfieldSep), + Layout: time.RFC3339}, + {Tag: "TTL", + Path: "TTL", + Type: utils.MetaVariable, + Value: utils.NewRSRParsersMustCompile("~*req.4", utils.InfieldSep), + Layout: time.RFC3339}, + {Tag: "Type", + Path: "Type", + Type: utils.MetaVariable, + Value: utils.NewRSRParsersMustCompile("~*req.5", utils.InfieldSep), + Layout: time.RFC3339}, + {Tag: "AddressPool", + Path: "AddressPool", + Type: utils.MetaVariable, + Value: utils.NewRSRParsersMustCompile("~*req.6", utils.InfieldSep), + Layout: time.RFC3339}, + {Tag: "Allocation", + Path: "Allocation", + Type: utils.MetaVariable, + Value: utils.NewRSRParsersMustCompile("~*req.7", utils.InfieldSep), + Layout: time.RFC3339}, + {Tag: "Stored", + Path: "Stored", + Type: utils.MetaVariable, + Value: utils.NewRSRParsersMustCompile("~*req.8", utils.InfieldSep), + Layout: time.RFC3339}, + }, + }, { Type: utils.MetaStats, Filename: utils.StatsCsv, @@ -3327,6 +3385,7 @@ func TestCgrCfgV1GetConfigSectionLoader(t *testing.T) { utils.MetaFilters: map[string]any{utils.LimitCfg: -1, utils.TTLCfg: "5s", utils.PrecacheCfg: false, utils.RemoteCfg: false, utils.ReplicateCfg: false, utils.StaticTTLCfg: false}, utils.MetaAttributes: map[string]any{utils.LimitCfg: -1, utils.TTLCfg: "5s", utils.PrecacheCfg: false, utils.RemoteCfg: false, utils.ReplicateCfg: false, utils.StaticTTLCfg: false}, utils.MetaResources: map[string]any{utils.LimitCfg: -1, utils.TTLCfg: "5s", utils.PrecacheCfg: false, utils.RemoteCfg: false, utils.ReplicateCfg: false, utils.StaticTTLCfg: false}, + utils.MetaIPs: map[string]any{utils.LimitCfg: -1, utils.TTLCfg: "5s", utils.PrecacheCfg: false, utils.RemoteCfg: false, utils.ReplicateCfg: false, utils.StaticTTLCfg: false}, utils.MetaStats: map[string]any{utils.LimitCfg: -1, utils.TTLCfg: "5s", utils.PrecacheCfg: false, utils.RemoteCfg: false, utils.ReplicateCfg: false, utils.StaticTTLCfg: false}, utils.MetaThresholds: map[string]any{utils.LimitCfg: -1, utils.TTLCfg: "5s", utils.PrecacheCfg: false, utils.RemoteCfg: false, utils.ReplicateCfg: false, utils.StaticTTLCfg: false}, utils.MetaRoutes: map[string]any{utils.LimitCfg: -1, utils.TTLCfg: "5s", utils.PrecacheCfg: false, utils.RemoteCfg: false, utils.ReplicateCfg: false, utils.StaticTTLCfg: false}, @@ -4615,7 +4674,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_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*accounts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*action_profile_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*action_profiles":{"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},"*filters":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*load_ids":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*ranking_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rankings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rate_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rate_profile_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rate_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_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},"*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},"*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_filtered":false}}` + 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_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*accounts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*action_profile_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*action_profiles":{"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},"*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},"*rate_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rate_profile_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rate_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_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},"*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},"*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_filtered":false}}` cfgCgr := NewDefaultCGRConfig() if err := cfgCgr.V1GetConfigAsJSON(context.Background(), &SectionWithAPIOpts{Sections: []string{DataDBJSON}}, &reply); err != nil { t.Error(err) @@ -4637,7 +4696,7 @@ func TestV1GetConfigAsJSONTls(t *testing.T) { func TestV1GetConfigAsJSONTCache(t *testing.T) { var reply string - expected := `{"caches":{"partitions":{"*account_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*accounts":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*action_profile_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*action_profiles":{"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"},"*diameter_messages":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*event_charges":{"limit":0,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"10s"},"*event_resources":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*filters":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*load_ids":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*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},"*rate_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rate_profile_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rate_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_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"},"*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},"*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_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*accounts":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*action_profile_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*action_profiles":{"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"},"*diameter_messages":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*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},"*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},"*rate_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rate_profile_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rate_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_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"},"*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},"*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{Sections: []string{CacheJSON}}, &reply); err != nil { t.Error(err) @@ -4859,7 +4918,7 @@ func TestV1GetConfigAsJSONSureTax(t *testing.T) { func TestV1GetConfigAsJSONLoaders(t *testing.T) { var reply string - expected := `{"loaders":[{"action":"*store","cache":{"*accounts":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*action_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*attributes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*chargers":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*filters":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*rankings":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*rate_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*resources":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*routes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*stats":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*thresholds":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*trends":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"}},"caches_conns":["*internal"],"data":[{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"new_branch":true,"path":"Rules.Type","tag":"Type","type":"*variable","value":"~*req.2"},{"path":"Rules.Element","tag":"Element","type":"*variable","value":"~*req.3"},{"path":"Rules.Values","tag":"Values","type":"*variable","value":"~*req.4"}],"file_name":"Filters.csv","flags":null,"type":"*filters"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"TenantID","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ProfileID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"new_branch":true,"path":"Attributes.FilterIDs","tag":"AttributeFilterIDs","type":"*variable","value":"~*req.5"},{"path":"Attributes.Blockers","tag":"AttributeBlockers","type":"*variable","value":"~*req.6"},{"path":"Attributes.Path","tag":"Path","type":"*variable","value":"~*req.7"},{"path":"Attributes.Type","tag":"Type","type":"*variable","value":"~*req.8"},{"path":"Attributes.Value","tag":"Value","type":"*variable","value":"~*req.9"}],"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":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"Weights","tag":"Weights","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":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.9"}],"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":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"path":"QueueLength","tag":"QueueLength","type":"*variable","value":"~*req.5"},{"path":"TTL","tag":"TTL","type":"*variable","value":"~*req.6"},{"path":"MinItems","tag":"MinItems","type":"*variable","value":"~*req.7"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.8"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.9"},{"new_branch":true,"path":"Metrics.MetricID","tag":"MetricIDs","type":"*variable","value":"~*req.10"},{"path":"Metrics.FilterIDs","tag":"MetricFilterIDs","type":"*variable","value":"~*req.11"},{"path":"Metrics.Blockers","tag":"MetricBlockers","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":"Weights","tag":"Weights","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":"ActionProfileIDs","tag":"ActionProfileIDs","type":"*variable","value":"~*req.8"},{"path":"Async","tag":"Async","type":"*variable","value":"~*req.9"}],"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":"Schedule","tag":"Schedule","type":"*variable","value":"~*req.2"},{"path":"StatID","tag":"StatID","type":"*variable","value":"~*req.3"},{"path":"Metrics","tag":"Metrics","type":"*variable","value":"~*req.4"},{"path":"TTL","tag":"TTL","type":"*variable","value":"~*req.5"},{"path":"QueueLength","tag":"QueueLength","type":"*variable","value":"~*req.6"},{"path":"MinItems","tag":"MinItems","type":"*variable","value":"~*req.7"},{"path":"CorrelationType","tag":"CorrelationType","type":"*variable","value":"~*req.8"},{"path":"Tolerance","tag":"Tolerance","type":"*variable","value":"~*req.9"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.10"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.11"}],"file_name":"Trends.csv","flags":null,"type":"*trends"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Schedule","tag":"Schedule","type":"*variable","value":"~*req.2"},{"path":"StatIDs","tag":"StatIDs","type":"*variable","value":"~*req.3"},{"path":"MetricIDs","tag":"MetricIDs","type":"*variable","value":"~*req.4"},{"path":"Sorting","tag":"Sorting","type":"*variable","value":"~*req.5"},{"path":"SortingParameters","tag":"SortingParameters","type":"*variable","value":"~*req.6"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.7"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.8"}],"file_name":"Rankings.csv","flags":null,"type":"*rankings"},{"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":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"path":"Sorting","tag":"Sorting","type":"*variable","value":"~*req.5"},{"path":"SortingParameters","tag":"SortingParameters","type":"*variable","value":"~*req.6"},{"new_branch":true,"path":"Routes.ID","tag":"RouteID","type":"*variable","value":"~*req.7"},{"path":"Routes.FilterIDs","tag":"RouteFilterIDs","type":"*variable","value":"~*req.8"},{"path":"Routes.AccountIDs","tag":"RouteAccountIDs","type":"*variable","value":"~*req.9"},{"path":"Routes.RateProfileIDs","tag":"RouteRateProfileIDs","type":"*variable","value":"~*req.10"},{"path":"Routes.ResourceIDs","tag":"RouteResourceIDs","type":"*variable","value":"~*req.11"},{"path":"Routes.StatIDs","tag":"RouteStatIDs","type":"*variable","value":"~*req.12"},{"path":"Routes.Weights","tag":"RouteWeights","type":"*variable","value":"~*req.13"},{"path":"Routes.Blockers","tag":"RouteBlockers","type":"*variable","value":"~*req.14"},{"path":"Routes.RouteParameters","tag":"RouteParameters","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":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"path":"RunID","tag":"RunID","type":"*variable","value":"~*req.5"},{"path":"AttributeIDs","tag":"AttributeIDs","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":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"MinCost","tag":"MinCost","type":"*variable","value":"~*req.4"},{"path":"MaxCost","tag":"MaxCost","type":"*variable","value":"~*req.5"},{"path":"MaxCostStrategy","tag":"MaxCostStrategy","type":"*variable","value":"~*req.6"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].FilterIDs","tag":"RateFilterIDs","type":"*variable","value":"~*req.8"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].ActivationTimes","tag":"RateActivationTimes","type":"*variable","value":"~*req.9"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].Weights","tag":"RateWeights","type":"*variable","value":"~*req.10"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].Blocker","tag":"RateBlocker","type":"*variable","value":"~*req.11"},{"filters":["*notempty:~*req.7:"],"new_branch":true,"path":"Rates[\u003c~*req.7\u003e].IntervalRates.IntervalStart","tag":"RateIntervalStart","type":"*variable","value":"~*req.12"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].IntervalRates.FixedFee","tag":"RateFixedFee","type":"*variable","value":"~*req.13"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].IntervalRates.RecurrentFee","tag":"RateRecurrentFee","type":"*variable","value":"~*req.14"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].IntervalRates.Unit","tag":"RateUnit","type":"*variable","value":"~*req.15"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].IntervalRates.Increment","tag":"RateIncrement","type":"*variable","value":"~*req.16"}],"file_name":"Rates.csv","flags":null,"type":"*rate_profiles"},{"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":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"path":"Schedule","tag":"Schedule","type":"*variable","value":"~*req.5"},{"path":"Targets[\u003c~*req.6\u003e]","tag":"TargetIDs","type":"*variable","value":"~*req.7"},{"filters":["*notempty:~*req.8:"],"path":"Actions[\u003c~*req.8\u003e].FilterIDs","tag":"ActionFilterIDs","type":"*variable","value":"~*req.9"},{"filters":["*notempty:~*req.8:"],"path":"Actions[\u003c~*req.8\u003e].TTL","tag":"ActionTTL","type":"*variable","value":"~*req.10"},{"filters":["*notempty:~*req.8:"],"path":"Actions[\u003c~*req.8\u003e].Type","tag":"ActionType","type":"*variable","value":"~*req.11"},{"filters":["*notempty:~*req.8:"],"path":"Actions[\u003c~*req.8\u003e].Opts","tag":"ActionOpts","type":"*variable","value":"~*req.12"},{"filters":["*notempty:~*req.8:"],"new_branch":true,"path":"Actions[\u003c~*req.8\u003e].Diktats.Path","tag":"ActionPath","type":"*variable","value":"~*req.13"},{"filters":["*notempty:~*req.8:"],"path":"Actions[\u003c~*req.8\u003e].Diktats.Value","tag":"ActionValue","type":"*variable","value":"~*req.14"}],"file_name":"Actions.csv","flags":null,"type":"*action_profiles"},{"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":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"path":"Opts","tag":"Opts","type":"*variable","value":"~*req.5"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].FilterIDs","tag":"BalanceFilterIDs","type":"*variable","value":"~*req.7"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].Weights","tag":"BalanceWeights","type":"*variable","value":"~*req.8"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].Blockers","tag":"BalanceBlockers","type":"*variable","value":"~*req.9"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].Type","tag":"BalanceType","type":"*variable","value":"~*req.10"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].Units","tag":"BalanceUnits","type":"*variable","value":"~*req.11"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].UnitFactors","tag":"BalanceUnitFactors","type":"*variable","value":"~*req.12"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].Opts","tag":"BalanceOpts","type":"*variable","value":"~*req.13"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].CostIncrements","tag":"BalanceCostIncrements","type":"*variable","value":"~*req.14"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].AttributeIDs","tag":"BalanceAttributeIDs","type":"*variable","value":"~*req.15"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].RateProfileIDs","tag":"BalanceRateProfileIDs","type":"*variable","value":"~*req.16"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.17"}],"file_name":"Accounts.csv","flags":null,"type":"*accounts"}],"enabled":false,"field_separator":",","id":"*default","lockfile_path":".cgr.lck","opts":{"*cache":"","*forceLock":false,"*stopOnError":false,"*withIndex":true},"run_delay":"0","tenant":"","tp_in_dir":"/var/spool/cgrates/loader/in","tp_out_dir":"/var/spool/cgrates/loader/out"}]}` + expected := `{"loaders":[{"action":"*store","cache":{"*accounts":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*action_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*attributes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*chargers":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*filters":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*ips":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*rankings":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*rate_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*resources":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*routes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*stats":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*thresholds":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*trends":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"}},"caches_conns":["*internal"],"data":[{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"new_branch":true,"path":"Rules.Type","tag":"Type","type":"*variable","value":"~*req.2"},{"path":"Rules.Element","tag":"Element","type":"*variable","value":"~*req.3"},{"path":"Rules.Values","tag":"Values","type":"*variable","value":"~*req.4"}],"file_name":"Filters.csv","flags":null,"type":"*filters"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"TenantID","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ProfileID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"new_branch":true,"path":"Attributes.FilterIDs","tag":"AttributeFilterIDs","type":"*variable","value":"~*req.5"},{"path":"Attributes.Blockers","tag":"AttributeBlockers","type":"*variable","value":"~*req.6"},{"path":"Attributes.Path","tag":"Path","type":"*variable","value":"~*req.7"},{"path":"Attributes.Type","tag":"Type","type":"*variable","value":"~*req.8"},{"path":"Attributes.Value","tag":"Value","type":"*variable","value":"~*req.9"}],"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":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"Weights","tag":"Weights","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":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.9"}],"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":"Weights","tag":"Weights","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"}],"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":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"path":"QueueLength","tag":"QueueLength","type":"*variable","value":"~*req.5"},{"path":"TTL","tag":"TTL","type":"*variable","value":"~*req.6"},{"path":"MinItems","tag":"MinItems","type":"*variable","value":"~*req.7"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.8"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.9"},{"new_branch":true,"path":"Metrics.MetricID","tag":"MetricIDs","type":"*variable","value":"~*req.10"},{"path":"Metrics.FilterIDs","tag":"MetricFilterIDs","type":"*variable","value":"~*req.11"},{"path":"Metrics.Blockers","tag":"MetricBlockers","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":"Weights","tag":"Weights","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":"ActionProfileIDs","tag":"ActionProfileIDs","type":"*variable","value":"~*req.8"},{"path":"Async","tag":"Async","type":"*variable","value":"~*req.9"}],"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":"Schedule","tag":"Schedule","type":"*variable","value":"~*req.2"},{"path":"StatID","tag":"StatID","type":"*variable","value":"~*req.3"},{"path":"Metrics","tag":"Metrics","type":"*variable","value":"~*req.4"},{"path":"TTL","tag":"TTL","type":"*variable","value":"~*req.5"},{"path":"QueueLength","tag":"QueueLength","type":"*variable","value":"~*req.6"},{"path":"MinItems","tag":"MinItems","type":"*variable","value":"~*req.7"},{"path":"CorrelationType","tag":"CorrelationType","type":"*variable","value":"~*req.8"},{"path":"Tolerance","tag":"Tolerance","type":"*variable","value":"~*req.9"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.10"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.11"}],"file_name":"Trends.csv","flags":null,"type":"*trends"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Schedule","tag":"Schedule","type":"*variable","value":"~*req.2"},{"path":"StatIDs","tag":"StatIDs","type":"*variable","value":"~*req.3"},{"path":"MetricIDs","tag":"MetricIDs","type":"*variable","value":"~*req.4"},{"path":"Sorting","tag":"Sorting","type":"*variable","value":"~*req.5"},{"path":"SortingParameters","tag":"SortingParameters","type":"*variable","value":"~*req.6"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.7"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.8"}],"file_name":"Rankings.csv","flags":null,"type":"*rankings"},{"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":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"path":"Sorting","tag":"Sorting","type":"*variable","value":"~*req.5"},{"path":"SortingParameters","tag":"SortingParameters","type":"*variable","value":"~*req.6"},{"new_branch":true,"path":"Routes.ID","tag":"RouteID","type":"*variable","value":"~*req.7"},{"path":"Routes.FilterIDs","tag":"RouteFilterIDs","type":"*variable","value":"~*req.8"},{"path":"Routes.AccountIDs","tag":"RouteAccountIDs","type":"*variable","value":"~*req.9"},{"path":"Routes.RateProfileIDs","tag":"RouteRateProfileIDs","type":"*variable","value":"~*req.10"},{"path":"Routes.ResourceIDs","tag":"RouteResourceIDs","type":"*variable","value":"~*req.11"},{"path":"Routes.StatIDs","tag":"RouteStatIDs","type":"*variable","value":"~*req.12"},{"path":"Routes.Weights","tag":"RouteWeights","type":"*variable","value":"~*req.13"},{"path":"Routes.Blockers","tag":"RouteBlockers","type":"*variable","value":"~*req.14"},{"path":"Routes.RouteParameters","tag":"RouteParameters","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":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"path":"RunID","tag":"RunID","type":"*variable","value":"~*req.5"},{"path":"AttributeIDs","tag":"AttributeIDs","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":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"MinCost","tag":"MinCost","type":"*variable","value":"~*req.4"},{"path":"MaxCost","tag":"MaxCost","type":"*variable","value":"~*req.5"},{"path":"MaxCostStrategy","tag":"MaxCostStrategy","type":"*variable","value":"~*req.6"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].FilterIDs","tag":"RateFilterIDs","type":"*variable","value":"~*req.8"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].ActivationTimes","tag":"RateActivationTimes","type":"*variable","value":"~*req.9"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].Weights","tag":"RateWeights","type":"*variable","value":"~*req.10"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].Blocker","tag":"RateBlocker","type":"*variable","value":"~*req.11"},{"filters":["*notempty:~*req.7:"],"new_branch":true,"path":"Rates[\u003c~*req.7\u003e].IntervalRates.IntervalStart","tag":"RateIntervalStart","type":"*variable","value":"~*req.12"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].IntervalRates.FixedFee","tag":"RateFixedFee","type":"*variable","value":"~*req.13"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].IntervalRates.RecurrentFee","tag":"RateRecurrentFee","type":"*variable","value":"~*req.14"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].IntervalRates.Unit","tag":"RateUnit","type":"*variable","value":"~*req.15"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].IntervalRates.Increment","tag":"RateIncrement","type":"*variable","value":"~*req.16"}],"file_name":"Rates.csv","flags":null,"type":"*rate_profiles"},{"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":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"path":"Schedule","tag":"Schedule","type":"*variable","value":"~*req.5"},{"path":"Targets[\u003c~*req.6\u003e]","tag":"TargetIDs","type":"*variable","value":"~*req.7"},{"filters":["*notempty:~*req.8:"],"path":"Actions[\u003c~*req.8\u003e].FilterIDs","tag":"ActionFilterIDs","type":"*variable","value":"~*req.9"},{"filters":["*notempty:~*req.8:"],"path":"Actions[\u003c~*req.8\u003e].TTL","tag":"ActionTTL","type":"*variable","value":"~*req.10"},{"filters":["*notempty:~*req.8:"],"path":"Actions[\u003c~*req.8\u003e].Type","tag":"ActionType","type":"*variable","value":"~*req.11"},{"filters":["*notempty:~*req.8:"],"path":"Actions[\u003c~*req.8\u003e].Opts","tag":"ActionOpts","type":"*variable","value":"~*req.12"},{"filters":["*notempty:~*req.8:"],"new_branch":true,"path":"Actions[\u003c~*req.8\u003e].Diktats.Path","tag":"ActionPath","type":"*variable","value":"~*req.13"},{"filters":["*notempty:~*req.8:"],"path":"Actions[\u003c~*req.8\u003e].Diktats.Value","tag":"ActionValue","type":"*variable","value":"~*req.14"}],"file_name":"Actions.csv","flags":null,"type":"*action_profiles"},{"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":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"path":"Opts","tag":"Opts","type":"*variable","value":"~*req.5"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].FilterIDs","tag":"BalanceFilterIDs","type":"*variable","value":"~*req.7"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].Weights","tag":"BalanceWeights","type":"*variable","value":"~*req.8"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].Blockers","tag":"BalanceBlockers","type":"*variable","value":"~*req.9"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].Type","tag":"BalanceType","type":"*variable","value":"~*req.10"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].Units","tag":"BalanceUnits","type":"*variable","value":"~*req.11"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].UnitFactors","tag":"BalanceUnitFactors","type":"*variable","value":"~*req.12"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].Opts","tag":"BalanceOpts","type":"*variable","value":"~*req.13"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].CostIncrements","tag":"BalanceCostIncrements","type":"*variable","value":"~*req.14"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].AttributeIDs","tag":"BalanceAttributeIDs","type":"*variable","value":"~*req.15"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].RateProfileIDs","tag":"BalanceRateProfileIDs","type":"*variable","value":"~*req.16"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.17"}],"file_name":"Accounts.csv","flags":null,"type":"*accounts"}],"enabled":false,"field_separator":",","id":"*default","lockfile_path":".cgr.lck","opts":{"*cache":"","*forceLock":false,"*stopOnError":false,"*withIndex":true},"run_delay":"0","tenant":"","tp_in_dir":"/var/spool/cgrates/loader/in","tp_out_dir":"/var/spool/cgrates/loader/out"}]}` cgrCfg := NewDefaultCGRConfig() if err := cgrCfg.V1GetConfigAsJSON(context.Background(), &SectionWithAPIOpts{Sections: []string{LoaderSJSON}}, &reply); err != nil { t.Error(err) @@ -5076,7 +5135,7 @@ func TestV1GetConfigAsJSONAllConfig(t *testing.T) { } }` var reply string - expected := `{"accounts":{"attributes_conns":[],"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"max_iterations":1000,"max_usage":"259200000000000","nested_fields":false,"notexists_indexed_fields":[],"opts":{"*profileIDs":[],"*profileIgnoreFilters":[{"FilterIDs":null,"Tenant":""}],"*usage":[{"FilterIDs":null,"Tenant":""}]},"prefix_indexed_fields":[],"rates_conns":[],"suffix_indexed_fields":[],"thresholds_conns":[]},"actions":{"accounts_conns":[],"cdrs_conns":[],"dynaprepaid_actionprofile":[],"ees_conns":[],"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"notexists_indexed_fields":[],"opts":{"*posterAttempts":[{"FilterIDs":null,"Tenant":""}],"*profileIDs":[],"*profileIgnoreFilters":[{"FilterIDs":null,"Tenant":""}]},"prefix_indexed_fields":[],"stats_conns":[],"suffix_indexed_fields":[],"tenants":[],"thresholds_conns":[]},"admins":{"actions_conns":[],"attributes_conns":[],"caches_conns":["*internal"],"ees_conns":[],"enabled":false},"analyzers":{"cleanup_interval":"1h0m0s","db_path":"/var/spool/cgrates/analyzers","ees_conns":[],"enabled":false,"index_type":"*scorch","opts":{"*exporterIDs":[]},"ttl":"24h0m0s"},"apiban":{"enabled":false,"keys":[]},"asterisk_agent":{"asterisk_conns":[{"address":"127.0.0.1:8088","alias":"","connect_attempts":3,"max_reconnect_interval":"0s","password":"CGRateS.org","reconnects":5,"user":"cgrates"}],"create_cdr":false,"enabled":false,"sessions_conns":["*birpc_internal"]},"attributes":{"accounts_conns":[],"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"notexists_indexed_fields":[],"opts":{"*processRuns":[{"FilterIDs":null,"Tenant":""}],"*profileIDs":[],"*profileIgnoreFilters":[{"FilterIDs":null,"Tenant":""}],"*profileRuns":[{"FilterIDs":null,"Tenant":""}]},"prefix_indexed_fields":[],"resources_conns":[],"stats_conns":[],"suffix_indexed_fields":[]},"caches":{"partitions":{"*account_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*accounts":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*action_profile_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*action_profiles":{"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"},"*diameter_messages":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*event_charges":{"limit":0,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"10s"},"*event_resources":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*filters":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*load_ids":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*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},"*rate_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rate_profile_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rate_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_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"},"*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},"*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":{"accounts_conns":[],"actions_conns":[],"attributes_conns":[],"chargers_conns":[],"ees_conns":[],"enabled":false,"extra_fields":[],"online_cdr_exports":null,"opts":{"*accounts":[{"FilterIDs":null,"Tenant":""}],"*attributes":[{"FilterIDs":null,"Tenant":""}],"*chargers":[{"FilterIDs":null,"Tenant":""}],"*ees":[{"FilterIDs":null,"Tenant":""}],"*rates":[{"FilterIDs":null,"Tenant":""}],"*refund":[{"FilterIDs":null,"Tenant":""}],"*rerate":[{"FilterIDs":null,"Tenant":""}],"*stats":[{"FilterIDs":null,"Tenant":""}],"*store":[{"FilterIDs":null,"Tenant":""}],"*thresholds":[{"FilterIDs":null,"Tenant":""}]},"rates_conns":[],"session_cost_retries":5,"stats_conns":[],"thresholds_conns":[]},"chargers":{"attributes_conns":[],"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"notexists_indexed_fields":[],"prefix_indexed_fields":[],"suffix_indexed_fields":[]},"config_db":{"db_host":"","db_name":"","db_password":"","db_port":0,"db_type":"*internal","db_user":"","opts":{"internalDBBackupPath":"/var/lib/cgrates/internal_db/backup/configdb","internalDBDumpInterval":"0s","internalDBDumpPath":"/var/lib/cgrates/internal_db/configdb","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,"redisReadTimeout":"0s","redisSentinel":"","redisTLS":false,"redisWriteTimeout":"0s"}},"configs":{"enabled":false,"root_dir":"/var/spool/cgrates/configs","url":"/configs/"},"cores":{"caps":0,"caps_stats_interval":"0","caps_strategy":"*busy","ees_conns":[],"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_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*accounts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*action_profile_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*action_profiles":{"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},"*filters":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*load_ids":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*ranking_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rankings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rate_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rate_profile_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rate_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_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},"*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},"*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_filtered":false},"diameter_agent":{"asr_template":"","dictionaries_path":"/usr/share/cgrates/diameter/dict/","enabled":false,"forced_disconnect":"*none","listen":"127.0.0.1:3868","listen_net":"tcp","origin_host":"CGR-DA","origin_realm":"cgrates.org","product_name":"CGRateS","rar_template":"","request_processors":[],"sessions_conns":["*birpc_internal"],"synced_conn_requests":false,"vendor_id":0},"dns_agent":{"enabled":false,"listeners":[{"address":"127.0.0.1:53","network":"udp"}],"request_processors":[],"sessions_conns":["*internal"],"timezone":""},"ees":{"attributes_conns":[],"cache":{"*fileCSV":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"}},"enabled":false,"exporters":[{"attempts":1,"attribute_context":"","attribute_ids":[],"blocker":false,"concurrent_requests":0,"efs_conns":["*internal"],"export_path":"/var/spool/cgrates/ees","failed_posts_dir":"/var/spool/cgrates/failed_posts","fields":[],"filters":[],"flags":[],"id":"*default","opts":{},"synchronous":false,"timezone":"","type":"*none"}]},"efs":{"enabled":false,"failed_posts_dir":"/var/spool/cgrates/failed_posts","failed_posts_ttl":"5s","poster_attempts":3},"ers":{"enabled":false,"partial_cache_ttl":"1s","readers":[{"cache_dump_fields":[],"concurrent_requests":1024,"fields":[{"mandatory":true,"path":"*cgreq.ToR","tag":"ToR","type":"*variable","value":"~*req.2"},{"mandatory":true,"path":"*cgreq.OriginID","tag":"OriginID","type":"*variable","value":"~*req.3"},{"mandatory":true,"path":"*cgreq.RequestType","tag":"RequestType","type":"*variable","value":"~*req.4"},{"mandatory":true,"path":"*cgreq.Tenant","tag":"Tenant","type":"*variable","value":"~*req.6"},{"mandatory":true,"path":"*cgreq.Category","tag":"Category","type":"*variable","value":"~*req.7"},{"mandatory":true,"path":"*cgreq.Account","tag":"Account","type":"*variable","value":"~*req.8"},{"mandatory":true,"path":"*cgreq.Subject","tag":"Subject","type":"*variable","value":"~*req.9"},{"mandatory":true,"path":"*cgreq.Destination","tag":"Destination","type":"*variable","value":"~*req.10"},{"mandatory":true,"path":"*cgreq.SetupTime","tag":"SetupTime","type":"*variable","value":"~*req.11"},{"mandatory":true,"path":"*cgreq.AnswerTime","tag":"AnswerTime","type":"*variable","value":"~*req.12"},{"mandatory":true,"path":"*cgreq.Usage","tag":"Usage","type":"*variable","value":"~*req.13"}],"filters":[],"flags":[],"id":"*default","max_reconnect_interval":"5m0s","opts":{"csvFieldSeparator":",","csvHeaderDefineChar":":","csvRowLength":0,"natsSubject":"cgrates_cdrs","partialCacheAction":"*none","partialOrderField":"~*req.AnswerTime"},"partial_commit_fields":[],"processed_path":"/var/spool/cgrates/ers/out","reconnects":-1,"run_delay":"0","source_path":"/var/spool/cgrates/ers/in","start_delay":"0","tenant":"","timezone":"","type":"*none"}],"sessions_conns":["*internal"]},"filters":{"accounts_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","request_processors":[],"sessions_conns":["*birpc_internal"],"subscribe_park":true},"general":{"caching_delay":"0","connect_attempts":5,"connect_timeout":"1s","dbdata_encoding":"*msgpack","decimal_max_scale":0,"decimal_min_scale":0,"decimal_precision":0,"decimal_rounding_mode":"*toNearestEven","default_caching":"*reload","default_category":"call","default_request_type":"*rated","default_tenant":"cgrates.org","default_timezone":"Local","digest_equal":":","digest_separator":",","locking_timeout":"0","max_parallel_conns":100,"max_reconnect_interval":"0","node_id":"ENGINE1","opts":{"*exporterIDs":[]},"reconnects":-1,"reply_timeout":"2s","rounding_decimals":5,"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","skipTLSVerification":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":[],"janus_agent":{"enabled":false,"janus_conns":[{"address":"127.0.0.1:8088","admin_address":"localhost:7188","admin_password":"","type":"*ws"}],"request_processors":[],"sessions_conns":["*internal"],"url":"/janus"},"kamailio_agent":{"create_cdr":false,"enabled":false,"evapi_conns":[{"address":"127.0.0.1:8448","alias":"","max_reconnect_interval":"0s","reconnects":5}],"sessions_conns":["*birpc_internal"],"timezone":""},"listen":{"http":"127.0.0.1:2080","http_tls":"127.0.0.1:2280","rpc_gob":"127.0.0.1:2013","rpc_gob_tls":"127.0.0.1:2023","rpc_json":"127.0.0.1:2012","rpc_json_tls":"127.0.0.1:2022"},"loader":{"actions_conns":["*localhost"],"caches_conns":["*localhost"],"data_path":"./","disable_reverse":false,"field_separator":",","gapi_credentials":".gapi/credentials.json","gapi_token":".gapi/token.json","tpid":""},"loaders":[{"action":"*store","cache":{"*accounts":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*action_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*attributes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*chargers":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*filters":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*rankings":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*rate_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*resources":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*routes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*stats":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*thresholds":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*trends":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"}},"caches_conns":["*internal"],"data":[{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"new_branch":true,"path":"Rules.Type","tag":"Type","type":"*variable","value":"~*req.2"},{"path":"Rules.Element","tag":"Element","type":"*variable","value":"~*req.3"},{"path":"Rules.Values","tag":"Values","type":"*variable","value":"~*req.4"}],"file_name":"Filters.csv","flags":null,"type":"*filters"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"TenantID","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ProfileID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"new_branch":true,"path":"Attributes.FilterIDs","tag":"AttributeFilterIDs","type":"*variable","value":"~*req.5"},{"path":"Attributes.Blockers","tag":"AttributeBlockers","type":"*variable","value":"~*req.6"},{"path":"Attributes.Path","tag":"Path","type":"*variable","value":"~*req.7"},{"path":"Attributes.Type","tag":"Type","type":"*variable","value":"~*req.8"},{"path":"Attributes.Value","tag":"Value","type":"*variable","value":"~*req.9"}],"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":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"Weights","tag":"Weights","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":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.9"}],"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":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"path":"QueueLength","tag":"QueueLength","type":"*variable","value":"~*req.5"},{"path":"TTL","tag":"TTL","type":"*variable","value":"~*req.6"},{"path":"MinItems","tag":"MinItems","type":"*variable","value":"~*req.7"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.8"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.9"},{"new_branch":true,"path":"Metrics.MetricID","tag":"MetricIDs","type":"*variable","value":"~*req.10"},{"path":"Metrics.FilterIDs","tag":"MetricFilterIDs","type":"*variable","value":"~*req.11"},{"path":"Metrics.Blockers","tag":"MetricBlockers","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":"Weights","tag":"Weights","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":"ActionProfileIDs","tag":"ActionProfileIDs","type":"*variable","value":"~*req.8"},{"path":"Async","tag":"Async","type":"*variable","value":"~*req.9"}],"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":"Schedule","tag":"Schedule","type":"*variable","value":"~*req.2"},{"path":"StatID","tag":"StatID","type":"*variable","value":"~*req.3"},{"path":"Metrics","tag":"Metrics","type":"*variable","value":"~*req.4"},{"path":"TTL","tag":"TTL","type":"*variable","value":"~*req.5"},{"path":"QueueLength","tag":"QueueLength","type":"*variable","value":"~*req.6"},{"path":"MinItems","tag":"MinItems","type":"*variable","value":"~*req.7"},{"path":"CorrelationType","tag":"CorrelationType","type":"*variable","value":"~*req.8"},{"path":"Tolerance","tag":"Tolerance","type":"*variable","value":"~*req.9"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.10"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.11"}],"file_name":"Trends.csv","flags":null,"type":"*trends"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Schedule","tag":"Schedule","type":"*variable","value":"~*req.2"},{"path":"StatIDs","tag":"StatIDs","type":"*variable","value":"~*req.3"},{"path":"MetricIDs","tag":"MetricIDs","type":"*variable","value":"~*req.4"},{"path":"Sorting","tag":"Sorting","type":"*variable","value":"~*req.5"},{"path":"SortingParameters","tag":"SortingParameters","type":"*variable","value":"~*req.6"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.7"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.8"}],"file_name":"Rankings.csv","flags":null,"type":"*rankings"},{"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":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"path":"Sorting","tag":"Sorting","type":"*variable","value":"~*req.5"},{"path":"SortingParameters","tag":"SortingParameters","type":"*variable","value":"~*req.6"},{"new_branch":true,"path":"Routes.ID","tag":"RouteID","type":"*variable","value":"~*req.7"},{"path":"Routes.FilterIDs","tag":"RouteFilterIDs","type":"*variable","value":"~*req.8"},{"path":"Routes.AccountIDs","tag":"RouteAccountIDs","type":"*variable","value":"~*req.9"},{"path":"Routes.RateProfileIDs","tag":"RouteRateProfileIDs","type":"*variable","value":"~*req.10"},{"path":"Routes.ResourceIDs","tag":"RouteResourceIDs","type":"*variable","value":"~*req.11"},{"path":"Routes.StatIDs","tag":"RouteStatIDs","type":"*variable","value":"~*req.12"},{"path":"Routes.Weights","tag":"RouteWeights","type":"*variable","value":"~*req.13"},{"path":"Routes.Blockers","tag":"RouteBlockers","type":"*variable","value":"~*req.14"},{"path":"Routes.RouteParameters","tag":"RouteParameters","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":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"path":"RunID","tag":"RunID","type":"*variable","value":"~*req.5"},{"path":"AttributeIDs","tag":"AttributeIDs","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":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"MinCost","tag":"MinCost","type":"*variable","value":"~*req.4"},{"path":"MaxCost","tag":"MaxCost","type":"*variable","value":"~*req.5"},{"path":"MaxCostStrategy","tag":"MaxCostStrategy","type":"*variable","value":"~*req.6"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].FilterIDs","tag":"RateFilterIDs","type":"*variable","value":"~*req.8"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].ActivationTimes","tag":"RateActivationTimes","type":"*variable","value":"~*req.9"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].Weights","tag":"RateWeights","type":"*variable","value":"~*req.10"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].Blocker","tag":"RateBlocker","type":"*variable","value":"~*req.11"},{"filters":["*notempty:~*req.7:"],"new_branch":true,"path":"Rates[\u003c~*req.7\u003e].IntervalRates.IntervalStart","tag":"RateIntervalStart","type":"*variable","value":"~*req.12"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].IntervalRates.FixedFee","tag":"RateFixedFee","type":"*variable","value":"~*req.13"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].IntervalRates.RecurrentFee","tag":"RateRecurrentFee","type":"*variable","value":"~*req.14"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].IntervalRates.Unit","tag":"RateUnit","type":"*variable","value":"~*req.15"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].IntervalRates.Increment","tag":"RateIncrement","type":"*variable","value":"~*req.16"}],"file_name":"Rates.csv","flags":null,"type":"*rate_profiles"},{"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":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"path":"Schedule","tag":"Schedule","type":"*variable","value":"~*req.5"},{"path":"Targets[\u003c~*req.6\u003e]","tag":"TargetIDs","type":"*variable","value":"~*req.7"},{"filters":["*notempty:~*req.8:"],"path":"Actions[\u003c~*req.8\u003e].FilterIDs","tag":"ActionFilterIDs","type":"*variable","value":"~*req.9"},{"filters":["*notempty:~*req.8:"],"path":"Actions[\u003c~*req.8\u003e].TTL","tag":"ActionTTL","type":"*variable","value":"~*req.10"},{"filters":["*notempty:~*req.8:"],"path":"Actions[\u003c~*req.8\u003e].Type","tag":"ActionType","type":"*variable","value":"~*req.11"},{"filters":["*notempty:~*req.8:"],"path":"Actions[\u003c~*req.8\u003e].Opts","tag":"ActionOpts","type":"*variable","value":"~*req.12"},{"filters":["*notempty:~*req.8:"],"new_branch":true,"path":"Actions[\u003c~*req.8\u003e].Diktats.Path","tag":"ActionPath","type":"*variable","value":"~*req.13"},{"filters":["*notempty:~*req.8:"],"path":"Actions[\u003c~*req.8\u003e].Diktats.Value","tag":"ActionValue","type":"*variable","value":"~*req.14"}],"file_name":"Actions.csv","flags":null,"type":"*action_profiles"},{"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":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"path":"Opts","tag":"Opts","type":"*variable","value":"~*req.5"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].FilterIDs","tag":"BalanceFilterIDs","type":"*variable","value":"~*req.7"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].Weights","tag":"BalanceWeights","type":"*variable","value":"~*req.8"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].Blockers","tag":"BalanceBlockers","type":"*variable","value":"~*req.9"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].Type","tag":"BalanceType","type":"*variable","value":"~*req.10"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].Units","tag":"BalanceUnits","type":"*variable","value":"~*req.11"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].UnitFactors","tag":"BalanceUnitFactors","type":"*variable","value":"~*req.12"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].Opts","tag":"BalanceOpts","type":"*variable","value":"~*req.13"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].CostIncrements","tag":"BalanceCostIncrements","type":"*variable","value":"~*req.14"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].AttributeIDs","tag":"BalanceAttributeIDs","type":"*variable","value":"~*req.15"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].RateProfileIDs","tag":"BalanceRateProfileIDs","type":"*variable","value":"~*req.16"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.17"}],"file_name":"Accounts.csv","flags":null,"type":"*accounts"}],"enabled":false,"field_separator":",","id":"*default","lockfile_path":".cgr.lck","opts":{"*cache":"","*forceLock":false,"*stopOnError":false,"*withIndex":true},"run_delay":"0","tenant":"","tp_in_dir":"/var/spool/cgrates/loader/in","tp_out_dir":"/var/spool/cgrates/loader/out"}],"logger":{"efs_conns":["*internal"],"level":6,"opts":{"failed_posts_dir":"/var/spool/cgrates/failed_posts","kafka_attempts":1,"kafka_conn":"","kafka_topic":""},"type":"*syslog"},"migrator":{"out_datadb_encoding":"msgpack","out_datadb_host":"127.0.0.1","out_datadb_name":"10","out_datadb_opts":{"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"},"out_datadb_password":"","out_datadb_port":"6379","out_datadb_type":"*redis","out_datadb_user":"cgrates","users_filters":null},"prometheus_agent":{"collect_go_metrics":false,"collect_process_metrics":false,"cores_conns":[],"enabled":false,"path":"/prometheus","stat_queue_ids":null,"stats_conns":[]},"radius_agent":{"client_dictionaries":{"*default":"/usr/share/cgrates/radius/dict/"},"client_secrets":{"*default":"CGRateS.org"},"enabled":false,"listen_acct":"127.0.0.1:1813","listen_auth":"127.0.0.1:1812","listen_net":"udp","request_processors":[],"sessions_conns":["*internal"]},"rankings":{"ees_conns":[],"ees_exporter_ids":null,"enabled":false,"scheduled_ids":{},"stats_conns":[],"store_interval":"","thresholds_conns":[]},"rates":{"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"notexists_indexed_fields":[],"opts":{"*intervalStart":[{"FilterIDs":null,"Tenant":""}],"*profileIDs":[],"*profileIgnoreFilters":[{"FilterIDs":null,"Tenant":""}],"*startTime":[{"FilterIDs":null,"Tenant":""}],"*usage":[{"FilterIDs":null,"Tenant":""}]},"prefix_indexed_fields":[],"rate_exists_indexed_fields":[],"rate_indexed_selects":true,"rate_nested_fields":false,"rate_notexists_indexed_fields":[],"rate_prefix_indexed_fields":[],"rate_suffix_indexed_fields":[],"suffix_indexed_fields":[],"verbosity":1000},"registrarc":{"rpc":{"hosts":[],"refresh_interval":"5m0s","registrars_conns":[]}},"resources":{"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"notexists_indexed_fields":[],"opts":{"*units":[{"FilterIDs":null,"Tenant":""}],"*usageID":[{"FilterIDs":null,"Tenant":""}],"*usageTTL":[{"FilterIDs":null,"Tenant":""}]},"prefix_indexed_fields":[],"store_interval":"","suffix_indexed_fields":[],"thresholds_conns":[]},"routes":{"accounts_conns":[],"attributes_conns":[],"default_ratio":1,"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"notexists_indexed_fields":[],"opts":{"*context":[{"FilterIDs":null,"Tenant":""}],"*ignoreErrors":[{"FilterIDs":null,"Tenant":""}],"*limit":[],"*maxCost":[{"Tenant":"","Value":""}],"*maxItems":[],"*offset":[],"*profileCount":[{"FilterIDs":null,"Tenant":""}],"*usage":[{"FilterIDs":null,"Tenant":""}]},"prefix_indexed_fields":[],"rates_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"}},"sentrypeer":{"audience":"https://sentrypeer.com/api","client_id":"","client_secret":"","grant_type":"client_credentials","ips_url":"https://sentrypeer.com/api/ip-addresses","numbers_url":"https://sentrypeer.com/api/phone-numbers","token_url":"https://authz.sentrypeer.com/oauth/token"},"sessions":{"accounts_conns":[],"actions_conns":[],"alterable_fields":[],"attributes_conns":[],"cdrs_conns":[],"channel_sync_interval":"0","chargers_conns":[],"client_protocol":1,"default_usage":{"*any":"3h0m0s","*data":"1048576","*sms":"1","*voice":"3h0m0s"},"enabled":false,"listen_bigob":"","listen_bijson":"127.0.0.1:2014","min_dur_low_balance":"0","opts":{"*accounts":[{"FilterIDs":null,"Tenant":""}],"*accountsForceUsage":[],"*attributes":[{"FilterIDs":null,"Tenant":""}],"*attributesDerivedReply":[{"FilterIDs":null,"Tenant":""}],"*blockerError":[{"FilterIDs":null,"Tenant":""}],"*cdrs":[{"FilterIDs":null,"Tenant":""}],"*cdrsDerivedReply":[{"FilterIDs":null,"Tenant":""}],"*chargeable":[{"FilterIDs":null,"Tenant":""}],"*chargers":[{"FilterIDs":null,"Tenant":""}],"*debitInterval":[{"FilterIDs":null,"Tenant":""}],"*forceUsage":[],"*initiate":[{"FilterIDs":null,"Tenant":""}],"*maxUsage":[{"FilterIDs":null,"Tenant":""}],"*message":[{"FilterIDs":null,"Tenant":""}],"*originID":[],"*resources":[{"FilterIDs":null,"Tenant":""}],"*resourcesAllocate":[{"FilterIDs":null,"Tenant":""}],"*resourcesAuthorize":[{"FilterIDs":null,"Tenant":""}],"*resourcesDerivedReply":[{"FilterIDs":null,"Tenant":""}],"*resourcesRelease":[{"FilterIDs":null,"Tenant":""}],"*routes":[{"FilterIDs":null,"Tenant":""}],"*routesDerivedReply":[{"FilterIDs":null,"Tenant":""}],"*stats":[{"FilterIDs":null,"Tenant":""}],"*statsDerivedReply":[{"FilterIDs":null,"Tenant":""}],"*terminate":[{"FilterIDs":null,"Tenant":""}],"*thresholds":[{"FilterIDs":null,"Tenant":""}],"*thresholdsDerivedReply":[{"FilterIDs":null,"Tenant":""}],"*ttl":[{"FilterIDs":null,"Tenant":""}],"*ttlLastUsage":[],"*ttlLastUsed":[],"*ttlMaxDelay":[{"FilterIDs":null,"Tenant":""}],"*ttlUsage":[],"*update":[{"FilterIDs":null,"Tenant":""}]},"rates_conns":[],"replication_conns":[],"resources_conns":[],"routes_conns":[],"session_indexes":[],"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":"1s","sessions_conns":["*internal"],"timezone":""},"stats":{"ees_conns":[],"ees_exporter_ids":null,"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"notexists_indexed_fields":[],"opts":{"*profileIDs":[],"*profileIgnoreFilters":[{"FilterIDs":null,"Tenant":""}],"*roundingDecimals":[]},"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}},"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","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":"~*opts.*originID","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"}],"*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"}],"*fsa":[{"path":"*cgreq.ToR","tag":"ToR","type":"*constant","value":"*voice"},{"path":"*cgreq.PDD","tag":"PDD","type":"*composed","value":"~*req.variable_progress_mediamsec;ms"},{"path":"*cgreq.ACD","tag":"ACD","type":"*composed","value":"~*req.variable_cdr_acd;s"},{"path":"*cgreq.OriginID","tag":"OriginID","type":"*variable","value":"~*req.Unique-ID"},{"path":"*opts.*originID","tag":"*originID","type":"*variable","value":"~*req.Unique-ID"},{"path":"*cgreq.OriginHost","tag":"OriginHost","type":"*variable","value":"~*req.variable_cgr_originhost"},{"path":"*cgreq.Account","tag":"Account","type":"*variable","value":"~*req.Caller-Username"},{"path":"*cgreq.Source","tag":"Source","type":"*composed","value":"FS_;~*req.Event-Name"},{"filters":["*string:*req.variable_process_cdr:false"],"path":"*cgreq.RequestType","tag":"RequestType","type":"*constant","value":"*none"},{"filters":["*string:*req.Caller-Dialplan:inline"],"path":"*cgreq.RequestType","tag":"RequestType","type":"*constant","value":"*none"},{"filters":["*exists:*cgreq.RequestType:"],"path":"*cgreq.RequestType","tag":"RequestType","type":"*constant","value":"*prepaid"},{"path":"*cgreq.Tenant","tag":"Tenant","type":"*constant","value":"cgrates.org"},{"path":"*cgreq.Category","tag":"Category","type":"*constant","value":"call"},{"path":"*cgreq.Subject","tag":"Subject","type":"*variable","value":"~*req.Caller-Username"},{"path":"*cgreq.Destination","tag":"Destination","type":"*variable","value":"~*req.Caller-Destination-Number"},{"path":"*cgreq.SetupTime","tag":"SetupTime","type":"*variable","value":"~*req.Caller-Channel-Created-Time"},{"path":"*cgreq.AnswerTime","tag":"AnswerTime","type":"*variable","value":"~*req.Caller-Channel-Answered-Time"},{"path":"*cgreq.Usage","tag":"Usage","type":"*composed","value":"~*req.variable_billsec;s"},{"path":"*cgreq.Route","tag":"Route","type":"*variable","value":"~*req.variable_cgr_route"},{"path":"*cgreq.Cost","tag":"Cost","type":"*constant","value":"-1.0"},{"filters":["*notempty:*req.Hangup-Cause:"],"path":"*cgreq.DisconnectCause","tag":"DisconnectCause","type":"*variable","value":"~*req.Hangup-Cause"}],"*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":{"actions_conns":[],"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"notexists_indexed_fields":[],"opts":{"*profileIDs":[],"*profileIgnoreFilters":[{"FilterIDs":null,"Tenant":""}]},"prefix_indexed_fields":[],"store_interval":"","suffix_indexed_fields":[]},"tls":{"ca_certificate":"","client_certificate":"","client_key":"","server_certificate":"","server_key":"","server_name":"","server_policy":4},"tpes":{"enabled":false},"trends":{"ees_conns":[],"ees_exporter_ids":null,"enabled":false,"scheduled_ids":{},"stats_conns":[],"store_interval":"","store_uncompressed_limit":0,"thresholds_conns":[]}}` + expected := `{"accounts":{"attributes_conns":[],"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"max_iterations":1000,"max_usage":"259200000000000","nested_fields":false,"notexists_indexed_fields":[],"opts":{"*profileIDs":[],"*profileIgnoreFilters":[{"FilterIDs":null,"Tenant":""}],"*usage":[{"FilterIDs":null,"Tenant":""}]},"prefix_indexed_fields":[],"rates_conns":[],"suffix_indexed_fields":[],"thresholds_conns":[]},"actions":{"accounts_conns":[],"cdrs_conns":[],"dynaprepaid_actionprofile":[],"ees_conns":[],"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"notexists_indexed_fields":[],"opts":{"*posterAttempts":[{"FilterIDs":null,"Tenant":""}],"*profileIDs":[],"*profileIgnoreFilters":[{"FilterIDs":null,"Tenant":""}]},"prefix_indexed_fields":[],"stats_conns":[],"suffix_indexed_fields":[],"tenants":[],"thresholds_conns":[]},"admins":{"actions_conns":[],"attributes_conns":[],"caches_conns":["*internal"],"ees_conns":[],"enabled":false},"analyzers":{"cleanup_interval":"1h0m0s","db_path":"/var/spool/cgrates/analyzers","ees_conns":[],"enabled":false,"index_type":"*scorch","opts":{"*exporterIDs":[]},"ttl":"24h0m0s"},"apiban":{"enabled":false,"keys":[]},"asterisk_agent":{"asterisk_conns":[{"address":"127.0.0.1:8088","alias":"","connect_attempts":3,"max_reconnect_interval":"0s","password":"CGRateS.org","reconnects":5,"user":"cgrates"}],"create_cdr":false,"enabled":false,"sessions_conns":["*birpc_internal"]},"attributes":{"accounts_conns":[],"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"notexists_indexed_fields":[],"opts":{"*processRuns":[{"FilterIDs":null,"Tenant":""}],"*profileIDs":[],"*profileIgnoreFilters":[{"FilterIDs":null,"Tenant":""}],"*profileRuns":[{"FilterIDs":null,"Tenant":""}]},"prefix_indexed_fields":[],"resources_conns":[],"stats_conns":[],"suffix_indexed_fields":[]},"caches":{"partitions":{"*account_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*accounts":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*action_profile_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*action_profiles":{"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"},"*diameter_messages":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*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},"*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},"*rate_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rate_profile_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rate_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_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"},"*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},"*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":{"accounts_conns":[],"actions_conns":[],"attributes_conns":[],"chargers_conns":[],"ees_conns":[],"enabled":false,"extra_fields":[],"online_cdr_exports":null,"opts":{"*accounts":[{"FilterIDs":null,"Tenant":""}],"*attributes":[{"FilterIDs":null,"Tenant":""}],"*chargers":[{"FilterIDs":null,"Tenant":""}],"*ees":[{"FilterIDs":null,"Tenant":""}],"*rates":[{"FilterIDs":null,"Tenant":""}],"*refund":[{"FilterIDs":null,"Tenant":""}],"*rerate":[{"FilterIDs":null,"Tenant":""}],"*stats":[{"FilterIDs":null,"Tenant":""}],"*store":[{"FilterIDs":null,"Tenant":""}],"*thresholds":[{"FilterIDs":null,"Tenant":""}]},"rates_conns":[],"session_cost_retries":5,"stats_conns":[],"thresholds_conns":[]},"chargers":{"attributes_conns":[],"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"notexists_indexed_fields":[],"prefix_indexed_fields":[],"suffix_indexed_fields":[]},"config_db":{"db_host":"","db_name":"","db_password":"","db_port":0,"db_type":"*internal","db_user":"","opts":{"internalDBBackupPath":"/var/lib/cgrates/internal_db/backup/configdb","internalDBDumpInterval":"0s","internalDBDumpPath":"/var/lib/cgrates/internal_db/configdb","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,"redisReadTimeout":"0s","redisSentinel":"","redisTLS":false,"redisWriteTimeout":"0s"}},"configs":{"enabled":false,"root_dir":"/var/spool/cgrates/configs","url":"/configs/"},"cores":{"caps":0,"caps_stats_interval":"0","caps_strategy":"*busy","ees_conns":[],"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_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*accounts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*action_profile_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*action_profiles":{"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},"*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},"*rate_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rate_profile_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rate_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_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},"*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},"*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_filtered":false},"diameter_agent":{"asr_template":"","dictionaries_path":"/usr/share/cgrates/diameter/dict/","enabled":false,"forced_disconnect":"*none","listen":"127.0.0.1:3868","listen_net":"tcp","origin_host":"CGR-DA","origin_realm":"cgrates.org","product_name":"CGRateS","rar_template":"","request_processors":[],"sessions_conns":["*birpc_internal"],"synced_conn_requests":false,"vendor_id":0},"dns_agent":{"enabled":false,"listeners":[{"address":"127.0.0.1:53","network":"udp"}],"request_processors":[],"sessions_conns":["*internal"],"timezone":""},"ees":{"attributes_conns":[],"cache":{"*fileCSV":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"}},"enabled":false,"exporters":[{"attempts":1,"attribute_context":"","attribute_ids":[],"blocker":false,"concurrent_requests":0,"efs_conns":["*internal"],"export_path":"/var/spool/cgrates/ees","failed_posts_dir":"/var/spool/cgrates/failed_posts","fields":[],"filters":[],"flags":[],"id":"*default","opts":{},"synchronous":false,"timezone":"","type":"*none"}]},"efs":{"enabled":false,"failed_posts_dir":"/var/spool/cgrates/failed_posts","failed_posts_ttl":"5s","poster_attempts":3},"ers":{"enabled":false,"partial_cache_ttl":"1s","readers":[{"cache_dump_fields":[],"concurrent_requests":1024,"fields":[{"mandatory":true,"path":"*cgreq.ToR","tag":"ToR","type":"*variable","value":"~*req.2"},{"mandatory":true,"path":"*cgreq.OriginID","tag":"OriginID","type":"*variable","value":"~*req.3"},{"mandatory":true,"path":"*cgreq.RequestType","tag":"RequestType","type":"*variable","value":"~*req.4"},{"mandatory":true,"path":"*cgreq.Tenant","tag":"Tenant","type":"*variable","value":"~*req.6"},{"mandatory":true,"path":"*cgreq.Category","tag":"Category","type":"*variable","value":"~*req.7"},{"mandatory":true,"path":"*cgreq.Account","tag":"Account","type":"*variable","value":"~*req.8"},{"mandatory":true,"path":"*cgreq.Subject","tag":"Subject","type":"*variable","value":"~*req.9"},{"mandatory":true,"path":"*cgreq.Destination","tag":"Destination","type":"*variable","value":"~*req.10"},{"mandatory":true,"path":"*cgreq.SetupTime","tag":"SetupTime","type":"*variable","value":"~*req.11"},{"mandatory":true,"path":"*cgreq.AnswerTime","tag":"AnswerTime","type":"*variable","value":"~*req.12"},{"mandatory":true,"path":"*cgreq.Usage","tag":"Usage","type":"*variable","value":"~*req.13"}],"filters":[],"flags":[],"id":"*default","max_reconnect_interval":"5m0s","opts":{"csvFieldSeparator":",","csvHeaderDefineChar":":","csvRowLength":0,"natsSubject":"cgrates_cdrs","partialCacheAction":"*none","partialOrderField":"~*req.AnswerTime"},"partial_commit_fields":[],"processed_path":"/var/spool/cgrates/ers/out","reconnects":-1,"run_delay":"0","source_path":"/var/spool/cgrates/ers/in","start_delay":"0","tenant":"","timezone":"","type":"*none"}],"sessions_conns":["*internal"]},"filters":{"accounts_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","request_processors":[],"sessions_conns":["*birpc_internal"],"subscribe_park":true},"general":{"caching_delay":"0","connect_attempts":5,"connect_timeout":"1s","dbdata_encoding":"*msgpack","decimal_max_scale":0,"decimal_min_scale":0,"decimal_precision":0,"decimal_rounding_mode":"*toNearestEven","default_caching":"*reload","default_category":"call","default_request_type":"*rated","default_tenant":"cgrates.org","default_timezone":"Local","digest_equal":":","digest_separator":",","locking_timeout":"0","max_parallel_conns":100,"max_reconnect_interval":"0","node_id":"ENGINE1","opts":{"*exporterIDs":[]},"reconnects":-1,"reply_timeout":"2s","rounding_decimals":5,"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","skipTLSVerification":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,"notexists_indexed_fields":null,"opts":{"*ttl":[{"FilterIDs":null,"Tenant":""}],"*units":[{"FilterIDs":null,"Tenant":""}],"*usageID":[{"FilterIDs":null,"Tenant":""}]},"prefix_indexed_fields":[],"store_interval":"0s","string_indexed_fields":null,"suffix_indexed_fields":[]},"janus_agent":{"enabled":false,"janus_conns":[{"address":"127.0.0.1:8088","admin_address":"localhost:7188","admin_password":"","type":"*ws"}],"request_processors":[],"sessions_conns":["*internal"],"url":"/janus"},"kamailio_agent":{"create_cdr":false,"enabled":false,"evapi_conns":[{"address":"127.0.0.1:8448","alias":"","max_reconnect_interval":"0s","reconnects":5}],"sessions_conns":["*birpc_internal"],"timezone":""},"listen":{"http":"127.0.0.1:2080","http_tls":"127.0.0.1:2280","rpc_gob":"127.0.0.1:2013","rpc_gob_tls":"127.0.0.1:2023","rpc_json":"127.0.0.1:2012","rpc_json_tls":"127.0.0.1:2022"},"loader":{"actions_conns":["*localhost"],"caches_conns":["*localhost"],"data_path":"./","disable_reverse":false,"field_separator":",","gapi_credentials":".gapi/credentials.json","gapi_token":".gapi/token.json","tpid":""},"loaders":[{"action":"*store","cache":{"*accounts":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*action_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*attributes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*chargers":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*filters":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*ips":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*rankings":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*rate_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*resources":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*routes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*stats":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*thresholds":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"},"*trends":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"}},"caches_conns":["*internal"],"data":[{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"new_branch":true,"path":"Rules.Type","tag":"Type","type":"*variable","value":"~*req.2"},{"path":"Rules.Element","tag":"Element","type":"*variable","value":"~*req.3"},{"path":"Rules.Values","tag":"Values","type":"*variable","value":"~*req.4"}],"file_name":"Filters.csv","flags":null,"type":"*filters"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"TenantID","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ProfileID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"new_branch":true,"path":"Attributes.FilterIDs","tag":"AttributeFilterIDs","type":"*variable","value":"~*req.5"},{"path":"Attributes.Blockers","tag":"AttributeBlockers","type":"*variable","value":"~*req.6"},{"path":"Attributes.Path","tag":"Path","type":"*variable","value":"~*req.7"},{"path":"Attributes.Type","tag":"Type","type":"*variable","value":"~*req.8"},{"path":"Attributes.Value","tag":"Value","type":"*variable","value":"~*req.9"}],"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":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"Weights","tag":"Weights","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":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.9"}],"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":"Weights","tag":"Weights","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"}],"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":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"path":"QueueLength","tag":"QueueLength","type":"*variable","value":"~*req.5"},{"path":"TTL","tag":"TTL","type":"*variable","value":"~*req.6"},{"path":"MinItems","tag":"MinItems","type":"*variable","value":"~*req.7"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.8"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.9"},{"new_branch":true,"path":"Metrics.MetricID","tag":"MetricIDs","type":"*variable","value":"~*req.10"},{"path":"Metrics.FilterIDs","tag":"MetricFilterIDs","type":"*variable","value":"~*req.11"},{"path":"Metrics.Blockers","tag":"MetricBlockers","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":"Weights","tag":"Weights","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":"ActionProfileIDs","tag":"ActionProfileIDs","type":"*variable","value":"~*req.8"},{"path":"Async","tag":"Async","type":"*variable","value":"~*req.9"}],"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":"Schedule","tag":"Schedule","type":"*variable","value":"~*req.2"},{"path":"StatID","tag":"StatID","type":"*variable","value":"~*req.3"},{"path":"Metrics","tag":"Metrics","type":"*variable","value":"~*req.4"},{"path":"TTL","tag":"TTL","type":"*variable","value":"~*req.5"},{"path":"QueueLength","tag":"QueueLength","type":"*variable","value":"~*req.6"},{"path":"MinItems","tag":"MinItems","type":"*variable","value":"~*req.7"},{"path":"CorrelationType","tag":"CorrelationType","type":"*variable","value":"~*req.8"},{"path":"Tolerance","tag":"Tolerance","type":"*variable","value":"~*req.9"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.10"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.11"}],"file_name":"Trends.csv","flags":null,"type":"*trends"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Schedule","tag":"Schedule","type":"*variable","value":"~*req.2"},{"path":"StatIDs","tag":"StatIDs","type":"*variable","value":"~*req.3"},{"path":"MetricIDs","tag":"MetricIDs","type":"*variable","value":"~*req.4"},{"path":"Sorting","tag":"Sorting","type":"*variable","value":"~*req.5"},{"path":"SortingParameters","tag":"SortingParameters","type":"*variable","value":"~*req.6"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.7"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.8"}],"file_name":"Rankings.csv","flags":null,"type":"*rankings"},{"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":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"path":"Sorting","tag":"Sorting","type":"*variable","value":"~*req.5"},{"path":"SortingParameters","tag":"SortingParameters","type":"*variable","value":"~*req.6"},{"new_branch":true,"path":"Routes.ID","tag":"RouteID","type":"*variable","value":"~*req.7"},{"path":"Routes.FilterIDs","tag":"RouteFilterIDs","type":"*variable","value":"~*req.8"},{"path":"Routes.AccountIDs","tag":"RouteAccountIDs","type":"*variable","value":"~*req.9"},{"path":"Routes.RateProfileIDs","tag":"RouteRateProfileIDs","type":"*variable","value":"~*req.10"},{"path":"Routes.ResourceIDs","tag":"RouteResourceIDs","type":"*variable","value":"~*req.11"},{"path":"Routes.StatIDs","tag":"RouteStatIDs","type":"*variable","value":"~*req.12"},{"path":"Routes.Weights","tag":"RouteWeights","type":"*variable","value":"~*req.13"},{"path":"Routes.Blockers","tag":"RouteBlockers","type":"*variable","value":"~*req.14"},{"path":"Routes.RouteParameters","tag":"RouteParameters","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":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"path":"RunID","tag":"RunID","type":"*variable","value":"~*req.5"},{"path":"AttributeIDs","tag":"AttributeIDs","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":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"MinCost","tag":"MinCost","type":"*variable","value":"~*req.4"},{"path":"MaxCost","tag":"MaxCost","type":"*variable","value":"~*req.5"},{"path":"MaxCostStrategy","tag":"MaxCostStrategy","type":"*variable","value":"~*req.6"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].FilterIDs","tag":"RateFilterIDs","type":"*variable","value":"~*req.8"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].ActivationTimes","tag":"RateActivationTimes","type":"*variable","value":"~*req.9"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].Weights","tag":"RateWeights","type":"*variable","value":"~*req.10"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].Blocker","tag":"RateBlocker","type":"*variable","value":"~*req.11"},{"filters":["*notempty:~*req.7:"],"new_branch":true,"path":"Rates[\u003c~*req.7\u003e].IntervalRates.IntervalStart","tag":"RateIntervalStart","type":"*variable","value":"~*req.12"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].IntervalRates.FixedFee","tag":"RateFixedFee","type":"*variable","value":"~*req.13"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].IntervalRates.RecurrentFee","tag":"RateRecurrentFee","type":"*variable","value":"~*req.14"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].IntervalRates.Unit","tag":"RateUnit","type":"*variable","value":"~*req.15"},{"filters":["*notempty:~*req.7:"],"path":"Rates[\u003c~*req.7\u003e].IntervalRates.Increment","tag":"RateIncrement","type":"*variable","value":"~*req.16"}],"file_name":"Rates.csv","flags":null,"type":"*rate_profiles"},{"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":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"path":"Schedule","tag":"Schedule","type":"*variable","value":"~*req.5"},{"path":"Targets[\u003c~*req.6\u003e]","tag":"TargetIDs","type":"*variable","value":"~*req.7"},{"filters":["*notempty:~*req.8:"],"path":"Actions[\u003c~*req.8\u003e].FilterIDs","tag":"ActionFilterIDs","type":"*variable","value":"~*req.9"},{"filters":["*notempty:~*req.8:"],"path":"Actions[\u003c~*req.8\u003e].TTL","tag":"ActionTTL","type":"*variable","value":"~*req.10"},{"filters":["*notempty:~*req.8:"],"path":"Actions[\u003c~*req.8\u003e].Type","tag":"ActionType","type":"*variable","value":"~*req.11"},{"filters":["*notempty:~*req.8:"],"path":"Actions[\u003c~*req.8\u003e].Opts","tag":"ActionOpts","type":"*variable","value":"~*req.12"},{"filters":["*notempty:~*req.8:"],"new_branch":true,"path":"Actions[\u003c~*req.8\u003e].Diktats.Path","tag":"ActionPath","type":"*variable","value":"~*req.13"},{"filters":["*notempty:~*req.8:"],"path":"Actions[\u003c~*req.8\u003e].Diktats.Value","tag":"ActionValue","type":"*variable","value":"~*req.14"}],"file_name":"Actions.csv","flags":null,"type":"*action_profiles"},{"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":"Weights","tag":"Weights","type":"*variable","value":"~*req.3"},{"path":"Blockers","tag":"Blockers","type":"*variable","value":"~*req.4"},{"path":"Opts","tag":"Opts","type":"*variable","value":"~*req.5"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].FilterIDs","tag":"BalanceFilterIDs","type":"*variable","value":"~*req.7"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].Weights","tag":"BalanceWeights","type":"*variable","value":"~*req.8"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].Blockers","tag":"BalanceBlockers","type":"*variable","value":"~*req.9"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].Type","tag":"BalanceType","type":"*variable","value":"~*req.10"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].Units","tag":"BalanceUnits","type":"*variable","value":"~*req.11"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].UnitFactors","tag":"BalanceUnitFactors","type":"*variable","value":"~*req.12"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].Opts","tag":"BalanceOpts","type":"*variable","value":"~*req.13"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].CostIncrements","tag":"BalanceCostIncrements","type":"*variable","value":"~*req.14"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].AttributeIDs","tag":"BalanceAttributeIDs","type":"*variable","value":"~*req.15"},{"filters":["*notempty:~*req.6:"],"path":"Balances[\u003c~*req.6\u003e].RateProfileIDs","tag":"BalanceRateProfileIDs","type":"*variable","value":"~*req.16"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.17"}],"file_name":"Accounts.csv","flags":null,"type":"*accounts"}],"enabled":false,"field_separator":",","id":"*default","lockfile_path":".cgr.lck","opts":{"*cache":"","*forceLock":false,"*stopOnError":false,"*withIndex":true},"run_delay":"0","tenant":"","tp_in_dir":"/var/spool/cgrates/loader/in","tp_out_dir":"/var/spool/cgrates/loader/out"}],"logger":{"efs_conns":["*internal"],"level":6,"opts":{"failed_posts_dir":"/var/spool/cgrates/failed_posts","kafka_attempts":1,"kafka_conn":"","kafka_topic":""},"type":"*syslog"},"migrator":{"out_datadb_encoding":"msgpack","out_datadb_host":"127.0.0.1","out_datadb_name":"10","out_datadb_opts":{"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"},"out_datadb_password":"","out_datadb_port":"6379","out_datadb_type":"*redis","out_datadb_user":"cgrates","users_filters":null},"prometheus_agent":{"collect_go_metrics":false,"collect_process_metrics":false,"cores_conns":[],"enabled":false,"path":"/prometheus","stat_queue_ids":null,"stats_conns":[]},"radius_agent":{"client_dictionaries":{"*default":"/usr/share/cgrates/radius/dict/"},"client_secrets":{"*default":"CGRateS.org"},"enabled":false,"listen_acct":"127.0.0.1:1813","listen_auth":"127.0.0.1:1812","listen_net":"udp","request_processors":[],"sessions_conns":["*internal"]},"rankings":{"ees_conns":[],"ees_exporter_ids":null,"enabled":false,"scheduled_ids":{},"stats_conns":[],"store_interval":"","thresholds_conns":[]},"rates":{"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"notexists_indexed_fields":[],"opts":{"*intervalStart":[{"FilterIDs":null,"Tenant":""}],"*profileIDs":[],"*profileIgnoreFilters":[{"FilterIDs":null,"Tenant":""}],"*startTime":[{"FilterIDs":null,"Tenant":""}],"*usage":[{"FilterIDs":null,"Tenant":""}]},"prefix_indexed_fields":[],"rate_exists_indexed_fields":[],"rate_indexed_selects":true,"rate_nested_fields":false,"rate_notexists_indexed_fields":[],"rate_prefix_indexed_fields":[],"rate_suffix_indexed_fields":[],"suffix_indexed_fields":[],"verbosity":1000},"registrarc":{"rpc":{"hosts":[],"refresh_interval":"5m0s","registrars_conns":[]}},"resources":{"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"notexists_indexed_fields":[],"opts":{"*units":[{"FilterIDs":null,"Tenant":""}],"*usageID":[{"FilterIDs":null,"Tenant":""}],"*usageTTL":[{"FilterIDs":null,"Tenant":""}]},"prefix_indexed_fields":[],"store_interval":"","suffix_indexed_fields":[],"thresholds_conns":[]},"routes":{"accounts_conns":[],"attributes_conns":[],"default_ratio":1,"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"notexists_indexed_fields":[],"opts":{"*context":[{"FilterIDs":null,"Tenant":""}],"*ignoreErrors":[{"FilterIDs":null,"Tenant":""}],"*limit":[],"*maxCost":[{"Tenant":"","Value":""}],"*maxItems":[],"*offset":[],"*profileCount":[{"FilterIDs":null,"Tenant":""}],"*usage":[{"FilterIDs":null,"Tenant":""}]},"prefix_indexed_fields":[],"rates_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"}},"sentrypeer":{"audience":"https://sentrypeer.com/api","client_id":"","client_secret":"","grant_type":"client_credentials","ips_url":"https://sentrypeer.com/api/ip-addresses","numbers_url":"https://sentrypeer.com/api/phone-numbers","token_url":"https://authz.sentrypeer.com/oauth/token"},"sessions":{"accounts_conns":[],"actions_conns":[],"alterable_fields":[],"attributes_conns":[],"cdrs_conns":[],"channel_sync_interval":"0","chargers_conns":[],"client_protocol":1,"default_usage":{"*any":"3h0m0s","*data":"1048576","*sms":"1","*voice":"3h0m0s"},"enabled":false,"listen_bigob":"","listen_bijson":"127.0.0.1:2014","min_dur_low_balance":"0","opts":{"*accounts":[{"FilterIDs":null,"Tenant":""}],"*accountsForceUsage":[],"*attributes":[{"FilterIDs":null,"Tenant":""}],"*attributesDerivedReply":[{"FilterIDs":null,"Tenant":""}],"*blockerError":[{"FilterIDs":null,"Tenant":""}],"*cdrs":[{"FilterIDs":null,"Tenant":""}],"*cdrsDerivedReply":[{"FilterIDs":null,"Tenant":""}],"*chargeable":[{"FilterIDs":null,"Tenant":""}],"*chargers":[{"FilterIDs":null,"Tenant":""}],"*debitInterval":[{"FilterIDs":null,"Tenant":""}],"*forceUsage":[],"*initiate":[{"FilterIDs":null,"Tenant":""}],"*maxUsage":[{"FilterIDs":null,"Tenant":""}],"*message":[{"FilterIDs":null,"Tenant":""}],"*originID":[],"*resources":[{"FilterIDs":null,"Tenant":""}],"*resourcesAllocate":[{"FilterIDs":null,"Tenant":""}],"*resourcesAuthorize":[{"FilterIDs":null,"Tenant":""}],"*resourcesDerivedReply":[{"FilterIDs":null,"Tenant":""}],"*resourcesRelease":[{"FilterIDs":null,"Tenant":""}],"*routes":[{"FilterIDs":null,"Tenant":""}],"*routesDerivedReply":[{"FilterIDs":null,"Tenant":""}],"*stats":[{"FilterIDs":null,"Tenant":""}],"*statsDerivedReply":[{"FilterIDs":null,"Tenant":""}],"*terminate":[{"FilterIDs":null,"Tenant":""}],"*thresholds":[{"FilterIDs":null,"Tenant":""}],"*thresholdsDerivedReply":[{"FilterIDs":null,"Tenant":""}],"*ttl":[{"FilterIDs":null,"Tenant":""}],"*ttlLastUsage":[],"*ttlLastUsed":[],"*ttlMaxDelay":[{"FilterIDs":null,"Tenant":""}],"*ttlUsage":[],"*update":[{"FilterIDs":null,"Tenant":""}]},"rates_conns":[],"replication_conns":[],"resources_conns":[],"routes_conns":[],"session_indexes":[],"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":"1s","sessions_conns":["*internal"],"timezone":""},"stats":{"ees_conns":[],"ees_exporter_ids":null,"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"notexists_indexed_fields":[],"opts":{"*profileIDs":[],"*profileIgnoreFilters":[{"FilterIDs":null,"Tenant":""}],"*roundingDecimals":[]},"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}},"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","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":"~*opts.*originID","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"}],"*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"}],"*fsa":[{"path":"*cgreq.ToR","tag":"ToR","type":"*constant","value":"*voice"},{"path":"*cgreq.PDD","tag":"PDD","type":"*composed","value":"~*req.variable_progress_mediamsec;ms"},{"path":"*cgreq.ACD","tag":"ACD","type":"*composed","value":"~*req.variable_cdr_acd;s"},{"path":"*cgreq.OriginID","tag":"OriginID","type":"*variable","value":"~*req.Unique-ID"},{"path":"*opts.*originID","tag":"*originID","type":"*variable","value":"~*req.Unique-ID"},{"path":"*cgreq.OriginHost","tag":"OriginHost","type":"*variable","value":"~*req.variable_cgr_originhost"},{"path":"*cgreq.Account","tag":"Account","type":"*variable","value":"~*req.Caller-Username"},{"path":"*cgreq.Source","tag":"Source","type":"*composed","value":"FS_;~*req.Event-Name"},{"filters":["*string:*req.variable_process_cdr:false"],"path":"*cgreq.RequestType","tag":"RequestType","type":"*constant","value":"*none"},{"filters":["*string:*req.Caller-Dialplan:inline"],"path":"*cgreq.RequestType","tag":"RequestType","type":"*constant","value":"*none"},{"filters":["*exists:*cgreq.RequestType:"],"path":"*cgreq.RequestType","tag":"RequestType","type":"*constant","value":"*prepaid"},{"path":"*cgreq.Tenant","tag":"Tenant","type":"*constant","value":"cgrates.org"},{"path":"*cgreq.Category","tag":"Category","type":"*constant","value":"call"},{"path":"*cgreq.Subject","tag":"Subject","type":"*variable","value":"~*req.Caller-Username"},{"path":"*cgreq.Destination","tag":"Destination","type":"*variable","value":"~*req.Caller-Destination-Number"},{"path":"*cgreq.SetupTime","tag":"SetupTime","type":"*variable","value":"~*req.Caller-Channel-Created-Time"},{"path":"*cgreq.AnswerTime","tag":"AnswerTime","type":"*variable","value":"~*req.Caller-Channel-Answered-Time"},{"path":"*cgreq.Usage","tag":"Usage","type":"*composed","value":"~*req.variable_billsec;s"},{"path":"*cgreq.Route","tag":"Route","type":"*variable","value":"~*req.variable_cgr_route"},{"path":"*cgreq.Cost","tag":"Cost","type":"*constant","value":"-1.0"},{"filters":["*notempty:*req.Hangup-Cause:"],"path":"*cgreq.DisconnectCause","tag":"DisconnectCause","type":"*variable","value":"~*req.Hangup-Cause"}],"*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":{"actions_conns":[],"enabled":false,"exists_indexed_fields":[],"indexed_selects":true,"nested_fields":false,"notexists_indexed_fields":[],"opts":{"*profileIDs":[],"*profileIgnoreFilters":[{"FilterIDs":null,"Tenant":""}]},"prefix_indexed_fields":[],"store_interval":"","suffix_indexed_fields":[]},"tls":{"ca_certificate":"","client_certificate":"","client_key":"","server_certificate":"","server_key":"","server_name":"","server_policy":4},"tpes":{"enabled":false},"trends":{"ees_conns":[],"ees_exporter_ids":null,"enabled":false,"scheduled_ids":{},"stats_conns":[],"store_interval":"","store_uncompressed_limit":0,"thresholds_conns":[]}}` cgrCfg, err := NewCGRConfigFromJSONStringWithDefaults(cfgJSON) if err != nil { diff --git a/config/configsanity.go b/config/configsanity.go index 3b8c3dfd8..779094ae5 100644 --- a/config/configsanity.go +++ b/config/configsanity.go @@ -112,7 +112,7 @@ func (cfg *CGRConfig) checkConfigSanity() error { } } for _, data := range ldrSCfg.Data { - if !posibleLoaderTypes.Has(data.Type) { + if !possibleLoaderTypes.Has(data.Type) { return fmt.Errorf("<%s> unsupported data type %s", utils.LoaderS, data.Type) } diff --git a/config/ips.go b/config/ips.go new file mode 100644 index 000000000..c6ba42ebb --- /dev/null +++ b/config/ips.go @@ -0,0 +1,268 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package config + +import ( + "slices" + "time" + + "github.com/cgrates/birpc/context" + "github.com/cgrates/cgrates/utils" +) + +const ( + IPsUsageIDDftOpt = utils.EmptyString + IPsTTLDftOpt = 72 * time.Hour + IPsUnitsDftOpt = 1 +) + +// IPsJsonCfg holds the unparsed ips section configuration as found in the +// config file. +type IPsJsonCfg struct { + Enabled *bool `json:"enabled"` + IndexedSelects *bool `json:"indexed_selects"` + StoreInterval *string `json:"store_interval"` + StringIndexedFields *[]string `json:"string_indexed_fields"` + PrefixIndexedFields *[]string `json:"prefix_indexed_fields"` + SuffixIndexedFields *[]string `json:"suffix_indexed_fields"` + ExistsIndexedFields *[]string `json:"exists_indexed_fields"` + NotExistsIndexedFields *[]string `json:"not_exists_indexed_fields"` + NestedFields *bool `json:"nested_fields"` + Opts *IPsOptsJson `json:"opts"` +} + +// IPsCfg represents the configuration of the IPs module. +type IPsCfg struct { + Enabled bool + IndexedSelects bool + StoreInterval time.Duration + StringIndexedFields *[]string + PrefixIndexedFields *[]string + SuffixIndexedFields *[]string + ExistsIndexedFields *[]string + NotExistsIndexedFields *[]string + NestedFields bool + Opts *IPsOpts +} + +// Load loads the IPs section of the configuration. +func (rlcfg *IPsCfg) Load(ctx *context.Context, jsnCfg ConfigDB, _ *CGRConfig) (err error) { + jsnRLSCfg := new(IPsJsonCfg) + if err = jsnCfg.GetSection(ctx, IPsJSON, jsnRLSCfg); err != nil { + return + } + return rlcfg.loadFromJSONCfg(jsnRLSCfg) +} + +func (IPsCfg) SName() string { return IPsJSON } +func (c IPsCfg) CloneSection() Section { return c.Clone() } + +func (c *IPsCfg) loadFromJSONCfg(jc *IPsJsonCfg) error { + if jc == nil { + return nil + } + if jc.Enabled != nil { + c.Enabled = *jc.Enabled + } + if jc.IndexedSelects != nil { + c.IndexedSelects = *jc.IndexedSelects + } + if jc.StoreInterval != nil { + v, err := utils.ParseDurationWithNanosecs(*jc.StoreInterval) + if err != nil { + return err + } + c.StoreInterval = v + } + if jc.StringIndexedFields != nil { + sif := slices.Clone(*jc.StringIndexedFields) + c.StringIndexedFields = &sif + } + if jc.PrefixIndexedFields != nil { + pif := slices.Clone(*jc.PrefixIndexedFields) + c.PrefixIndexedFields = &pif + } + if jc.SuffixIndexedFields != nil { + sif := slices.Clone(*jc.SuffixIndexedFields) + c.SuffixIndexedFields = &sif + } + if jc.ExistsIndexedFields != nil { + eif := slices.Clone(*jc.ExistsIndexedFields) + c.ExistsIndexedFields = &eif + } + if jc.NotExistsIndexedFields != nil { + c.NotExistsIndexedFields = utils.SliceStringPointer(slices.Clone(*jc.NotExistsIndexedFields)) + } + if jc.NestedFields != nil { + c.NestedFields = *jc.NestedFields + } + if jc.Opts != nil { + if err := c.Opts.loadFromJSONCfg(jc.Opts); err != nil { + return err + } + } + return nil +} + +// Clone returns a deep copy of IPsCfg. +func (c IPsCfg) Clone() *IPsCfg { + clone := &IPsCfg{ + Enabled: c.Enabled, + IndexedSelects: c.IndexedSelects, + StoreInterval: c.StoreInterval, + NestedFields: c.NestedFields, + Opts: c.Opts.Clone(), + } + if c.StringIndexedFields != nil { + idx := slices.Clone(*c.StringIndexedFields) + clone.StringIndexedFields = &idx + } + if c.PrefixIndexedFields != nil { + idx := slices.Clone(*c.PrefixIndexedFields) + clone.PrefixIndexedFields = &idx + } + if c.SuffixIndexedFields != nil { + idx := slices.Clone(*c.SuffixIndexedFields) + clone.SuffixIndexedFields = &idx + } + if c.ExistsIndexedFields != nil { + idx := slices.Clone(*c.ExistsIndexedFields) + clone.ExistsIndexedFields = &idx + } + if c.NotExistsIndexedFields != nil { + idx := slices.Clone(*c.NotExistsIndexedFields) + clone.NotExistsIndexedFields = &idx + } + return clone +} + +// AsMapInterface returns the ips config as a map[string]any. +func (c IPsCfg) AsMapInterface() any { + return map[string]any{ + utils.EnabledCfg: c.Enabled, + utils.IndexedSelectsCfg: c.IndexedSelects, + utils.NestedFieldsCfg: c.NestedFields, + utils.StoreIntervalCfg: c.StoreInterval.String(), + utils.StringIndexedFieldsCfg: c.StringIndexedFields, + utils.PrefixIndexedFieldsCfg: c.PrefixIndexedFields, + utils.SuffixIndexedFieldsCfg: c.SuffixIndexedFields, + utils.ExistsIndexedFieldsCfg: c.ExistsIndexedFields, + utils.NotExistsIndexedFieldsCfg: c.NotExistsIndexedFields, + utils.OptsCfg: c.Opts.AsMapInterface(), + } +} + +type IPsOptsJson struct { + UsageID []*DynamicInterfaceOpt `json:"*usageID"` + TTL []*DynamicInterfaceOpt `json:"*ttl"` + Units []*DynamicInterfaceOpt `json:"*units"` +} + +type IPsOpts struct { + UsageID []*DynamicStringOpt + TTL []*DynamicDurationOpt + Units []*DynamicFloat64Opt +} + +func (o *IPsOpts) loadFromJSONCfg(jc *IPsOptsJson) error { + if jc == nil { + return nil + } + if jc.UsageID != nil { + usageID, err := InterfaceToDynamicStringOpts(jc.UsageID) + if err != nil { + return err + } + o.UsageID = append(o.UsageID, usageID...) + } + if jc.TTL != nil { + ttl, err := IfaceToDurationDynamicOpts(jc.TTL) + if err != nil { + return err + } + o.TTL = append(o.TTL, ttl...) + } + if jc.Units != nil { + units, err := InterfaceToFloat64DynamicOpts(jc.Units) + if err != nil { + return err + } + o.Units = append(o.Units, units...) + } + return nil +} + +// Clone returns a deep copy of IPsOpts. +func (o *IPsOpts) Clone() *IPsOpts { + return &IPsOpts{ + UsageID: CloneDynamicStringOpt(o.UsageID), + TTL: CloneDynamicDurationOpt(o.TTL), + Units: CloneDynamicFloat64Opt(o.Units), + } +} + +// AsMapInterface returns the config as a map[string]any. +func (o *IPsOpts) AsMapInterface() map[string]any { + return map[string]any{ + utils.MetaUsageIDCfg: o.UsageID, + utils.MetaUnitsCfg: o.Units, + utils.MetaTTLCfg: o.TTL, + } +} + +func diffIPsOptsJsonCfg(d *IPsOptsJson, v1, v2 *IPsOpts) *IPsOptsJson { + if d == nil { + d = new(IPsOptsJson) + } + if !DynamicStringOptEqual(v1.UsageID, v2.UsageID) { + d.UsageID = DynamicStringToInterfaceOpts(v2.UsageID) + } + if !DynamicDurationOptEqual(v1.TTL, v2.TTL) { + d.TTL = DurationToIfaceDynamicOpts(v2.TTL) + } + if !DynamicFloat64OptEqual(v1.Units, v2.Units) { + d.Units = Float64ToInterfaceDynamicOpts(v2.Units) + } + return d +} + +func diffIPsJsonCfg(d *IPsJsonCfg, v1, v2 *IPsCfg) *IPsJsonCfg { + if d == nil { + d = new(IPsJsonCfg) + } + if v1.Enabled != v2.Enabled { + d.Enabled = utils.BoolPointer(v2.Enabled) + } + if v1.IndexedSelects != v2.IndexedSelects { + d.IndexedSelects = utils.BoolPointer(v2.IndexedSelects) + } + if v1.StoreInterval != v2.StoreInterval { + d.StoreInterval = utils.StringPointer(v2.StoreInterval.String()) + } + d.StringIndexedFields = diffIndexSlice(d.StringIndexedFields, v1.StringIndexedFields, v2.StringIndexedFields) + d.PrefixIndexedFields = diffIndexSlice(d.PrefixIndexedFields, v1.PrefixIndexedFields, v2.PrefixIndexedFields) + d.SuffixIndexedFields = diffIndexSlice(d.SuffixIndexedFields, v1.SuffixIndexedFields, v2.SuffixIndexedFields) + d.ExistsIndexedFields = diffIndexSlice(d.ExistsIndexedFields, v1.ExistsIndexedFields, v2.ExistsIndexedFields) + d.NotExistsIndexedFields = diffIndexSlice(d.NotExistsIndexedFields, v1.NotExistsIndexedFields, v2.NotExistsIndexedFields) + if v1.NestedFields != v2.NestedFields { + d.NestedFields = utils.BoolPointer(v2.NestedFields) + } + d.Opts = diffIPsOptsJsonCfg(d.Opts, v1.Opts, v2.Opts) + return d +} diff --git a/config/loaderscfg_test.go b/config/loaderscfg_test.go index 3e640cf82..83dfeff99 100644 --- a/config/loaderscfg_test.go +++ b/config/loaderscfg_test.go @@ -72,6 +72,7 @@ func TestLoaderSCfgloadFromJsonCfgCase1(t *testing.T) { utils.MetaFilters: {Limit: -1, TTL: 5 * time.Second}, utils.MetaAttributes: {Limit: -1, TTL: 5 * time.Second}, utils.MetaResources: {Limit: -1, TTL: 5 * time.Second}, + utils.MetaIPs: {Limit: -1, TTL: 5 * time.Second}, utils.MetaStats: {Limit: -1, TTL: 5 * time.Second}, utils.MetaThresholds: {Limit: -1, TTL: 5 * time.Second}, utils.MetaRoutes: {Limit: -1, TTL: 5 * time.Second}, @@ -190,6 +191,59 @@ func TestLoaderSCfgloadFromJsonCfgCase1(t *testing.T) { Layout: time.RFC3339}, }, }, + { + Type: utils.MetaIPs, + Filename: utils.IPsCsv, + Fields: []*FCTemplate{ + {Tag: "Tenant", + Path: "Tenant", + Type: utils.MetaVariable, + Value: utils.NewRSRParsersMustCompile("~*req.0", utils.InfieldSep), + Mandatory: true, + Layout: time.RFC3339}, + {Tag: "ID", + Path: "ID", + Type: utils.MetaVariable, + Value: utils.NewRSRParsersMustCompile("~*req.1", utils.InfieldSep), + Mandatory: true, + Layout: time.RFC3339}, + {Tag: "FilterIDs", + Path: "FilterIDs", + Type: utils.MetaVariable, + Value: utils.NewRSRParsersMustCompile("~*req.2", utils.InfieldSep), + Layout: time.RFC3339}, + {Tag: "Weights", + Path: "Weights", + Type: utils.MetaVariable, + Value: utils.NewRSRParsersMustCompile("~*req.3", utils.InfieldSep), + Layout: time.RFC3339}, + {Tag: "TTL", + Path: "TTL", + Type: utils.MetaVariable, + Value: utils.NewRSRParsersMustCompile("~*req.4", utils.InfieldSep), + Layout: time.RFC3339}, + {Tag: "Type", + Path: "Type", + Type: utils.MetaVariable, + Value: utils.NewRSRParsersMustCompile("~*req.5", utils.InfieldSep), + Layout: time.RFC3339}, + {Tag: "AddressPool", + Path: "AddressPool", + Type: utils.MetaVariable, + Value: utils.NewRSRParsersMustCompile("~*req.6", utils.InfieldSep), + Layout: time.RFC3339}, + {Tag: "Allocation", + Path: "Allocation", + Type: utils.MetaVariable, + Value: utils.NewRSRParsersMustCompile("~*req.7", utils.InfieldSep), + Layout: time.RFC3339}, + {Tag: "Stored", + Path: "Stored", + Type: utils.MetaVariable, + Value: utils.NewRSRParsersMustCompile("~*req.8", utils.InfieldSep), + Layout: time.RFC3339}, + }, + }, { Type: utils.MetaStats, Filename: utils.StatsCsv, @@ -1235,6 +1289,69 @@ func TestLoaderCfgAsMapInterfaceCase1(t *testing.T) { }, }, }, + { + utils.TypeCfg: "*ips", + utils.FilenameCfg: "IPs.csv", + utils.FlagsCfg: flags, + utils.FieldsCfg: []map[string]any{ + { + utils.TagCfg: "Tenant", + utils.PathCfg: "Tenant", + utils.TypeCfg: "*variable", + utils.ValueCfg: "~*req.0", + utils.MandatoryCfg: true, + }, + { + utils.TagCfg: "ID", + utils.PathCfg: "ID", + utils.TypeCfg: "*variable", + utils.ValueCfg: "~*req.1", + utils.MandatoryCfg: true, + }, + { + utils.TagCfg: "FilterIDs", + utils.PathCfg: "FilterIDs", + utils.TypeCfg: "*variable", + utils.ValueCfg: "~*req.2", + }, + { + utils.TagCfg: "Weights", + utils.PathCfg: "Weights", + utils.TypeCfg: "*variable", + utils.ValueCfg: "~*req.3", + }, + { + utils.TagCfg: "TTL", + utils.PathCfg: "TTL", + utils.TypeCfg: "*variable", + utils.ValueCfg: "~*req.4", + }, + { + utils.TagCfg: "Type", + utils.PathCfg: "Type", + utils.TypeCfg: "*variable", + utils.ValueCfg: "~*req.5", + }, + { + utils.TagCfg: "AddressPool", + utils.PathCfg: "AddressPool", + utils.TypeCfg: "*variable", + utils.ValueCfg: "~*req.6", + }, + { + utils.TagCfg: "Allocation", + utils.PathCfg: "Allocation", + utils.TypeCfg: "*variable", + utils.ValueCfg: "~*req.7", + }, + { + utils.TagCfg: "Stored", + utils.PathCfg: "Stored", + utils.TypeCfg: "*variable", + utils.ValueCfg: "~*req.8", + }, + }, + }, { utils.TypeCfg: "*stats", utils.FilenameCfg: "Stats.csv", @@ -2027,6 +2144,7 @@ func TestLoaderCfgAsMapInterfaceCase1(t *testing.T) { utils.MetaFilters: map[string]any{utils.LimitCfg: -1, utils.TTLCfg: "5s", utils.PrecacheCfg: false, utils.RemoteCfg: false, utils.ReplicateCfg: false, utils.StaticTTLCfg: false}, utils.MetaAttributes: map[string]any{utils.LimitCfg: -1, utils.TTLCfg: "5s", utils.PrecacheCfg: false, utils.RemoteCfg: false, utils.ReplicateCfg: false, utils.StaticTTLCfg: false}, utils.MetaResources: map[string]any{utils.LimitCfg: -1, utils.TTLCfg: "5s", utils.PrecacheCfg: false, utils.RemoteCfg: false, utils.ReplicateCfg: false, utils.StaticTTLCfg: false}, + utils.MetaIPs: map[string]any{utils.LimitCfg: -1, utils.TTLCfg: "5s", utils.PrecacheCfg: false, utils.RemoteCfg: false, utils.ReplicateCfg: false, utils.StaticTTLCfg: false}, utils.MetaStats: map[string]any{utils.LimitCfg: -1, utils.TTLCfg: "5s", utils.PrecacheCfg: false, utils.RemoteCfg: false, utils.ReplicateCfg: false, utils.StaticTTLCfg: false}, utils.MetaThresholds: map[string]any{utils.LimitCfg: -1, utils.TTLCfg: "5s", utils.PrecacheCfg: false, utils.RemoteCfg: false, utils.ReplicateCfg: false, utils.StaticTTLCfg: false}, utils.MetaRoutes: map[string]any{utils.LimitCfg: -1, utils.TTLCfg: "5s", utils.PrecacheCfg: false, utils.RemoteCfg: false, utils.ReplicateCfg: false, utils.StaticTTLCfg: false}, diff --git a/data/tariffplans/loadRateTest/IPs.csv b/data/tariffplans/loadRateTest/IPs.csv new file mode 100644 index 000000000..e69de29bb diff --git a/data/tariffplans/testit/IPs.csv b/data/tariffplans/testit/IPs.csv new file mode 100644 index 000000000..54e8535b8 --- /dev/null +++ b/data/tariffplans/testit/IPs.csv @@ -0,0 +1,2 @@ +#Tenant[0],Id[1],FilterIDs[2],Weights[3],TTL[4],Type[5],AddressPool[6],Allocation[7],Stored[8] +cgrates.org,IPs1,*string:~*req.Account:1001,;10,1s,ipv4,172.16.1.1/24,alloc_success,true diff --git a/data/tariffplans/testtp/IPs.csv b/data/tariffplans/testtp/IPs.csv new file mode 100644 index 000000000..e69de29bb diff --git a/engine/datadbmock.go b/engine/datadbmock.go index 2be67b80b..9248befb7 100644 --- a/engine/datadbmock.go +++ b/engine/datadbmock.go @@ -49,6 +49,12 @@ type DataDBMock struct { RemoveResourceDrvF func(ctx *context.Context, tnt, id string) error SetResourceDrvF func(ctx *context.Context, r *utils.Resource) error GetResourceDrvF func(ctx *context.Context, tenant, id string) (*utils.Resource, error) + GetIPProfileDrvF func(ctx *context.Context, tnt, id string) (*utils.IPProfile, error) + SetIPProfileDrvF func(ctx *context.Context, ipp *utils.IPProfile) error + RemoveIPProfileDrvF func(ctx *context.Context, tnt, id string) error + RemoveIPDrvF func(ctx *context.Context, tnt, id string) error + SetIPDrvF func(ctx *context.Context, ip *utils.IP) error + GetIPDrvF func(ctx *context.Context, tenant, id string) (*utils.IP, error) SetTrendProfileDrvF func(ctx *context.Context, tr *utils.TrendProfile) (err error) GetTrendProfileDrvF func(ctx *context.Context, tenant string, id string) (sq *utils.TrendProfile, err error) RemTrendProfileDrvF func(ctx *context.Context, tenant string, id string) (err error) @@ -163,6 +169,48 @@ func (dbM *DataDBMock) RemoveResourceDrv(ctx *context.Context, tnt, id string) e return utils.ErrNotImplemented } +func (dbM *DataDBMock) GetIPProfileDrv(ctx *context.Context, tnt, id string) (*utils.IPProfile, error) { + if dbM.GetIPProfileDrvF != nil { + return dbM.GetIPProfileDrvF(ctx, tnt, id) + } + return nil, utils.ErrNotImplemented +} + +func (dbM *DataDBMock) SetIPProfileDrv(ctx *context.Context, ipp *utils.IPProfile) error { + if dbM.SetIPProfileDrvF != nil { + return dbM.SetIPProfileDrvF(ctx, ipp) + } + return utils.ErrNotImplemented +} + +func (dbM *DataDBMock) RemoveIPProfileDrv(ctx *context.Context, tnt string, id string) error { + if dbM.RemoveIPProfileDrvF != nil { + return dbM.RemoveIPProfileDrvF(ctx, tnt, id) + } + return utils.ErrNotImplemented +} + +func (dbM *DataDBMock) GetIPDrv(ctx *context.Context, tenant, id string) (*utils.IP, error) { + if dbM.GetIPDrvF != nil { + return dbM.GetIPDrvF(ctx, tenant, id) + } + return nil, utils.ErrNotImplemented +} + +func (dbM *DataDBMock) SetIPDrv(ctx *context.Context, ip *utils.IP) error { + if dbM.SetIPDrvF != nil { + return dbM.SetIPDrvF(ctx, ip) + } + return utils.ErrNotImplemented +} + +func (dbM *DataDBMock) RemoveIPDrv(ctx *context.Context, tnt, id string) error { + if dbM.RemoveIPDrvF != nil { + return dbM.RemoveIPDrvF(ctx, tnt, id) + } + return utils.ErrNotImplemented +} + func (dbM *DataDBMock) GetLoadHistory(int, bool, string) ([]*utils.LoadInstance, error) { return nil, utils.ErrNotImplemented } diff --git a/engine/datamanager.go b/engine/datamanager.go index 745b6277d..fe2bc1d94 100644 --- a/engine/datamanager.go +++ b/engine/datamanager.go @@ -34,6 +34,7 @@ var ( filterIndexesPrefixMap = utils.StringSet{ utils.AttributeFilterIndexes: {}, utils.ResourceFilterIndexes: {}, + utils.IPFilterIndexes: {}, utils.StatFilterIndexes: {}, utils.ThresholdFilterIndexes: {}, utils.RouteFilterIndexes: {}, @@ -48,6 +49,8 @@ var ( cachePrefixMap = utils.StringSet{ utils.ResourceProfilesPrefix: {}, utils.ResourcesPrefix: {}, + utils.IPProfilesPrefix: {}, + utils.IPsPrefix: {}, utils.StatQueuePrefix: {}, utils.StatQueueProfilePrefix: {}, utils.ThresholdPrefix: {}, @@ -66,6 +69,7 @@ var ( utils.ActionProfilePrefix: {}, utils.AttributeFilterIndexes: {}, utils.ResourceFilterIndexes: {}, + utils.IPFilterIndexes: {}, utils.StatFilterIndexes: {}, utils.ThresholdFilterIndexes: {}, utils.RouteFilterIndexes: {}, @@ -158,6 +162,16 @@ func (dm *DataManager) CacheDataFromDB(ctx *context.Context, prfx string, ids [] lkID := guardian.Guardian.GuardIDs("", dm.cfg.GeneralCfg().LockingTimeout, utils.ResourceLockKey(tntID.Tenant, tntID.ID)) _, err = dm.GetResource(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional) guardian.Guardian.UnguardIDs(lkID) + case utils.IPProfilesPrefix: + tntID := utils.NewTenantID(dataID) + lkID := guardian.Guardian.GuardIDs("", dm.cfg.GeneralCfg().LockingTimeout, utils.IPProfileLockKey(tntID.Tenant, tntID.ID)) + _, err = dm.GetIPProfile(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional) + guardian.Guardian.UnguardIDs(lkID) + case utils.IPsPrefix: + tntID := utils.NewTenantID(dataID) + lkID := guardian.Guardian.GuardIDs("", dm.cfg.GeneralCfg().LockingTimeout, utils.IPLockKey(tntID.Tenant, tntID.ID)) + _, err = dm.GetIP(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional) + guardian.Guardian.UnguardIDs(lkID) case utils.StatQueueProfilePrefix: tntID := utils.NewTenantID(dataID) lkID := guardian.Guardian.GuardIDs("", dm.cfg.GeneralCfg().LockingTimeout, statQueueProfileLockKey(tntID.Tenant, tntID.ID)) @@ -216,6 +230,12 @@ func (dm *DataManager) CacheDataFromDB(ctx *context.Context, prfx string, ids [] return } _, err = dm.GetIndexes(ctx, utils.CacheResourceFilterIndexes, tntCtx, idxKey, utils.NonTransactional, false, true) + case utils.IPFilterIndexes: + var tntCtx, idxKey string + if tntCtx, idxKey, err = splitFilterIndex(dataID); err != nil { + return + } + _, err = dm.GetIndexes(ctx, utils.CacheIPFilterIndexes, tntCtx, idxKey, utils.NonTransactional, false, true) case utils.StatFilterIndexes: var tntCtx, idxKey string if tntCtx, idxKey, err = splitFilterIndex(dataID); err != nil { @@ -1663,6 +1683,241 @@ func (dm *DataManager) RemoveResourceProfile(ctx *context.Context, tenant, id st return dm.RemoveResource(ctx, tenant, id) } +func (dm *DataManager) GetIP(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool, + transactionID string) (ip *utils.IP, err error) { + tntID := utils.ConcatenatedKey(tenant, id) + if cacheRead { + if x, ok := Cache.Get(utils.CacheIPs, tntID); ok { + if x == nil { + return nil, utils.ErrNotFound + } + return x.(*utils.IP), nil + } + } + if dm == nil { + err = utils.ErrNoDatabaseConn + return + } + ip, err = dm.dataDB.GetIPDrv(ctx, tenant, id) + if err != nil { + if itm := dm.cfg.DataDbCfg().Items[utils.MetaIPs]; err == utils.ErrNotFound && itm.Remote { + if err = dm.connMgr.Call(ctx, dm.cfg.DataDbCfg().RmtConns, + utils.ReplicatorSv1GetIP, + &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: tenant, ID: id}, + APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, + utils.FirstNonEmpty(dm.cfg.DataDbCfg().RmtConnID, + dm.cfg.GeneralCfg().NodeID)), + }, &ip); err == nil { + err = dm.dataDB.SetIPDrv(ctx, ip) + } + } + if err != nil { + err = utils.CastRPCErr(err) + if err == utils.ErrNotFound && cacheWrite { + if errCh := Cache.Set(ctx, utils.CacheIPs, tntID, nil, nil, + cacheCommit(transactionID), transactionID); errCh != nil { + return nil, errCh + } + + } + return nil, err + } + } + if cacheWrite { + if errCh := Cache.Set(ctx, utils.CacheIPs, tntID, ip, nil, + cacheCommit(transactionID), transactionID); errCh != nil { + return nil, errCh + } + } + return +} + +func (dm *DataManager) SetIP(ctx *context.Context, ip *utils.IP) (err error) { + if dm == nil { + return utils.ErrNoDatabaseConn + } + if err = dm.DataDB().SetIPDrv(ctx, ip); err != nil { + return + } + if itm := dm.cfg.DataDbCfg().Items[utils.MetaIPs]; itm.Replicate { + err = replicate(ctx, dm.connMgr, dm.cfg.DataDbCfg().RplConns, + dm.cfg.DataDbCfg().RplFiltered, + utils.IPsPrefix, ip.TenantID(), // this are used to get the host IDs from cache + utils.ReplicatorSv1SetIP, + &utils.IPWithAPIOpts{ + IP: ip, + APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, + dm.cfg.DataDbCfg().RplCache, utils.EmptyString)}) + } + return +} + +func (dm *DataManager) RemoveIP(ctx *context.Context, tenant, id string) (err error) { + if dm == nil { + return utils.ErrNoDatabaseConn + } + if err = dm.DataDB().RemoveIPDrv(ctx, tenant, id); err != nil { + return + } + if itm := dm.cfg.DataDbCfg().Items[utils.MetaIPs]; itm.Replicate { + replicate(ctx, dm.connMgr, dm.cfg.DataDbCfg().RplConns, + dm.cfg.DataDbCfg().RplFiltered, + utils.IPsPrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache + utils.ReplicatorSv1RemoveIP, + &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: tenant, ID: id}, + APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, + dm.cfg.DataDbCfg().RplCache, utils.EmptyString)}) + } + return +} + +func (dm *DataManager) GetIPProfile(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool, + transactionID string) (ipp *utils.IPProfile, err error) { + tntID := utils.ConcatenatedKey(tenant, id) + if cacheRead { + if x, ok := Cache.Get(utils.CacheIPProfiles, tntID); ok { + if x == nil { + return nil, utils.ErrNotFound + } + return x.(*utils.IPProfile), nil + } + } + if dm == nil { + err = utils.ErrNoDatabaseConn + return + } + ipp, err = dm.dataDB.GetIPProfileDrv(ctx, tenant, id) + if err != nil { + if itm := dm.cfg.DataDbCfg().Items[utils.MetaIPProfiles]; err == utils.ErrNotFound && itm.Remote { + if err = dm.connMgr.Call(ctx, dm.cfg.DataDbCfg().RmtConns, + utils.ReplicatorSv1GetIPProfile, &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: tenant, ID: id}, + APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, + utils.FirstNonEmpty(dm.cfg.DataDbCfg().RmtConnID, + dm.cfg.GeneralCfg().NodeID)), + }, &ipp); err == nil { + err = dm.dataDB.SetIPProfileDrv(ctx, ipp) + } + } + if err != nil { + err = utils.CastRPCErr(err) + if err == utils.ErrNotFound && cacheWrite { + if errCh := Cache.Set(ctx, utils.CacheIPProfiles, tntID, nil, nil, + cacheCommit(transactionID), transactionID); errCh != nil { + return nil, errCh + } + + } + return nil, err + } + } + if cacheWrite { + if errCh := Cache.Set(ctx, utils.CacheIPProfiles, tntID, ipp, nil, + cacheCommit(transactionID), transactionID); errCh != nil { + return nil, errCh + } + } + return +} + +func (dm *DataManager) SetIPProfile(ctx *context.Context, ipp *utils.IPProfile, withIndex bool) (err error) { + if dm == nil { + return utils.ErrNoDatabaseConn + } + if withIndex { + if err := dm.checkFilters(ctx, 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()) + } + } + oldIPP, err := dm.GetIPProfile(ctx, ipp.Tenant, ipp.ID, true, false, utils.NonTransactional) + if err != nil && err != utils.ErrNotFound { + return err + } + if err = dm.DataDB().SetIPProfileDrv(ctx, ipp); err != nil { + return err + } + if withIndex { + var oldFiltersIDs *[]string + if oldIPP != nil { + oldFiltersIDs = &oldIPP.FilterIDs + } + if err := updatedIndexes(ctx, dm, utils.CacheIPFilterIndexes, ipp.Tenant, + utils.EmptyString, ipp.ID, oldFiltersIDs, ipp.FilterIDs, false); err != nil { + return err + } + Cache.Clear([]string{utils.CacheEventIPs}) + } + if itm := dm.cfg.DataDbCfg().Items[utils.MetaIPProfiles]; itm.Replicate { + if err = replicate(ctx, dm.connMgr, dm.cfg.DataDbCfg().RplConns, + dm.cfg.DataDbCfg().RplFiltered, + utils.IPProfilesPrefix, ipp.TenantID(), // this are used to get the host IDs from cache + utils.ReplicatorSv1SetIPProfile, + &utils.IPProfileWithAPIOpts{ + IPProfile: ipp, + APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, + dm.cfg.DataDbCfg().RplCache, utils.EmptyString)}); err != nil { + return + } + } + 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.SetIP(ctx, &utils.IP{ + Tenant: ipp.Tenant, + ID: ipp.ID, + Usages: make(map[string]*utils.IPUsage), + }) + } else if _, errRs := dm.GetIP(ctx, ipp.Tenant, ipp.ID, // do not try to get the resource if the configuration changed + true, false, utils.NonTransactional); errRs == utils.ErrNotFound { // the resource does not exist + err = dm.SetIP(ctx, &utils.IP{ + Tenant: ipp.Tenant, + ID: ipp.ID, + Usages: make(map[string]*utils.IPUsage), + }) + } + return +} + +func (dm *DataManager) RemoveIPProfile(ctx *context.Context, tenant, id string, withIndex bool) (err error) { + if dm == nil { + return utils.ErrNoDatabaseConn + } + oldIPP, err := dm.GetIPProfile(ctx, tenant, id, true, false, utils.NonTransactional) + if err != nil && err != utils.ErrNotFound { + return err + } + if err = dm.DataDB().RemoveIPProfileDrv(ctx, tenant, id); err != nil { + return + } + if oldIPP == nil { + return utils.ErrNotFound + } + if withIndex { + if err = removeIndexFiltersItem(ctx, dm, utils.CacheIPFilterIndexes, tenant, id, oldIPP.FilterIDs); err != nil { + return + } + if err = removeItemFromFilterIndex(ctx, dm, utils.CacheIPFilterIndexes, + tenant, utils.EmptyString, id, oldIPP.FilterIDs); err != nil { + return + } + } + if itm := dm.cfg.DataDbCfg().Items[utils.MetaIPProfiles]; itm.Replicate { + replicate(ctx, dm.connMgr, dm.cfg.DataDbCfg().RplConns, + dm.cfg.DataDbCfg().RplFiltered, + utils.IPProfilesPrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache + utils.ReplicatorSv1RemoveIPProfile, + &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{Tenant: tenant, ID: id}, + APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, + dm.cfg.DataDbCfg().RplCache, utils.EmptyString)}) + } + return dm.RemoveIP(ctx, tenant, id) +} + func (dm *DataManager) HasData(category, subject, tenant string) (has bool, err error) { if dm == nil { err = utils.ErrNoDatabaseConn diff --git a/engine/libengine.go b/engine/libengine.go index 621c51f2f..0f63f47e9 100644 --- a/engine/libengine.go +++ b/engine/libengine.go @@ -234,6 +234,9 @@ func NewDispatcherService(val any) (_ IntService, err error) { case strings.HasPrefix(m, utils.ResourceS): m = strings.TrimPrefix(m, utils.ResourceS) key = utils.ResourceS + case strings.HasPrefix(m, utils.IPs): + m = strings.TrimPrefix(m, utils.IPs) + key = utils.IPs case strings.HasPrefix(m, utils.RouteS): m = strings.TrimPrefix(m, utils.RouteS) key = utils.RouteS diff --git a/engine/libtest.go b/engine/libtest.go index dff7d7acc..1045c759e 100644 --- a/engine/libtest.go +++ b/engine/libtest.go @@ -273,10 +273,14 @@ func GetDefaultEmptyCacheStats() map[string]*ltcache.CacheStats { utils.CacheChargerFilterIndexes: {}, utils.CacheChargerProfiles: {}, utils.CacheEventResources: {}, + utils.CacheEventIPs: {}, utils.CacheFilters: {}, utils.CacheResourceFilterIndexes: {}, utils.CacheResourceProfiles: {}, utils.CacheResources: {}, + utils.CacheIPFilterIndexes: {}, + utils.CacheIPProfiles: {}, + utils.CacheIPs: {}, utils.CacheRPCResponses: {}, utils.CacheStatFilterIndexes: {}, utils.CacheStatQueueProfiles: {}, @@ -585,6 +589,7 @@ var serviceReceivers = map[string]string{ utils.ERs: utils.ErSv1, utils.RateS: utils.RateSv1, utils.ResourceS: utils.ResourceSv1, + utils.IPs: utils.IPsV1, utils.RouteS: utils.RouteSv1, utils.SessionS: utils.SessionSv1, utils.StatS: utils.StatSv1, diff --git a/engine/storage_interface.go b/engine/storage_interface.go index 3f297d0fe..6d84af22b 100644 --- a/engine/storage_interface.go +++ b/engine/storage_interface.go @@ -46,6 +46,12 @@ type DataDB interface { GetResourceDrv(*context.Context, string, string) (*utils.Resource, error) SetResourceDrv(*context.Context, *utils.Resource) error RemoveResourceDrv(*context.Context, string, string) error + GetIPProfileDrv(*context.Context, string, string) (*utils.IPProfile, error) + SetIPProfileDrv(*context.Context, *utils.IPProfile) error + RemoveIPProfileDrv(*context.Context, string, string) error + GetIPDrv(*context.Context, string, string) (*utils.IP, error) + SetIPDrv(*context.Context, *utils.IP) error + RemoveIPDrv(*context.Context, string, string) error GetLoadHistory(int, bool, string) ([]*utils.LoadInstance, error) AddLoadHistory(*utils.LoadInstance, int, string) error GetIndexesDrv(ctx *context.Context, idxItmType, tntCtx, idxKey, transactionID string) (indexes map[string]utils.StringSet, err error) diff --git a/engine/storage_internal_datadb.go b/engine/storage_internal_datadb.go index 834633049..a36b70d43 100644 --- a/engine/storage_internal_datadb.go +++ b/engine/storage_internal_datadb.go @@ -241,6 +241,44 @@ func (iDB *InternalDB) RemoveResourceDrv(_ *context.Context, tenant, id string) return } +func (iDB *InternalDB) GetIPProfileDrv(_ *context.Context, tenant, id string) (*utils.IPProfile, error) { + if x, ok := iDB.db.Get(utils.CacheIPProfiles, utils.ConcatenatedKey(tenant, id)); ok && x != nil { + return x.(*utils.IPProfile), nil + } + return nil, utils.ErrNotFound +} + +func (iDB *InternalDB) SetIPProfileDrv(_ *context.Context, ipp *utils.IPProfile) error { + iDB.db.Set(utils.CacheIPProfiles, ipp.TenantID(), ipp, nil, + true, utils.NonTransactional) + return nil +} + +func (iDB *InternalDB) RemoveIPProfileDrv(_ *context.Context, tenant, id string) error { + iDB.db.Remove(utils.CacheIPProfiles, utils.ConcatenatedKey(tenant, id), + true, utils.NonTransactional) + return nil +} + +func (iDB *InternalDB) GetIPDrv(_ *context.Context, tenant, id string) (*utils.IP, error) { + if x, ok := iDB.db.Get(utils.CacheIPs, utils.ConcatenatedKey(tenant, id)); ok && x != nil { + return x.(*utils.IP), nil + } + return nil, utils.ErrNotFound +} + +func (iDB *InternalDB) SetIPDrv(_ *context.Context, ip *utils.IP) error { + iDB.db.Set(utils.CacheIPs, ip.TenantID(), ip, nil, + true, utils.NonTransactional) + return nil +} + +func (iDB *InternalDB) RemoveIPDrv(_ *context.Context, tenant, id string) error { + iDB.db.Remove(utils.CacheIPs, utils.ConcatenatedKey(tenant, id), + true, utils.NonTransactional) + return nil +} + func (iDB *InternalDB) GetLoadHistory(int, bool, string) ([]*utils.LoadInstance, error) { return nil, nil } diff --git a/engine/storage_mongo_datadb.go b/engine/storage_mongo_datadb.go index a038c3dc9..88f38f48e 100644 --- a/engine/storage_mongo_datadb.go +++ b/engine/storage_mongo_datadb.go @@ -57,8 +57,10 @@ const ( ColLht = "load_history" ColVer = "versions" ColRsP = "resource_profiles" + ColIPp = "ip_profiles" ColIndx = "indexes" ColRes = "resources" + ColIPs = "ips" ColSqs = "statqueues" ColSqp = "statqueue_profiles" ColTps = "threshold_profiles" @@ -299,7 +301,7 @@ func (ms *MongoStorage) ensureIndexesForCol(col string) error { // exported for switch col { case ColAct, ColApl, ColAAp, ColAtr, ColRpl, ColDst, ColRds, ColLht, ColIndx: err = ms.ensureIndex(col, true, "key") - case ColRsP, ColRes, ColSqs, ColRgp, ColTrs, ColTrd, ColSqp, ColTps, ColThs, ColRts, ColAttr, ColFlt, ColCpp, ColRpp, ColApp, ColAnp: + case ColRsP, ColRes, ColIPp, ColIPs, ColSqs, ColRgp, ColTrs, ColTrd, ColSqp, ColTps, ColThs, ColRts, ColAttr, ColFlt, ColCpp, ColRpp, ColApp, ColAnp: err = ms.ensureIndex(col, true, "tenant", "id") case ColRpf, ColShg, ColAcc: err = ms.ensureIndex(col, true, "id") @@ -323,8 +325,9 @@ func (ms *MongoStorage) EnsureIndexes(cols ...string) error { if ms.IsDataDB() { cols = []string{ ColAct, ColApl, ColAAp, ColAtr, ColRpl, ColDst, ColRds, ColLht, ColIndx, - ColRsP, ColRes, ColSqs, ColSqp, ColTps, ColThs, ColRts, ColAttr, ColFlt, - ColCpp, ColRpp, ColApp, ColRpf, ColShg, ColAcc, ColAnp, ColTrd, ColTrs, + ColRsP, ColRes, ColIPp, ColIPs, ColSqs, ColSqp, ColTps, ColThs, ColRts, + ColAttr, ColFlt, ColCpp, ColRpp, ColApp, ColRpf, ColShg, ColAcc, ColAnp, + ColTrd, ColTrs, } } else { cols = []string{utils.CDRsTBL} @@ -460,6 +463,10 @@ func (ms *MongoStorage) GetKeysForPrefix(ctx *context.Context, prefix string) (k keys, qryErr = ms.getAllKeysMatchingTenantID(sctx, ColRsP, utils.ResourceProfilesPrefix, tntID) case utils.ResourcesPrefix: keys, qryErr = ms.getAllKeysMatchingTenantID(sctx, ColRes, utils.ResourcesPrefix, tntID) + case utils.IPProfilesPrefix: + keys, qryErr = ms.getAllKeysMatchingTenantID(sctx, ColIPp, utils.IPProfilesPrefix, tntID) + case utils.IPsPrefix: + keys, qryErr = ms.getAllKeysMatchingTenantID(sctx, ColIPs, utils.IPsPrefix, tntID) case utils.StatQueuePrefix: keys, qryErr = ms.getAllKeysMatchingTenantID(sctx, ColSqs, utils.StatQueuePrefix, tntID) case utils.StatQueueProfilePrefix: @@ -494,6 +501,8 @@ func (ms *MongoStorage) GetKeysForPrefix(ctx *context.Context, prefix string) (k keys, qryErr = ms.getAllIndexKeys(sctx, utils.AttributeFilterIndexes) case utils.ResourceFilterIndexes: keys, qryErr = ms.getAllIndexKeys(sctx, utils.ResourceFilterIndexes) + case utils.IPFilterIndexes: + keys, qryErr = ms.getAllIndexKeys(sctx, utils.IPFilterIndexes) case utils.StatFilterIndexes: keys, qryErr = ms.getAllIndexKeys(sctx, utils.StatFilterIndexes) case utils.ThresholdFilterIndexes: @@ -530,6 +539,10 @@ func (ms *MongoStorage) HasDataDrv(ctx *context.Context, category, subject, tena count, err = ms.getCol(ColRes).CountDocuments(sctx, bson.M{"tenant": tenant, "id": subject}) case utils.ResourceProfilesPrefix: count, err = ms.getCol(ColRsP).CountDocuments(sctx, bson.M{"tenant": tenant, "id": subject}) + case utils.IPsPrefix: + count, err = ms.getCol(ColIPs).CountDocuments(sctx, bson.M{"tenant": tenant, "id": subject}) + case utils.IPProfilesPrefix: + count, err = ms.getCol(ColIPp).CountDocuments(sctx, bson.M{"tenant": tenant, "id": subject}) case utils.StatQueuePrefix: count, err = ms.getCol(ColSqs).CountDocuments(sctx, bson.M{"tenant": tenant, "id": subject}) case utils.StatQueueProfilePrefix: @@ -739,6 +752,72 @@ func (ms *MongoStorage) RemoveResourceDrv(ctx *context.Context, tenant, id strin }) } +func (ms *MongoStorage) GetIPProfileDrv(ctx *context.Context, tenant, id string) (*utils.IPProfile, error) { + ipp := new(utils.IPProfile) + err := ms.query(ctx, func(sctx mongo.SessionContext) error { + sr := ms.getCol(ColIPp).FindOne(sctx, bson.M{"tenant": tenant, "id": id}) + decodeErr := sr.Decode(ipp) + if errors.Is(decodeErr, mongo.ErrNoDocuments) { + return utils.ErrNotFound + } + return decodeErr + }) + return ipp, err +} + +func (ms *MongoStorage) SetIPProfileDrv(ctx *context.Context, ipp *utils.IPProfile) error { + return ms.query(ctx, func(sctx mongo.SessionContext) error { + _, err := ms.getCol(ColIPp).UpdateOne(sctx, bson.M{"tenant": ipp.Tenant, "id": ipp.ID}, + bson.M{"$set": ipp}, + options.Update().SetUpsert(true), + ) + return err + }) +} + +func (ms *MongoStorage) RemoveIPProfileDrv(ctx *context.Context, tenant, id string) error { + return ms.query(ctx, func(sctx mongo.SessionContext) error { + dr, err := ms.getCol(ColIPp).DeleteOne(sctx, bson.M{"tenant": tenant, "id": id}) + if dr.DeletedCount == 0 { + return utils.ErrNotFound + } + return err + }) +} + +func (ms *MongoStorage) GetIPDrv(ctx *context.Context, tenant, id string) (*utils.IP, error) { + ip := new(utils.IP) + err := ms.query(ctx, func(sctx mongo.SessionContext) error { + sr := ms.getCol(ColIPs).FindOne(sctx, bson.M{"tenant": tenant, "id": id}) + decodeErr := sr.Decode(ip) + if errors.Is(decodeErr, mongo.ErrNoDocuments) { + return utils.ErrNotFound + } + return decodeErr + }) + return ip, err +} + +func (ms *MongoStorage) SetIPDrv(ctx *context.Context, ip *utils.IP) error { + return ms.query(ctx, func(sctx mongo.SessionContext) error { + _, err := ms.getCol(ColIPs).UpdateOne(sctx, bson.M{"tenant": ip.Tenant, "id": ip.ID}, + bson.M{"$set": ip}, + options.Update().SetUpsert(true), + ) + return err + }) +} + +func (ms *MongoStorage) RemoveIPDrv(ctx *context.Context, tenant, id string) error { + return ms.query(ctx, func(sctx mongo.SessionContext) error { + dr, err := ms.getCol(ColIPs).DeleteOne(sctx, bson.M{"tenant": tenant, "id": id}) + if dr.DeletedCount == 0 { + return utils.ErrNotFound + } + return err + }) +} + func (ms *MongoStorage) GetRankingProfileDrv(ctx *context.Context, tenant, id string) (*utils.RankingProfile, error) { rgProfile := new(utils.RankingProfile) err := ms.query(ctx, func(sctx mongo.SessionContext) error { diff --git a/engine/storage_redis.go b/engine/storage_redis.go index 4a9e8074b..5dcf0366f 100644 --- a/engine/storage_redis.go +++ b/engine/storage_redis.go @@ -420,6 +420,60 @@ func (rs *RedisStorage) RemoveResourceDrv(ctx *context.Context, tenant, id strin return rs.Cmd(nil, redisDEL, utils.ResourcesPrefix+utils.ConcatenatedKey(tenant, id)) } +func (rs *RedisStorage) GetIPProfileDrv(ctx *context.Context, tenant, id string) (*utils.IPProfile, error) { + var values []byte + if err := rs.Cmd(&values, redisGET, utils.IPProfilesPrefix+utils.ConcatenatedKey(tenant, id)); err != nil { + return nil, err + } + if len(values) == 0 { + return nil, utils.ErrNotFound + } + var ipp *utils.IPProfile + if err := rs.ms.Unmarshal(values, &ipp); err != nil { + return nil, err + } + return ipp, nil +} + +func (rs *RedisStorage) SetIPProfileDrv(ctx *context.Context, ipp *utils.IPProfile) error { + result, err := rs.ms.Marshal(ipp) + if err != nil { + return err + } + return rs.Cmd(nil, redisSET, utils.IPProfilesPrefix+ipp.TenantID(), string(result)) +} + +func (rs *RedisStorage) RemoveIPProfileDrv(ctx *context.Context, tenant, id string) error { + return rs.Cmd(nil, redisDEL, utils.IPProfilesPrefix+utils.ConcatenatedKey(tenant, id)) +} + +func (rs *RedisStorage) GetIPDrv(ctx *context.Context, tenant, id string) (*utils.IP, error) { + var values []byte + if err := rs.Cmd(&values, redisGET, utils.IPsPrefix+utils.ConcatenatedKey(tenant, id)); err != nil { + return nil, err + } + if len(values) == 0 { + return nil, utils.ErrNotFound + } + var ip *utils.IP + if err := rs.ms.Unmarshal(values, &ip); err != nil { + return nil, err + } + return ip, nil +} + +func (rs *RedisStorage) SetIPDrv(ctx *context.Context, ip *utils.IP) error { + result, err := rs.ms.Marshal(ip) + if err != nil { + return err + } + return rs.Cmd(nil, redisSET, utils.IPsPrefix+ip.TenantID(), string(result)) +} + +func (rs *RedisStorage) RemoveIPDrv(ctx *context.Context, tenant, id string) error { + return rs.Cmd(nil, redisDEL, utils.IPsPrefix+utils.ConcatenatedKey(tenant, id)) +} + func (rs *RedisStorage) GetVersions(itm string) (vrs Versions, err error) { if itm != "" { var fldVal int64 diff --git a/flaky_test.sh b/flaky_test.sh index 05131a39a..0a4a49c63 100755 --- a/flaky_test.sh +++ b/flaky_test.sh @@ -8,7 +8,7 @@ # Example: # ./flaky_tests.sh -dbtype=*mysql -rpc=*gob -packages=("agents" "apis" "attributes" "chargers" "cmd/cgr-console" "cmd/cgr-loader" "efs" "engine" "ers" "general_tests" "loaders" "rankings" "registrarc" "resources" "routes" "sessions" "trends") +packages=("agents" "apis" "attributes" "chargers" "cmd/cgr-console" "cmd/cgr-loader" "efs" "engine" "ers" "general_tests" "ips" "loaders" "rankings" "registrarc" "resources" "routes" "sessions" "trends") dbtypes=("*internal" "*mysql" "*mongo" "*postgres") # Tests that are independent of the dbtype flag and run only once diff --git a/general_tests/offline_internal_it_test.go b/general_tests/offline_internal_it_test.go index 4bb72ba87..48bcac667 100644 --- a/general_tests/offline_internal_it_test.go +++ b/general_tests/offline_internal_it_test.go @@ -319,12 +319,12 @@ func TestOfflineInternal(t *testing.T) { // run with sudo return nil }); err != nil { t.Error(err) - } else if dirs != 33 { - t.Errorf("expected <%d> directories, received <%d>", 33, dirs) - } else if i > 6 && files != 30 { + } else if dirs != 36 { + t.Errorf("expected <%d> directories, received <%d>", 36, dirs) + } else if i > 6 && files != 31 { + t.Errorf("expected <%d> files, received <%d>", 31, files) + } else if i < 6 && files != 30 { t.Errorf("expected <%d> files, received <%d>", 30, files) - } else if i < 6 && files != 29 { - t.Errorf("expected <%d> files, received <%d>", 29, files) } }) diff --git a/general_tests/service_toggle_it_test.go b/general_tests/service_toggle_it_test.go index aa04a259b..abfc18e24 100644 --- a/general_tests/service_toggle_it_test.go +++ b/general_tests/service_toggle_it_test.go @@ -186,6 +186,7 @@ func checkServiceStates(t *testing.T, client *birpc.Client, want string) { utils.ERs, utils.RateS, utils.ResourceS, + utils.IPs, utils.RouteS, utils.SessionS, utils.StatS, diff --git a/general_tests/tut_smgeneric_it_test.go b/general_tests/tut_smgeneric_it_test.go index c5f4253a2..976531e58 100644 --- a/general_tests/tut_smgeneric_it_test.go +++ b/general_tests/tut_smgeneric_it_test.go @@ -149,6 +149,8 @@ func testTutSMGCacheStats(t *testing.T) { expectedStats[utils.CacheActionProfiles].Items = 1 expectedStats[utils.CacheResourceProfiles].Items = 1 expectedStats[utils.CacheResources].Items = 1 + expectedStats[utils.CacheIPProfiles].Items = 1 + expectedStats[utils.CacheIPs].Items = 1 expectedStats[utils.CacheStatQueues].Items = 7 expectedStats[utils.CacheStatQueueProfiles].Items = 7 expectedStats[utils.CacheThresholds].Items = 1 @@ -157,7 +159,7 @@ func testTutSMGCacheStats(t *testing.T) { expectedStats[utils.CacheRouteProfiles].Items = 12 expectedStats[utils.CacheAttributeProfiles].Items = 8 expectedStats[utils.MetaDefault].Items = 0 - expectedStats[utils.CacheLoadIDs].Items = 28 + expectedStats[utils.CacheLoadIDs].Items = 31 expectedStats[utils.CacheChargerProfiles].Items = 3 expectedStats[utils.CacheRPCConnections].Items = 1 expectedStats[utils.CacheThresholdFilterIndexes].Items = 1 @@ -168,6 +170,8 @@ func testTutSMGCacheStats(t *testing.T) { expectedStats[utils.CacheRouteFilterIndexes].Groups = 1 expectedStats[utils.CacheResourceFilterIndexes].Items = 1 expectedStats[utils.CacheResourceFilterIndexes].Groups = 1 + expectedStats[utils.CacheIPFilterIndexes].Items = 1 + expectedStats[utils.CacheIPFilterIndexes].Groups = 1 expectedStats[utils.CacheChargerFilterIndexes].Items = 1 expectedStats[utils.CacheChargerFilterIndexes].Groups = 1 expectedStats[utils.CacheRankingProfiles].Items = 2 diff --git a/integration_test.sh b/integration_test.sh index 3d41659c6..f7fa570a8 100755 --- a/integration_test.sh +++ b/integration_test.sh @@ -8,7 +8,7 @@ # Example: # ./integration_test.sh -dbtype=*mysql -rpc=*gob -packages=("agents" "apis" "attributes" "chargers" "cmd/cgr-console" "cmd/cgr-loader" "efs" "engine" "ers" "general_tests" "loaders" "rankings" "registrarc" "resources" "routes" "sessions" "trends") +packages=("agents" "apis" "attributes" "chargers" "cmd/cgr-console" "cmd/cgr-loader" "efs" "engine" "ers" "general_tests" "ips" "loaders" "rankings" "registrarc" "resources" "routes" "sessions" "trends") dbtypes=("*internal" "*mysql" "*mongo" "*postgres") # Tests that are independent of the dbtype flag and run only once diff --git a/ips/apis.go b/ips/apis.go new file mode 100644 index 000000000..7aeef9606 --- /dev/null +++ b/ips/apis.go @@ -0,0 +1,327 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package ips + +import ( + "time" + + "github.com/cgrates/birpc/context" + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" + "github.com/cgrates/guardian" +) + +// V1GetIPsForEvent returns active IPs 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 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + + var usageID string + if usageID, err = engine.GetStringOpts(ctx, args.Tenant, args.AsDataProvider(), nil, s.fltrs, s.cfg.IPsCfg().Opts.UsageID, + utils.OptsIPsUsageID); err != nil { + return + } + + var ttl time.Duration + if ttl, err = engine.GetDurationOpts(ctx, args.Tenant, args.AsDataProvider(), nil, s.fltrs, s.cfg.IPsCfg().Opts.TTL, + utils.OptsIPsTTL); err != nil { + return + } + usageTTL := utils.DurationPointer(ttl) + + 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 := engine.Cache.Get(utils.CacheRPCResponses, cacheKey); has { + cachedResp := itm.(*utils.CachedRPCResponse) + if cachedResp.Error == nil { + *reply = *cachedResp.Result.(*IPs) + } + return cachedResp.Error + } + defer engine.Cache.Set(ctx, utils.CacheRPCResponses, cacheKey, + &utils.CachedRPCResponse{Result: reply, Error: err}, + nil, true, utils.NonTransactional) + } + // end of RPC caching + + var mtcRLs IPs + if mtcRLs, err = s.matchingIPsForEvent(ctx, tnt, args, usageID, usageTTL); err != nil { + return err + } + *reply = mtcRLs + mtcRLs.unlock() + return +} + +// V1AuthorizeIPs queries service to find if an Usage is allowed +func (s *IPService) V1AuthorizeIPs(ctx *context.Context, args *utils.CGREvent, reply *string) (err error) { + if args == nil { + return utils.NewErrMandatoryIeMissing(utils.Event) + } + if missing := utils.MissingStructFields(args, []string{utils.ID, utils.Event}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + + var usageID string + if usageID, err = engine.GetStringOpts(ctx, args.Tenant, args.AsDataProvider(), nil, s.fltrs, s.cfg.IPsCfg().Opts.UsageID, + utils.OptsIPsUsageID); err != nil { + return + } + + if _, err = engine.GetFloat64Opts(ctx, args.Tenant, args.AsDataProvider(), nil, s.fltrs, s.cfg.IPsCfg().Opts.Units, + utils.OptsIPsUnits); err != nil { + return + } + + var ttl time.Duration + if ttl, err = engine.GetDurationOpts(ctx, args.Tenant, args.AsDataProvider(), nil, s.fltrs, s.cfg.IPsCfg().Opts.TTL, + utils.OptsIPsTTL); err != nil { + return + } + usageTTL := utils.DurationPointer(ttl) + + if usageID == utils.EmptyString { + return utils.NewErrMandatoryIeMissing(utils.UsageID) + } + + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = s.cfg.GeneralCfg().DefaultTenant + } + + // RPC caching + if config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses].Limit != 0 { + cacheKey := utils.ConcatenatedKey(utils.IPsV1AuthorizeIPs, utils.ConcatenatedKey(tnt, args.ID)) + refID := guardian.Guardian.GuardIDs("", + config.CgrConfig().GeneralCfg().LockingTimeout, cacheKey) // RPC caching needs to be atomic + defer guardian.Guardian.UnguardIDs(refID) + if itm, has := engine.Cache.Get(utils.CacheRPCResponses, cacheKey); has { + cachedResp := itm.(*utils.CachedRPCResponse) + if cachedResp.Error == nil { + *reply = *cachedResp.Result.(*string) + } + return cachedResp.Error + } + defer engine.Cache.Set(ctx, utils.CacheRPCResponses, cacheKey, + &utils.CachedRPCResponse{Result: reply, Error: err}, + nil, true, utils.NonTransactional) + } + // end of RPC caching + + var mtcRLs IPs + if mtcRLs, err = s.matchingIPsForEvent(ctx, tnt, args, usageID, usageTTL); err != nil { + return err + } + defer mtcRLs.unlock() + + /* + authorize logic + ... + */ + + *reply = utils.OK + return +} + +// V1AllocateIPs is called when an IP requires allocation. +func (s *IPService) V1AllocateIPs(ctx *context.Context, args *utils.CGREvent, reply *string) (err error) { + if args == nil { + return utils.NewErrMandatoryIeMissing(utils.Event) + } + if missing := utils.MissingStructFields(args, []string{utils.ID, utils.Event}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + + var usageID string + if usageID, err = engine.GetStringOpts(ctx, args.Tenant, args.AsDataProvider(), nil, s.fltrs, s.cfg.IPsCfg().Opts.UsageID, + utils.OptsIPsUsageID); err != nil { + return + } + + if _, err = engine.GetFloat64Opts(ctx, args.Tenant, args.AsDataProvider(), nil, s.fltrs, s.cfg.IPsCfg().Opts.Units, + utils.OptsIPsUnits); err != nil { + return + } + + var ttl time.Duration + if ttl, err = engine.GetDurationOpts(ctx, args.Tenant, args.AsDataProvider(), nil, s.fltrs, s.cfg.IPsCfg().Opts.TTL, + utils.OptsIPsTTL); err != nil { + return + } + usageTTL := utils.DurationPointer(ttl) + + if usageID == utils.EmptyString { + return utils.NewErrMandatoryIeMissing(utils.UsageID) + } + + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = s.cfg.GeneralCfg().DefaultTenant + } + + // RPC caching + if config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses].Limit != 0 { + cacheKey := utils.ConcatenatedKey(utils.IPsV1AllocateIPs, utils.ConcatenatedKey(tnt, args.ID)) + refID := guardian.Guardian.GuardIDs("", + config.CgrConfig().GeneralCfg().LockingTimeout, cacheKey) // RPC caching needs to be atomic + defer guardian.Guardian.UnguardIDs(refID) + if itm, has := engine.Cache.Get(utils.CacheRPCResponses, cacheKey); has { + cachedResp := itm.(*utils.CachedRPCResponse) + if cachedResp.Error == nil { + *reply = *cachedResp.Result.(*string) + } + return cachedResp.Error + } + defer engine.Cache.Set(ctx, utils.CacheRPCResponses, cacheKey, + &utils.CachedRPCResponse{Result: reply, Error: err}, + nil, true, utils.NonTransactional) + } + // end of RPC caching + + var mtcRLs IPs + if mtcRLs, err = s.matchingIPsForEvent(ctx, tnt, args, usageID, + usageTTL); err != nil { + return err + } + defer mtcRLs.unlock() + + /* + allocate logic + ... + */ + + // index it for storing + if err = s.storeMatchedIPs(ctx, mtcRLs); err != nil { + return + } + *reply = utils.OK + return +} + +// V1ReleaseIPs is called when we need to clear an allocation +func (s *IPService) V1ReleaseIPs(ctx *context.Context, args *utils.CGREvent, reply *string) (err error) { + if args == nil { + return utils.NewErrMandatoryIeMissing(utils.Event) + } + if missing := utils.MissingStructFields(args, []string{utils.ID, utils.Event}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + + var usageID string + if usageID, err = engine.GetStringOpts(ctx, args.Tenant, args.AsDataProvider(), nil, s.fltrs, s.cfg.IPsCfg().Opts.UsageID, + utils.OptsIPsUsageID); err != nil { + return + } + + var ttl time.Duration + if ttl, err = engine.GetDurationOpts(ctx, args.Tenant, args.AsDataProvider(), nil, s.fltrs, s.cfg.IPsCfg().Opts.TTL, + utils.OptsIPsTTL); err != nil { + return + } + usageTTL := utils.DurationPointer(ttl) + + if usageID == utils.EmptyString { + return utils.NewErrMandatoryIeMissing(utils.UsageID) + } + + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = s.cfg.GeneralCfg().DefaultTenant + } + + // RPC caching + if config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses].Limit != 0 { + cacheKey := utils.ConcatenatedKey(utils.IPsV1ReleaseIPs, utils.ConcatenatedKey(tnt, args.ID)) + refID := guardian.Guardian.GuardIDs("", + config.CgrConfig().GeneralCfg().LockingTimeout, cacheKey) // RPC caching needs to be atomic + defer guardian.Guardian.UnguardIDs(refID) + if itm, has := engine.Cache.Get(utils.CacheRPCResponses, cacheKey); has { + cachedResp := itm.(*utils.CachedRPCResponse) + if cachedResp.Error == nil { + *reply = *cachedResp.Result.(*string) + } + return cachedResp.Error + } + defer engine.Cache.Set(ctx, utils.CacheRPCResponses, cacheKey, + &utils.CachedRPCResponse{Result: reply, Error: err}, + nil, true, utils.NonTransactional) + } + // end of RPC caching + + var mtcRLs IPs + if mtcRLs, err = s.matchingIPsForEvent(ctx, tnt, args, usageID, + usageTTL); err != nil { + return + } + defer mtcRLs.unlock() + + /* + release logic + ... + */ + + // Handle storing + if err = s.storeMatchedIPs(ctx, mtcRLs); err != nil { + return + } + + *reply = utils.OK + return +} + +// V1GetIP returns a resource configuration +func (s *IPService) V1GetIP(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *utils.IP) error { + if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + tnt := arg.Tenant + if tnt == utils.EmptyString { + tnt = s.cfg.GeneralCfg().DefaultTenant + } + + // make sure resource is locked at process level + lkID := guardian.Guardian.GuardIDs(utils.EmptyString, + config.CgrConfig().GeneralCfg().LockingTimeout, + utils.IPLockKey(tnt, arg.ID)) + defer guardian.Guardian.UnguardIDs(lkID) + + ip, err := s.dm.GetIP(ctx, tnt, arg.ID, true, true, utils.NonTransactional) + if err != nil { + return err + } + *reply = *ip + return nil +} diff --git a/ips/ips.go b/ips/ips.go new file mode 100644 index 000000000..c6b462877 --- /dev/null +++ b/ips/ips.go @@ -0,0 +1,475 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package ips + +import ( + "cmp" + "fmt" + "runtime" + "slices" + "sync" + "time" + + "github.com/cgrates/birpc/context" + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" + "github.com/cgrates/guardian" +) + +// ipProfile represents the user configuration for the ip +type ipProfile struct { + IPProfile *utils.IPProfile + lkID string // holds the reference towards guardian lock key + +} + +// lock will lock the ipProfile using guardian and store the lock within r.lkID +// if lkID is passed as argument, the lock is considered as executed +func (ip *ipProfile) lock(lkID string) { + if lkID == utils.EmptyString { + lkID = guardian.Guardian.GuardIDs("", + config.CgrConfig().GeneralCfg().LockingTimeout, + utils.IPProfileLockKey(ip.IPProfile.Tenant, ip.IPProfile.ID)) + } + ip.lkID = lkID +} + +// unlock will unlock the ipProfile and clear rp.lkID +func (ip *ipProfile) unlock() { + if ip.lkID == utils.EmptyString { + return + } + guardian.Guardian.UnguardIDs(ip.lkID) + ip.lkID = utils.EmptyString +} + +// isLocked returns the locks status of this ipProfile +func (ip *ipProfile) isLocked() bool { + return ip.lkID != utils.EmptyString +} + +// ip represents an ip in the system +// not thread safe, needs locking at process level +type ip struct { + IP *utils.IP + lkID string // ID of the lock used when matching the ip + ttl *time.Duration // time to leave for this ip, picked up on each IP initialization out of config + tUsage *float64 // sum of all usages + dirty *bool // the usages were modified, needs save, *bool so we only save if enabled in config + rPrf *ipProfile // for ordering purposes +} + +// 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, + utils.IPLockKey(ip.IP.Tenant, ip.IP.ID)) + } + ip.lkID = lkID +} + +// unlock will unlock the ip and clear r.lkID +func (ip *ip) unlock() { + if ip.lkID == utils.EmptyString { + return + } + guardian.Guardian.UnguardIDs(ip.lkID) + ip.lkID = utils.EmptyString +} + +// isLocked returns the locks status of this ip +func (ip *ip) isLocked() bool { + return ip.lkID != utils.EmptyString +} + +// removeExpiredUnits removes units which are expired from the ip +func (ip *ip) removeExpiredUnits() { + var firstActive int + for _, usageID := range ip.IP.TTLIdx { + if u, has := ip.IP.Usages[usageID]; has && u.IsActive(time.Now()) { + break + } + firstActive++ + } + if firstActive == 0 { + return + } + for _, uID := range ip.IP.TTLIdx[:firstActive] { + usage, has := ip.IP.Usages[uID] + if !has { + continue + } + delete(ip.IP.Usages, uID) + if ip.tUsage != nil { // total usage was not yet calculated so we do not need to update it + *ip.tUsage -= usage.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.IP.ID, *ip.tUsage)) + ip.tUsage = nil + } + } + } + ip.IP.TTLIdx = ip.IP.TTLIdx[firstActive:] + ip.tUsage = nil +} + +// recordUsage records a new usage +func (ip *ip) recordUsage(usage *utils.IPUsage) error { + if _, has := ip.IP.Usages[usage.ID]; has { + return fmt.Errorf("duplicate ip usage with id: %s", usage.TenantID()) + } + if ip.ttl != nil && *ip.ttl != -1 { + if *ip.ttl == 0 { + return nil // no recording for ttl of 0 + } + usage = usage.Clone() // don't influence the initial ru + usage.ExpiryTime = time.Now().Add(*ip.ttl) + } + ip.IP.Usages[usage.ID] = usage + if ip.tUsage != nil { + *ip.tUsage += usage.Units + } + if !usage.ExpiryTime.IsZero() { + ip.IP.TTLIdx = append(ip.IP.TTLIdx, usage.ID) + } + return nil +} + +// clearUsage clears the usage for an ID +func (ip *ip) clearUsage(usageID string) error { + usage, has := ip.IP.Usages[usageID] + if !has { + return fmt.Errorf("cannot find usage record with id: %s", usageID) + } + if !usage.ExpiryTime.IsZero() { + for i, uIDIdx := range ip.IP.TTLIdx { + if uIDIdx == usageID { + ip.IP.TTLIdx = slices.Delete(ip.IP.TTLIdx, i, i+1) + break + } + } + } + if ip.tUsage != nil { + *ip.tUsage -= usage.Units + } + delete(ip.IP.Usages, usageID) + return nil +} + +// IPs is a collection of IP objects. +type IPs []*ip + +// unlock will unlock ips part of this slice +func (ips IPs) unlock() { + for _, ip := range ips { + ip.unlock() + if ip.rPrf != nil { + ip.rPrf.unlock() + } + } +} + +// ids returns a map of ip IDs which is used for caching +func (ips IPs) ids() utils.StringSet { + ids := make(utils.StringSet) + for _, ip := range ips { + ids.Add(ip.IP.ID) + } + return ids +} + +// NewIPService returns a new IPService +func NewIPService(dm *engine.DataManager, cfg *config.CGRConfig, + fltrs *engine.FilterS, cm *engine.ConnManager) *IPService { + return &IPService{dm: dm, + storedIPs: make(utils.StringSet), + cfg: cfg, + cm: cm, + fltrs: fltrs, + loopStopped: make(chan struct{}), + stopBackup: make(chan struct{}), + } + +} + +// IPService is the service handling resources +type IPService struct { + dm *engine.DataManager // So we can load the data in cache and index it + fltrs *engine.FilterS + storedIPsMux sync.RWMutex // protects storedIPs + storedIPs utils.StringSet // keep a record of resources which need saving, map[resID]bool + cfg *config.CGRConfig + stopBackup chan struct{} // control storing process + loopStopped chan struct{} + cm *engine.ConnManager +} + +// Reload stops the backupLoop and restarts it +func (s *IPService) Reload(ctx *context.Context) { + close(s.stopBackup) + <-s.loopStopped // wait until the loop is done + s.stopBackup = make(chan struct{}) + go s.runBackup(ctx) +} + +// StartLoop starts the gorutine with the backup loop +func (s *IPService) StartLoop(ctx *context.Context) { + go s.runBackup(ctx) +} + +// Shutdown is called to shutdown the service +func (s *IPService) Shutdown(ctx *context.Context) { + close(s.stopBackup) + s.storeIPs(ctx) +} + +// backup will regularly store resources changed to dataDB +func (s *IPService) runBackup(ctx *context.Context) { + storeInterval := s.cfg.IPsCfg().StoreInterval + if storeInterval <= 0 { + s.loopStopped <- struct{}{} + return + } + for { + s.storeIPs(ctx) + select { + case <-s.stopBackup: + s.loopStopped <- struct{}{} + return + case <-time.After(storeInterval): + } + } +} + +// storeIPs represents one task of complete backup +func (s *IPService) storeIPs(ctx *context.Context) { + var failedRIDs []string + for { // don't stop until we store all dirty resources + s.storedIPsMux.Lock() + rID := s.storedIPs.GetOne() + if rID != "" { + s.storedIPs.Remove(rID) + } + s.storedIPsMux.Unlock() + if rID == "" { + break // no more keys, backup completed + } + rIf, ok := engine.Cache.Get(utils.CacheIPs, rID) + if !ok || rIf == nil { + utils.Logger.Warning(fmt.Sprintf("<%s> failed retrieving from cache resource with ID: %s", utils.IPs, rID)) + continue + } + r := &ip{ + IP: rIf.(*utils.IP), + + // NOTE: dirty is hardcoded to true, otherwise resources would + // never be stored. + // Previously, dirty was part of the cached resource. + dirty: utils.BoolPointer(true), + } + r.lock(utils.EmptyString) + if err := s.storeIP(ctx, r); err != nil { + failedRIDs = append(failedRIDs, rID) // record failure so we can schedule it for next backup + } + r.unlock() + // randomize the CPU load and give up thread control + runtime.Gosched() + } + if len(failedRIDs) != 0 { // there were errors on save, schedule the keys for next backup + s.storedIPsMux.Lock() + s.storedIPs.AddSlice(failedRIDs) + s.storedIPsMux.Unlock() + } +} + +// StoreIP stores the resource in DB and corrects dirty flag +func (s *IPService) storeIP(ctx *context.Context, r *ip) (err error) { + if r.dirty == nil || !*r.dirty { + return + } + if err = s.dm.SetIP(ctx, r.IP); err != nil { + utils.Logger.Warning( + fmt.Sprintf(" failed saving IP with ID: %s, error: %s", + r.IP.ID, err.Error())) + return + } + //since we no longer handle cache in DataManager do here a manual caching + if tntID := r.IP.TenantID(); engine.Cache.HasItem(utils.CacheIPs, tntID) { // only cache if previously there + if err = engine.Cache.Set(ctx, utils.CacheIPs, tntID, r.IP, nil, + true, utils.NonTransactional); err != nil { + utils.Logger.Warning( + fmt.Sprintf(" failed caching IP with ID: %s, error: %s", + tntID, err.Error())) + return + } + } + *r.dirty = false + return +} + +// storeMatchedIPs will store the list of resources based on the StoreInterval +func (s *IPService) storeMatchedIPs(ctx *context.Context, mtcRLs IPs) (err error) { + if s.cfg.IPsCfg().StoreInterval == 0 { + return + } + if s.cfg.IPsCfg().StoreInterval > 0 { + s.storedIPsMux.Lock() + defer s.storedIPsMux.Unlock() + } + for _, r := range mtcRLs { + if r.dirty != nil { + *r.dirty = true // mark it to be saved + if s.cfg.IPsCfg().StoreInterval > 0 { + s.storedIPs.Add(r.IP.TenantID()) + continue + } + if err = s.storeIP(ctx, r); err != nil { + return + } + } + + } + return +} + +// matchingIPsForEvent returns ordered list of matching resources which are active by the time of the call +func (s *IPService) matchingIPsForEvent(ctx *context.Context, tnt string, ev *utils.CGREvent, + evUUID string, ttl *time.Duration) (ips IPs, err error) { + var rIDs utils.StringSet + evNm := utils.MapStorage{ + utils.MetaReq: ev.Event, + utils.MetaOpts: ev.APIOpts, + } + if x, ok := engine.Cache.Get(utils.CacheEventIPs, evUUID); ok { // The IPIDs were cached as utils.StringSet{"resID":bool} + if x == nil { + return nil, utils.ErrNotFound + } + rIDs = x.(utils.StringSet) + 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 := engine.Cache.Remove(ctx, utils.CacheEventIPs, evUUID, + true, utils.NonTransactional); errCh != nil { + err = errCh + } + } + }() + + } else { // select the resourceIDs out of dataDB + rIDs, err = engine.MatchingItemIDsForEvent(ctx, evNm, + s.cfg.IPsCfg().StringIndexedFields, + s.cfg.IPsCfg().PrefixIndexedFields, + s.cfg.IPsCfg().SuffixIndexedFields, + s.cfg.IPsCfg().ExistsIndexedFields, + s.cfg.IPsCfg().NotExistsIndexedFields, + s.dm, utils.CacheIPFilterIndexes, tnt, + s.cfg.IPsCfg().IndexedSelects, + s.cfg.IPsCfg().NestedFields, + ) + if err != nil { + if err == utils.ErrNotFound { + if errCh := engine.Cache.Set(ctx, utils.CacheEventIPs, evUUID, nil, nil, true, ""); errCh != nil { // cache negative match + return nil, errCh + } + } + return + } + } + ips = make(IPs, 0, len(rIDs)) + weights := make(map[string]float64) // stores sorting weights by resource ID + for resName := range rIDs { + lkPrflID := guardian.Guardian.GuardIDs("", + config.CgrConfig().GeneralCfg().LockingTimeout, + utils.IPProfileLockKey(tnt, resName)) + var rp *utils.IPProfile + if rp, err = s.dm.GetIPProfile(ctx, tnt, resName, + true, true, utils.NonTransactional); err != nil { + guardian.Guardian.UnguardIDs(lkPrflID) + if err == utils.ErrNotFound { + continue + } + ips.unlock() + return + } + rPrf := &ipProfile{ + IPProfile: rp, + } + rPrf.lock(lkPrflID) + var pass bool + if pass, err = s.fltrs.Pass(ctx, tnt, rPrf.IPProfile.FilterIDs, + evNm); err != nil { + rPrf.unlock() + ips.unlock() + return nil, err + } else if !pass { + rPrf.unlock() + continue + } + lkID := guardian.Guardian.GuardIDs(utils.EmptyString, + config.CgrConfig().GeneralCfg().LockingTimeout, + utils.IPLockKey(rPrf.IPProfile.Tenant, rPrf.IPProfile.ID)) + var res *utils.IP + if res, err = s.dm.GetIP(ctx, rPrf.IPProfile.Tenant, rPrf.IPProfile.ID, true, true, ""); err != nil { + guardian.Guardian.UnguardIDs(lkID) + rPrf.unlock() + ips.unlock() + return nil, err + } + r := &ip{ + IP: res, + } + r.lock(lkID) // pass the lock into resource so we have it as reference + if rPrf.IPProfile.Stored && r.dirty == nil { + r.dirty = utils.BoolPointer(false) + } + if ttl != nil { + if *ttl != 0 { + r.ttl = ttl + } + } else if rPrf.IPProfile.TTL >= 0 { + r.ttl = utils.DurationPointer(rPrf.IPProfile.TTL) + } + r.rPrf = rPrf + weight, err := engine.WeightFromDynamics(ctx, rPrf.IPProfile.Weights, s.fltrs, tnt, evNm) + if err != nil { + return nil, err + } + weights[r.IP.ID] = weight + ips = append(ips, r) + } + + if len(ips) == 0 { + return nil, utils.ErrNotFound + } + + // Sort by weight (higher values first). + slices.SortFunc(ips, func(a, b *ip) int { + return cmp.Compare(weights[b.IP.ID], weights[a.IP.ID]) + }) + + if err = engine.Cache.Set(ctx, utils.CacheEventIPs, evUUID, ips.ids(), nil, true, ""); err != nil { + ips.unlock() + } + return +} diff --git a/loaders/libloader.go b/loaders/libloader.go index 61eee2447..0172b6c9e 100644 --- a/loaders/libloader.go +++ b/loaders/libloader.go @@ -270,6 +270,10 @@ func newProfileFunc(lType string) func() profile { return func() profile { return new(utils.ResourceProfile) } + case utils.MetaIPs: + return func() profile { + return new(utils.IPProfile) + } case utils.MetaFilters: return func() profile { return new(engine.Filter) diff --git a/loaders/loader.go b/loaders/loader.go index 3ac5dc8f4..8c0008405 100644 --- a/loaders/loader.go +++ b/loaders/loader.go @@ -45,6 +45,8 @@ func removeFromDB(ctx *context.Context, dm *engine.DataManager, lType string, wi return dm.RemoveAttributeProfile(ctx, tnt, id, withIndex) case utils.MetaResources: return dm.RemoveResourceProfile(ctx, tnt, id, withIndex) + case utils.MetaIPs: + return dm.RemoveIPProfile(ctx, tnt, id, withIndex) case utils.MetaFilters: return dm.RemoveFilter(ctx, tnt, id, withIndex) case utils.MetaStats: @@ -83,6 +85,8 @@ func setToDB(ctx *context.Context, dm *engine.DataManager, lType string, data pr return dm.SetAttributeProfile(ctx, data.(*utils.AttributeProfile), withIndex) case utils.MetaResources: return dm.SetResourceProfile(ctx, data.(*utils.ResourceProfile), withIndex) + case utils.MetaIPs: + return dm.SetIPProfile(ctx, data.(*utils.IPProfile), withIndex) case utils.MetaFilters: fltr := data.(*engine.Filter) fltr.Compress() @@ -124,6 +128,8 @@ func dryRun(ctx *context.Context, lType, ldrID string, obj profile) (err error) msg = "<%s-%s> DRY_RUN: AttributeProfile: %s" case utils.MetaResources: msg = "<%s-%s> DRY_RUN: ResourceProfile: %s" + case utils.MetaIPs: + msg = "<%s-%s> DRY_RUN: IPProfile: %s" case utils.MetaFilters: fltr := obj.(*engine.Filter) fltr.Compress() @@ -208,6 +214,10 @@ func (l *loader) process(ctx *context.Context, obj profile, lType, action string cacheIDs = []string{utils.CacheResourceFilterIndexes} cacheArgs[utils.CacheResourceProfiles] = []string{tntId} cacheArgs[utils.CacheResources] = []string{tntId} + case utils.MetaIPs: + cacheIDs = []string{utils.CacheIPFilterIndexes} + cacheArgs[utils.CacheIPProfiles] = []string{tntId} + cacheArgs[utils.CacheIPs] = []string{tntId} case utils.MetaFilters: cacheArgs[utils.CacheFilters] = []string{tntId} case utils.MetaStats: diff --git a/services/commonlisteners.go b/services/commonlisteners.go index 514dff9e3..2cc4b6571 100644 --- a/services/commonlisteners.go +++ b/services/commonlisteners.go @@ -92,6 +92,7 @@ func (s *CommonListenerService) Shutdown(registry *servmanager.ServiceRegistry) utils.RateS, utils.RegistrarC, utils.ResourceS, + utils.IPs, utils.RouteS, utils.SessionS, utils.StatS, diff --git a/services/connmanager.go b/services/connmanager.go index 5ddf85ce0..2912d7dd5 100644 --- a/services/connmanager.go +++ b/services/connmanager.go @@ -149,6 +149,10 @@ var serviceMethods = map[string]internalRoute{ receiver: utils.ResourceSv1, internalPath: utils.ConcatenatedKey(utils.MetaInternal, utils.MetaResources), }, + utils.IPs: { + receiver: utils.IPsV1, + internalPath: utils.ConcatenatedKey(utils.MetaInternal, utils.MetaIPs), + }, utils.SessionS: { receiver: utils.SessionSv1, internalPath: utils.ConcatenatedKey(utils.MetaInternal, utils.MetaSessionS), diff --git a/services/datadb.go b/services/datadb.go index 7bd5ca86e..224346255 100644 --- a/services/datadb.go +++ b/services/datadb.go @@ -112,6 +112,7 @@ func (db *DataDBService) Reload(_ *utils.SyncedChan, _ *servmanager.ServiceRegis func (db *DataDBService) Shutdown(registry *servmanager.ServiceRegistry) error { deps := []string{ utils.ResourceS, + utils.IPs, utils.TrendS, utils.RankingS, utils.StatS, diff --git a/services/ips.go b/services/ips.go new file mode 100644 index 000000000..8e50aba17 --- /dev/null +++ b/services/ips.go @@ -0,0 +1,121 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package services + +import ( + "sync" + + "github.com/cgrates/birpc/context" + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/ips" + "github.com/cgrates/cgrates/servmanager" + "github.com/cgrates/cgrates/utils" +) + +// NewIPService returns the IP Service +func NewIPService(cfg *config.CGRConfig) *IPService { + return &IPService{ + cfg: cfg, + stateDeps: NewStateDependencies([]string{utils.StateServiceUP, utils.StateServiceDOWN}), + } +} + +// IPService implements Service interface +type IPService struct { + mu sync.RWMutex + cfg *config.CGRConfig + ips *ips.IPService + stateDeps *StateDependencies // channel subscriptions for state changes +} + +// Start handles the service start. +func (s *IPService) Start(shutdown *utils.SyncedChan, registry *servmanager.ServiceRegistry) error { + srvDeps, err := WaitForServicesToReachState(utils.StateServiceUP, + []string{ + utils.CommonListenerS, + utils.ConnManager, + utils.CacheS, + utils.FilterS, + utils.DataDB, + }, + registry, s.cfg.GeneralCfg().ConnectTimeout) + if err != nil { + return err + } + cl := srvDeps[utils.CommonListenerS].(*CommonListenerService).CLS() + cms := srvDeps[utils.ConnManager].(*ConnManagerService) + cacheS := srvDeps[utils.CacheS].(*CacheService) + if err := cacheS.WaitToPrecache(shutdown, + utils.CacheIPProfiles, + utils.CacheIPs, + utils.CacheIPFilterIndexes); err != nil { + return err + } + fs := srvDeps[utils.FilterS].(*FilterService) + dbs := srvDeps[utils.DataDB].(*DataDBService) + + s.mu.Lock() + defer s.mu.Unlock() + s.ips = ips.NewIPService(dbs.DataManager(), s.cfg, fs.FilterS(), cms.ConnManager()) + s.ips.StartLoop(context.TODO()) + srv, err := engine.NewService(s.ips) + if err != nil { + return err + } + for _, svc := range srv { + cl.RpcRegister(svc) + } + cms.AddInternalConn(utils.IPs, srv) + return nil +} + +// Reload handles configuration changes. +func (s *IPService) Reload(_ *utils.SyncedChan, _ *servmanager.ServiceRegistry) error { + s.mu.Lock() + s.ips.Reload(context.TODO()) + s.mu.Unlock() + return nil +} + +// Shutdown stops the service. +func (s *IPService) Shutdown(registry *servmanager.ServiceRegistry) error { + s.mu.Lock() + defer s.mu.Unlock() + s.ips.Shutdown(context.TODO()) //we don't verify the error because shutdown never returns an error + s.ips = nil + cl := registry.Lookup(utils.CommonListenerS).(*CommonListenerService).CLS() + cl.RpcUnregisterName(utils.IPsV1) + return nil +} + +// ServiceName returns the service name. +func (s *IPService) ServiceName() string { + return utils.IPs +} + +// ShouldRun returns if the service should be running. +func (s *IPService) ShouldRun() bool { + return s.cfg.IPsCfg().Enabled +} + +// StateChan returns signaling channel of specific state +func (s *IPService) StateChan(stateID string) chan struct{} { + return s.stateDeps.StateChan(stateID) +} diff --git a/services/prometheus.go b/services/prometheus.go index 8775514cf..7a62592d8 100644 --- a/services/prometheus.go +++ b/services/prometheus.go @@ -35,7 +35,7 @@ func NewPrometheusAgent(cfg *config.CGRConfig) *PrometheusAgent { } } -// PrometheusAgent implements Agent interface +// PrometheusAgent implements the Service interface. type PrometheusAgent struct { mu sync.RWMutex cfg *config.CGRConfig @@ -43,7 +43,7 @@ type PrometheusAgent struct { stateDeps *StateDependencies } -// Start should handle the sercive start +// Start handles the service start. func (s *PrometheusAgent) Start(_ *utils.SyncedChan, registry *servmanager.ServiceRegistry) (err error) { srvDeps, err := WaitForServicesToReachState(utils.StateServiceUP, []string{ diff --git a/servmanager/servmanager.go b/servmanager/servmanager.go index 3725bf81e..157dc07f4 100644 --- a/servmanager/servmanager.go +++ b/servmanager/servmanager.go @@ -264,6 +264,9 @@ func toggleService(id string, status bool, srvMngr *ServiceManager) (err error) case utils.ResourceS: srvMngr.cfg.ResourceSCfg().Enabled = status srvMngr.cfg.GetReloadChan() <- id + case utils.IPs: + srvMngr.cfg.IPsCfg().Enabled = status + srvMngr.cfg.GetReloadChan() <- id case utils.RouteS: srvMngr.cfg.RouteSCfg().Enabled = status srvMngr.cfg.GetReloadChan() <- id diff --git a/utils/apitpdata.go b/utils/apitpdata.go index c20230a64..889832b42 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -402,6 +402,8 @@ func NewAttrReloadCacheWithOpts() *AttrReloadCacheWithAPIOpts { return &AttrReloadCacheWithAPIOpts{ ResourceProfileIDs: []string{MetaAny}, ResourceIDs: []string{MetaAny}, + IPProfileIDs: []string{MetaAny}, + IPIDs: []string{MetaAny}, StatsQueueIDs: []string{MetaAny}, StatsQueueProfileIDs: []string{MetaAny}, ThresholdIDs: []string{MetaAny}, @@ -420,6 +422,7 @@ func NewAttrReloadCacheWithOpts() *AttrReloadCacheWithAPIOpts { AttributeFilterIndexIDs: []string{MetaAny}, ResourceFilterIndexIDs: []string{MetaAny}, + IPFilterIndexIDs: []string{MetaAny}, StatFilterIndexIDs: []string{MetaAny}, ThresholdFilterIndexIDs: []string{MetaAny}, RouteFilterIndexIDs: []string{MetaAny}, @@ -439,6 +442,8 @@ func NewAttrReloadCacheWithOptsFromMap(arg map[string][]string, tnt string, opts ResourceProfileIDs: arg[CacheResourceProfiles], ResourceIDs: arg[CacheResources], + IPProfileIDs: arg[CacheIPProfiles], + IPIDs: arg[CacheIPs], StatsQueueProfileIDs: arg[CacheStatQueueProfiles], StatsQueueIDs: arg[CacheStatQueues], ThresholdProfileIDs: arg[CacheThresholdProfiles], @@ -453,6 +458,7 @@ func NewAttrReloadCacheWithOptsFromMap(arg map[string][]string, tnt string, opts ActionProfileIDs: arg[CacheActionProfiles], AccountIDs: arg[CacheAccounts], ResourceFilterIndexIDs: arg[CacheResourceFilterIndexes], + IPFilterIndexIDs: arg[CacheIPFilterIndexes], StatFilterIndexIDs: arg[CacheStatFilterIndexes], ThresholdFilterIndexIDs: arg[CacheThresholdFilterIndexes], RouteFilterIndexIDs: arg[CacheRouteFilterIndexes], @@ -474,6 +480,8 @@ type AttrReloadCacheWithAPIOpts struct { ResourceProfileIDs []string `json:",omitempty"` ResourceIDs []string `json:",omitempty"` + IPProfileIDs []string `json:",omitempty"` + IPIDs []string `json:",omitempty"` StatsQueueIDs []string `json:",omitempty"` StatsQueueProfileIDs []string `json:",omitempty"` ThresholdIDs []string `json:",omitempty"` @@ -492,6 +500,7 @@ type AttrReloadCacheWithAPIOpts struct { AttributeFilterIndexIDs []string `json:",omitempty"` ResourceFilterIndexIDs []string `json:",omitempty"` + IPFilterIndexIDs []string `json:",omitempty"` StatFilterIndexIDs []string `json:",omitempty"` ThresholdFilterIndexIDs []string `json:",omitempty"` RouteFilterIndexIDs []string `json:",omitempty"` @@ -507,6 +516,8 @@ func (a *AttrReloadCacheWithAPIOpts) Map() map[string][]string { return map[string][]string{ CacheResourceProfiles: a.ResourceProfileIDs, CacheResources: a.ResourceIDs, + CacheIPProfiles: a.IPProfileIDs, + CacheIPs: a.IPIDs, CacheStatQueueProfiles: a.StatsQueueProfileIDs, CacheStatQueues: a.StatsQueueIDs, CacheThresholdProfiles: a.ThresholdProfileIDs, @@ -523,6 +534,7 @@ func (a *AttrReloadCacheWithAPIOpts) Map() map[string][]string { CacheActionProfiles: a.ActionProfileIDs, CacheAccounts: a.AccountIDs, CacheResourceFilterIndexes: a.ResourceFilterIndexIDs, + CacheIPFilterIndexes: a.IPFilterIndexIDs, CacheStatFilterIndexes: a.StatFilterIndexIDs, CacheThresholdFilterIndexes: a.ThresholdFilterIndexIDs, CacheRouteFilterIndexes: a.RouteFilterIndexIDs, diff --git a/utils/apitpdata_test.go b/utils/apitpdata_test.go index d31d85f02..2a5de734f 100644 --- a/utils/apitpdata_test.go +++ b/utils/apitpdata_test.go @@ -114,6 +114,8 @@ func TestNewAttrReloadCacheWithOpts(t *testing.T) { newAttrReloadCache := &AttrReloadCacheWithAPIOpts{ ResourceProfileIDs: []string{MetaAny}, ResourceIDs: []string{MetaAny}, + IPProfileIDs: []string{MetaAny}, + IPIDs: []string{MetaAny}, StatsQueueIDs: []string{MetaAny}, StatsQueueProfileIDs: []string{MetaAny}, ThresholdIDs: []string{MetaAny}, @@ -127,6 +129,7 @@ func TestNewAttrReloadCacheWithOpts(t *testing.T) { RateProfileIDs: []string{MetaAny}, AttributeFilterIndexIDs: []string{MetaAny}, ResourceFilterIndexIDs: []string{MetaAny}, + IPFilterIndexIDs: []string{MetaAny}, StatFilterIndexIDs: []string{MetaAny}, ThresholdFilterIndexIDs: []string{MetaAny}, RouteFilterIndexIDs: []string{MetaAny}, diff --git a/utils/consts.go b/utils/consts.go index 6592840c9..58471729a 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -37,14 +37,16 @@ var ( CacheCapsEvents, CacheReplicationHosts}) DataDBPartitions = NewStringSet([]string{ - CacheResourceProfiles, CacheResources, CacheEventResources, CacheStatQueueProfiles, CacheStatQueues, - CacheThresholdProfiles, CacheThresholds, CacheFilters, CacheRouteProfiles, CacheAttributeProfiles, CacheTrendProfiles, - CacheChargerProfiles, CacheActionProfiles, CacheRankingProfiles, CacheRankings, CacheTrends, - CacheResourceFilterIndexes, CacheStatFilterIndexes, CacheThresholdFilterIndexes, CacheRouteFilterIndexes, - CacheAttributeFilterIndexes, CacheChargerFilterIndexes, CacheLoadIDs, - CacheRateProfiles, CacheRateProfilesFilterIndexes, CacheRateFilterIndexes, - CacheActionProfilesFilterIndexes, CacheAccountsFilterIndexes, CacheReverseFilterIndexes, - CacheAccounts}) + CacheResourceProfiles, CacheResources, CacheEventResources, CacheIPProfiles, CacheIPs, + CacheEventIPs, CacheStatQueueProfiles, CacheStatQueues, CacheThresholdProfiles, + CacheThresholds, CacheFilters, CacheRouteProfiles, CacheAttributeProfiles, + CacheTrendProfiles, CacheChargerProfiles, CacheActionProfiles, CacheRankingProfiles, + CacheRankings, CacheTrends, CacheResourceFilterIndexes, CacheIPFilterIndexes, CacheStatFilterIndexes, + CacheThresholdFilterIndexes, CacheRouteFilterIndexes, CacheAttributeFilterIndexes, + CacheChargerFilterIndexes, CacheLoadIDs, CacheRateProfiles, CacheRateProfilesFilterIndexes, + CacheRateFilterIndexes, CacheActionProfilesFilterIndexes, CacheAccountsFilterIndexes, + CacheReverseFilterIndexes, CacheAccounts, + }) // CachePartitions enables creation of cache partitions CachePartitions = JoinStringSet(extraDBPartition, DataDBPartitions) @@ -52,6 +54,8 @@ var ( CacheInstanceToPrefix = map[string]string{ CacheResourceProfiles: ResourceProfilesPrefix, CacheResources: ResourcesPrefix, + CacheIPProfiles: IPProfilesPrefix, + CacheIPs: IPsPrefix, CacheStatQueueProfiles: StatQueueProfilePrefix, CacheStatQueues: StatQueuePrefix, CacheTrendProfiles: TrendProfilePrefix, @@ -68,6 +72,7 @@ var ( CacheActionProfiles: ActionProfilePrefix, CacheAccounts: AccountPrefix, CacheResourceFilterIndexes: ResourceFilterIndexes, + CacheIPFilterIndexes: IPFilterIndexes, CacheStatFilterIndexes: StatFilterIndexes, CacheThresholdFilterIndexes: ThresholdFilterIndexes, CacheRouteFilterIndexes: RouteFilterIndexes, @@ -86,6 +91,7 @@ var ( CacheIndexesToPrefix = map[string]string{ // used by match index to get all the ids when index selects is disabled and for compute indexes CacheThresholdFilterIndexes: ThresholdProfilePrefix, CacheResourceFilterIndexes: ResourceProfilesPrefix, + CacheIPFilterIndexes: IPProfilesPrefix, CacheStatFilterIndexes: StatQueueProfilePrefix, CacheRouteFilterIndexes: RouteProfilePrefix, CacheAttributeFilterIndexes: AttributeProfilePrefix, @@ -99,6 +105,7 @@ var ( CacheInstanceToCacheIndex = map[string]string{ CacheThresholdProfiles: CacheThresholdFilterIndexes, CacheResourceProfiles: CacheResourceFilterIndexes, + CacheIPProfiles: CacheIPFilterIndexes, CacheStatQueueProfiles: CacheStatFilterIndexes, CacheRouteProfiles: CacheRouteFilterIndexes, CacheAttributeProfiles: CacheAttributeFilterIndexes, @@ -244,6 +251,8 @@ const ( ConfigPrefix = "cfg_" ResourcesPrefix = "res_" ResourceProfilesPrefix = "rsp_" + IPsPrefix = "ips_" + IPProfilesPrefix = "ipp_" ThresholdPrefix = "thd_" FilterPrefix = "ftr_" CDRsStatsPrefix = "cst_" @@ -407,6 +416,7 @@ const ( RatingProfiles = "RatingProfiles" AccountActions = "AccountActions" ResourcesStr = "Resources" + IPsStr = "IPs" Stats = "Stats" Rankings = "Rankings" Trends = "Trends" @@ -455,6 +465,7 @@ const ( AccountID = "AccountID" AccountIDs = "AccountIDs" ResourceID = "ResourceID" + IPID = "IPID" TotalUsage = "TotalUsage" StatID = "StatID" BalanceType = "BalanceType" @@ -485,6 +496,7 @@ const ( SessionSCosts = "SessionSCosts" RQF = "RQF" ResourceStr = "Resource" + IPStr = "IP" User = "User" Subscribers = "Subscribers" //Destinations = "Destinations" @@ -501,6 +513,8 @@ const ( Limit = "Limit" UsageTTL = "UsageTTL" AllocationMessage = "AllocationMessage" + AddressPool = "AddressPool" + Allocation = "Allocation" Stored = "Stored" RatingSubject = "RatingSubject" Categories = "Categories" @@ -664,6 +678,7 @@ const ( VersionName = "Version" MetaTenant = "*tenant" ResourceUsageStr = "ResourceUsage" + IPUsageStr = "IPUsage" MetaDuration = "*duration" MetaLibPhoneNumber = "*libphonenumber" MetaTimeString = "*time_string" @@ -975,6 +990,7 @@ const ( MetaAccounts = "*accounts" MetaActions = "*actions" MetaResourceProfile = "*resource_profiles" + MetaIPProfiles = "*ip_profiles" MetaStatQueueProfiles = "*statqueue_profiles" MetaStatQueues = "*statqueues" MetaRankingProfiles = "*ranking_profiles" @@ -1014,6 +1030,7 @@ const ( AttributeS = "AttributeS" RouteS = "RouteS" ResourceS = "ResourceS" + IPs = "IPs" StatService = "StatS" FilterS = "FilterS" ThresholdS = "ThresholdS" @@ -1041,6 +1058,7 @@ const ( ChargerSLow = "chargers" RoutesLow = "routes" ResourcesLow = "resources" + IPsLow = "ips" StatServiceLow = "stats" ThresholdsLow = "thresholds" AnalyzerSLow = "analyzers" @@ -1097,6 +1115,7 @@ const ( MetaTpActionProfiles = "*tp_action_profiles" MetaTpRateProfiles = "*tp_rate_profiles" MetaTpResources = "*tp_resources" + MetaTpIPs = "*tp_ips" MetaTpChargers = "*tp_chargers" MetaDurationSeconds = "*duration_seconds" MetaDurationNanoseconds = "*duration_nanoseconds" @@ -1117,6 +1136,8 @@ const ( TpStats = "TpStats" TpResources = "TpResources" TpResource = "TpResource" + TpIPs = "TpIPs" + TpIP = "TpIP" TpChargers = "TpChargers" TpRateProfiles = "TpRateProfiles" TpActionProfiles = "TpActionProfiles" @@ -1135,6 +1156,7 @@ const ( RankingSv1 = "RankingSv1" StatSv1 = "StatSv1" ResourceSv1 = "ResourceSv1" + IPsV1 = "IPsV1" RouteSv1 = "RouteSv1" AttributeSv1 = "AttributeSv1" SessionSv1 = "SessionSv1" @@ -1162,6 +1184,7 @@ const ( MetaGreaterThan = "*gt" MetaGreaterOrEqual = "*gte" MetaResources = "*resources" + MetaIPs = "*ips" MetaEqual = "*eq" MetaIPNet = "*ipnet" MetaAPIBan = "*apiban" @@ -1210,6 +1233,8 @@ const ( ReplicatorSv1GetTrend = "ReplicatorSv1.GetTrend" ReplicatorSv1GetResource = "ReplicatorSv1.GetResource" ReplicatorSv1GetResourceProfile = "ReplicatorSv1.GetResourceProfile" + ReplicatorSv1GetIP = "ReplicatorSv1.GetIP" + ReplicatorSv1GetIPProfile = "ReplicatorSv1.GetIPProfile" ReplicatorSv1GetRouteProfile = "ReplicatorSv1.GetRouteProfile" ReplicatorSv1GetAttributeProfile = "ReplicatorSv1.GetAttributeProfile" ReplicatorSv1GetChargerProfile = "ReplicatorSv1.GetChargerProfile" @@ -1230,6 +1255,8 @@ const ( ReplicatorSv1SetTrend ReplicatorSv1SetResource = "ReplicatorSv1.SetResource" ReplicatorSv1SetResourceProfile = "ReplicatorSv1.SetResourceProfile" + ReplicatorSv1SetIP = "ReplicatorSv1.SetIP" + ReplicatorSv1SetIPProfile = "ReplicatorSv1.SetIPProfile" ReplicatorSv1SetRouteProfile = "ReplicatorSv1.SetRouteProfile" ReplicatorSv1SetAttributeProfile = "ReplicatorSv1.SetAttributeProfile" ReplicatorSv1SetChargerProfile = "ReplicatorSv1.SetChargerProfile" @@ -1249,6 +1276,8 @@ const ( ReplicatorSv1RemoveTrend = "ReplicatorSv1.RemoveTrend" ReplicatorSv1RemoveResource = "ReplicatorSv1.RemoveResource" ReplicatorSv1RemoveResourceProfile = "ReplicatorSv1.RemoveResourceProfile" + ReplicatorSv1RemoveIP = "ReplicatorSv1.RemoveIP" + ReplicatorSv1RemoveIPProfile = "ReplicatorSv1.RemoveIPProfile" ReplicatorSv1RemoveRouteProfile = "ReplicatorSv1.RemoveRouteProfile" ReplicatorSv1RemoveAttributeProfile = "ReplicatorSv1.RemoveAttributeProfile" ReplicatorSv1RemoveChargerProfile = "ReplicatorSv1.RemoveChargerProfile" @@ -1278,6 +1307,7 @@ const ( AdminSv1GetReverseFilterHealth = "AdminSv1.GetReverseFilterHealth" AdminSv1GetThresholdsIndexesHealth = "AdminSv1.GetThresholdsIndexesHealth" AdminSv1GetResourcesIndexesHealth = "AdminSv1.GetResourcesIndexesHealth" + AdminSv1GetIPsIndexesHealth = "AdminSv1.GetIPsIndexesHealth" AdminSv1GetStatsIndexesHealth = "AdminSv1.GetStatsIndexesHealth" AdminSv1GetRoutesIndexesHealth = "AdminSv1.GetRoutesIndexesHealth" AdminSv1GetChargersIndexesHealth = "AdminSv1.GetChargersIndexesHealth" @@ -1584,6 +1614,24 @@ const ( AdminSv1GetResourceProfilesCount = "AdminSv1.GetResourceProfilesCount" ) +// IPs APIs +const ( + IPsV1AuthorizeIPs = "IPsV1.AuthorizeIPs" + IPsV1GetIPsForEvent = "IPsV1.GetIPsForEvent" + IPsV1AllocateIPs = "IPsV1.AllocateIPs" + IPsV1ReleaseIPs = "IPsV1.ReleaseIPs" + IPsV1Ping = "IPsV1.Ping" + IPsV1GetIPWithConfig = "IPsV1.GetIPWithConfig" + IPsV1GetIP = "IPsV1.GetIP" + IPsV1GetIPs = "IPsV1.GetIPs" + AdminSv1SetIPProfile = "AdminSv1.SetIPProfile" + AdminSv1GetIPProfiles = "AdminSv1.GetIPProfiles" + AdminSv1RemoveIPProfile = "AdminSv1.RemoveIPProfile" + AdminSv1GetIPProfile = "AdminSv1.GetIPProfile" + AdminSv1GetIPProfileIDs = "AdminSv1.GetIPProfileIDs" + AdminSv1GetIPProfilesCount = "AdminSv1.GetIPProfilesCount" +) + // SessionS APIs const ( SessionSv1AuthorizeEvent = "SessionSv1.AuthorizeEvent" @@ -1762,6 +1810,7 @@ const ( // CSV file name const ( ResourcesCsv = "Resources.csv" + IPsCsv = "IPs.csv" StatsCsv = "Stats.csv" RankingsCsv = "Rankings.csv" TrendsCsv = "Trends.csv" @@ -1778,6 +1827,7 @@ const ( // Table Name const ( TBLTPResources = "tp_resources" + TBLTPIPs = "tp_ips" TBLTPStats = "tp_stats" TBLTPRankings = "tp_rankings" TBLTPTrends = "tp_trends" @@ -1803,6 +1853,9 @@ const ( CacheResources = "*resources" CacheResourceProfiles = "*resource_profiles" CacheEventResources = "*event_resources" + CacheIPs = "*ips" + CacheIPProfiles = "*ip_profiles" + CacheEventIPs = "*event_ips" CacheStatQueueProfiles = "*statqueue_profiles" CacheStatQueues = "*statqueues" CacheRankingProfiles = "*ranking_profiles" @@ -1819,6 +1872,7 @@ const ( CacheActionProfiles = "*action_profiles" CacheAccounts = "*accounts" CacheResourceFilterIndexes = "*resource_filter_indexes" + CacheIPFilterIndexes = "*ip_filter_indexes" CacheStatFilterIndexes = "*stat_filter_indexes" CacheThresholdFilterIndexes = "*threshold_filter_indexes" CacheRouteFilterIndexes = "*route_filter_indexes" @@ -1850,6 +1904,7 @@ const ( // Prefix for indexing const ( ResourceFilterIndexes = "rfi_" + IPFilterIndexes = "ifi_" StatFilterIndexes = "sfi_" ThresholdFilterIndexes = "tfi_" AttributeFilterIndexes = "afi_" @@ -2535,6 +2590,11 @@ const ( OptsResourcesUsageID = "*rsUsageID" OptsResourcesUsageTTL = "*rsUsageTTL" + // IPs + OptsIPsUnits = "*ipUnits" + OptsIPsUsageID = "*ipUsageID" + OptsIPsTTL = "*ipTTL" + // Routes OptsRoutesProfilesCount = "*rouProfilesCount" OptsRoutesLimit = "*rouLimit" diff --git a/utils/ips.go b/utils/ips.go new file mode 100644 index 000000000..db4ab90ac --- /dev/null +++ b/utils/ips.go @@ -0,0 +1,267 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package utils + +import ( + "slices" + "time" +) + +// IPProfile defines the configuration of the IP. +type IPProfile struct { + Tenant string + ID string + FilterIDs []string + TTL time.Duration + Type string + AddressPool string + Allocation string + Stored bool + Weights DynamicWeights +} + +// 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), + TTL: ip.TTL, + Type: ip.Type, + AddressPool: ip.AddressPool, + Allocation: ip.Allocation, + Stored: ip.Stored, + Weights: ip.Weights.Clone(), + } +} + +// CacheClone returns a clone of IPProfile used by ltcache CacheCloner +func (ip *IPProfile) CacheClone() any { + return ip.Clone() +} + +// IPProfileWithAPIOpts wraps IPProfile with APIOpts. +type IPProfileWithAPIOpts struct { + *IPProfile + APIOpts map[string]any +} + +// TenantID returns the concatenated tenant and ID. +func (ip *IPProfile) TenantID() string { + return ConcatenatedKey(ip.Tenant, ip.ID) +} + +// 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 ConcatenatedKey(u.Tenant, u.ID) +} + +// isActive checks ExpiryTime at some time +func (u *IPUsage) IsActive(atTime time.Time) bool { + return u.ExpiryTime.IsZero() || u.ExpiryTime.Sub(atTime) > 0 +} + +// Clone duplicates ru +func (u *IPUsage) Clone() *IPUsage { + if u == nil { + return nil + } + clone := *u + return &clone +} + +// IP represents ... +type IP struct { + Tenant string + ID string + Usages map[string]*IPUsage + TTLIdx []string +} + +// Clone clones *IP (lkID excluded) +func (ip *IP) Clone() *IP { + if ip == nil { + return nil + } + clone := &IP{ + Tenant: ip.Tenant, + ID: ip.ID, + TTLIdx: slices.Clone(ip.TTLIdx), + } + if ip.Usages != nil { + clone.Usages = make(map[string]*IPUsage, len(ip.Usages)) + for key, usage := range ip.Usages { + clone.Usages[key] = usage.Clone() + } + } + return clone +} + +// CacheClone returns a clone of IP used by ltcache CacheCloner +func (ip *IP) CacheClone() any { + return ip.Clone() +} + +// IPWithAPIOpts wraps IP with APIOpts. +type IPWithAPIOpts struct { + *IP + APIOpts map[string]any +} + +// TenantID returns the unique ID in a multi-tenant environment +func (ip *IP) TenantID() string { + return ConcatenatedKey(ip.Tenant, ip.ID) +} + +// TotalUsage returns the sum of all usage units +// Exported to be used in FilterS +func (ip *IP) TotalUsage() float64 { + var tu float64 + for _, ru := range ip.Usages { + tu += ru.Units + } + return tu +} + +func (ip *IPProfile) Set(path []string, val any, _ bool) error { + if len(path) != 1 { + return ErrWrongPath + } + var err error + switch path[0] { + default: + return ErrWrongPath + case Tenant: + ip.Tenant = IfaceAsString(val) + case ID: + ip.ID = IfaceAsString(val) + case FilterIDs: + var valA []string + valA, err = IfaceAsStringSlice(val) + ip.FilterIDs = append(ip.FilterIDs, valA...) + case TTL: + ip.TTL, err = IfaceAsDuration(val) + case Type: + ip.Type = IfaceAsString(val) + case AddressPool: + ip.AddressPool = IfaceAsString(val) + case Allocation: + ip.Allocation = IfaceAsString(val) + case Stored: + ip.Stored, err = IfaceAsBool(val) + case Weights: + if val != "" { + ip.Weights, err = NewDynamicWeightsFromString(IfaceAsString(val), InfieldSep, ANDSep) + } + } + return err +} + +func (ip *IPProfile) Merge(v2 any) { + vi := v2.(*IPProfile) + if len(vi.Tenant) != 0 { + ip.Tenant = vi.Tenant + } + if len(vi.ID) != 0 { + ip.ID = vi.ID + } + ip.FilterIDs = append(ip.FilterIDs, vi.FilterIDs...) + if len(vi.Allocation) != 0 { + ip.Allocation = vi.Allocation + } + if vi.TTL != 0 { + ip.TTL = vi.TTL + } + if vi.Type != "" { + ip.Type = vi.Type + } + if vi.AddressPool != "" { + ip.AddressPool = vi.AddressPool + } + if vi.Stored { + ip.Stored = vi.Stored + } + ip.Weights = append(ip.Weights, vi.Weights...) +} + +func (ip *IPProfile) String() string { return ToJSON(ip) } +func (ip *IPProfile) FieldAsString(fldPath []string) (string, error) { + val, err := ip.FieldAsInterface(fldPath) + if err != nil { + return "", err + } + return IfaceAsString(val), nil +} +func (ip *IPProfile) FieldAsInterface(fldPath []string) (any, error) { + if len(fldPath) != 1 { + return nil, ErrNotFound + } + switch fldPath[0] { + default: + fld, idx := GetPathIndex(fldPath[0]) + if idx != nil { + switch fld { + case FilterIDs: + if *idx < len(ip.FilterIDs) { + return ip.FilterIDs[*idx], nil + } + } + } + return nil, ErrNotFound + case Tenant: + return ip.Tenant, nil + case ID: + return ip.ID, nil + case FilterIDs: + return ip.FilterIDs, nil + case TTL: + return ip.TTL, nil + case Type: + return ip.Type, nil + case AddressPool: + return ip.AddressPool, nil + case Allocation: + return ip.Allocation, nil + case Stored: + return ip.Stored, nil + case Weights: + return ip.Weights, nil + } +} + +// IPProfileLockKey returns the ID used to lock a resourceProfile with guardian +func IPProfileLockKey(tnt, id string) string { + return ConcatenatedKey(CacheIPProfiles, tnt, id) +} + +// IPLockKey returns the ID used to lock a resource with guardian +func IPLockKey(tnt, id string) string { + return ConcatenatedKey(CacheIPs, tnt, id) +}