From 895c1402098f796affd1005bf72ebaf0ed7d3b92 Mon Sep 17 00:00:00 2001 From: ionutboangiu Date: Thu, 22 Sep 2022 09:16:05 +0300 Subject: [PATCH] Port dispatcher changes from 1.0 to v0.11 --- apier/v1/caches.go | 12 + apier/v1/chargers_it_test.go | 10 +- apier/v1/core.go | 10 +- apier/v1/dispatcher.go | 61 +- apier/v1/dispatchersv1_it_test.go | 6 +- apier/v1/sessions_process_event_it_test.go | 10 +- apier/v1/sessionsv1_it_test.go | 10 +- config/cachecfg.go | 32 +- config/cachecfg_test.go | 4 +- config/config_defaults.go | 93 +-- config/config_json_test.go | 93 +-- config/config_test.go | 99 +-- config/eescfg_test.go | 1 + config/libconfig_json.go | 2 + cores/core.go | 5 + .../conf/samples/dispatcher_opts/cgrates.json | 52 ++ .../dispatcher_opts_apier/cgrates.json | 51 ++ .../dispatcher_opts_setter/cgrates.json | 51 ++ dispatchers/caches.go | 38 ++ dispatchers/cores.go | 175 +++++ dispatchers/dispatchers.go | 97 ++- dispatchers/dispatchers_test.go | 64 +- dispatchers/libdispatcher.go | 228 ++++--- dispatchers/libdispatcher_test.go | 54 +- dispatchers/sessions_it_test.go | 10 +- ees/ees.go | 2 +- ees/rpc_test.go | 2 +- engine/caches.go | 101 ++- engine/cdrs.go | 2 +- engine/chargers.go | 2 +- engine/chargers_test.go | 10 +- engine/routes.go | 2 +- engine/tpreader_test.go | 26 +- engine/z_chargers_test.go | 8 +- general_tests/dispatcher_opts_it_test.go | 646 ++++++++++++++++++ general_tests/filters_it_test.go | 4 +- general_tests/session3_it_test.go | 2 +- services/dispatchers.go | 3 + servmanager/servmanager.go | 2 +- sessions/sessions.go | 2 +- utils/apitpdata.go | 13 +- utils/consts.go | 15 +- utils/coreutils.go | 26 +- 43 files changed, 1730 insertions(+), 406 deletions(-) create mode 100644 data/conf/samples/dispatcher_opts/cgrates.json create mode 100644 data/conf/samples/dispatcher_opts_apier/cgrates.json create mode 100644 data/conf/samples/dispatcher_opts_setter/cgrates.json create mode 100644 dispatchers/cores.go create mode 100644 general_tests/dispatcher_opts_it_test.go diff --git a/apier/v1/caches.go b/apier/v1/caches.go index 923c1fd73..afda429fb 100644 --- a/apier/v1/caches.go +++ b/apier/v1/caches.go @@ -47,6 +47,18 @@ func (chSv1 *CacheSv1) HasItem(args *utils.ArgsGetCacheItemWithAPIOpts, return chSv1.cacheS.V1HasItem(args, reply) } +// GetItem returns an Item from the cache +func (chSv1 *CacheSv1) GetItem(args *utils.ArgsGetCacheItemWithAPIOpts, + reply *interface{}) error { + return chSv1.cacheS.V1GetItem(args, reply) +} + +// GetItemWithRemote returns an Item from local or remote cache +func (chSv1 *CacheSv1) GetItemWithRemote(args *utils.ArgsGetCacheItemWithAPIOpts, + reply *interface{}) error { + return chSv1.cacheS.V1GetItemWithRemote(args, reply) +} + // GetItemExpiryTime returns the expiryTime for an item func (chSv1 *CacheSv1) GetItemExpiryTime(args *utils.ArgsGetCacheItemWithAPIOpts, reply *time.Time) error { diff --git a/apier/v1/chargers_it_test.go b/apier/v1/chargers_it_test.go index 4f725ee3a..2f5092960 100644 --- a/apier/v1/chargers_it_test.go +++ b/apier/v1/chargers_it_test.go @@ -104,7 +104,7 @@ var ( } ) -//Test start here +// Test start here func TestChargerSIT(t *testing.T) { sTestsChargerCache := sTestsCharger switch *dbType { @@ -332,7 +332,7 @@ func testChargerSProcessEvent(t *testing.T) { }, APIOpts: map[string]interface{}{ utils.OptsContext: "simpleauth", - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, utils.OptsAttributesProfileIDs: []string{"ATTR_1001_SIMPLEAUTH"}, }, }, @@ -365,7 +365,7 @@ func testChargerSProcessEvent(t *testing.T) { }, APIOpts: map[string]interface{}{ utils.OptsContext: "simpleauth", - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, utils.OptsAttributesProfileIDs: []string{"*constant:*req.RequestType:*rated;*constant:*req.Category:call"}, }, }, @@ -542,7 +542,7 @@ func testChargerSProcessWithNotFoundAttribute(t *testing.T) { "RunID": "CustomRun", }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, utils.OptsAttributesProfileIDs: nil, }, }, @@ -607,7 +607,7 @@ func testChargerSProccessEventWithProcceSRunS(t *testing.T) { utils.RunID: "*default", }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, utils.OptsAttributesProfileIDs: []string{"*constant:*req.Account:1002", "*constant:*req.Account:1003"}, utils.OptsAttributesProcessRuns: 1., }, diff --git a/apier/v1/core.go b/apier/v1/core.go index 7baa8a1d7..f06a51e41 100644 --- a/apier/v1/core.go +++ b/apier/v1/core.go @@ -96,12 +96,6 @@ func (cS *CoreSv1) StopMemoryProfiling(_ *utils.TenantWithAPIOpts, reply *string return nil } -type PanicMessageArgs struct { - Tenant string - APIOpts map[string]interface{} - Message string -} - -func (cS *CoreSv1) Panic(args *PanicMessageArgs, _ *string) error { - panic(args.Message) +func (cS *CoreSv1) Panic(args *utils.PanicMessageArgs, reply *string) error { + return cS.cS.Panic(args, reply) } diff --git a/apier/v1/dispatcher.go b/apier/v1/dispatcher.go index ae4e629bb..209578b02 100644 --- a/apier/v1/dispatcher.go +++ b/apier/v1/dispatcher.go @@ -73,7 +73,7 @@ type DispatcherWithAPIOpts struct { APIOpts map[string]interface{} } -//SetDispatcherProfile add/update a new Dispatcher Profile +// SetDispatcherProfile add/update a new Dispatcher Profile func (apierSv1 *APIerSv1) SetDispatcherProfile(args *DispatcherWithAPIOpts, reply *string) error { if missing := utils.MissingStructFields(args.DispatcherProfile, []string{utils.ID, utils.Subsystems}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) @@ -97,7 +97,7 @@ func (apierSv1 *APIerSv1) SetDispatcherProfile(args *DispatcherWithAPIOpts, repl return nil } -//RemoveDispatcherProfile remove a specific Dispatcher Profile +// RemoveDispatcherProfile remove a specific Dispatcher Profile func (apierSv1 *APIerSv1) RemoveDispatcherProfile(arg *utils.TenantIDWithAPIOpts, reply *string) error { if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing return utils.NewErrMandatoryIeMissing(missing...) @@ -162,7 +162,7 @@ func (apierSv1 *APIerSv1) GetDispatcherHostIDs(tenantArg *utils.PaginatorWithTen return nil } -//SetDispatcherHost add/update a new Dispatcher Host +// SetDispatcherHost add/update a new Dispatcher Host func (apierSv1 *APIerSv1) SetDispatcherHost(args *engine.DispatcherHostWithAPIOpts, reply *string) error { if missing := utils.MissingStructFields(args.DispatcherHost, []string{utils.ID}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) @@ -186,7 +186,7 @@ func (apierSv1 *APIerSv1) SetDispatcherHost(args *engine.DispatcherHostWithAPIOp return nil } -//RemoveDispatcherHost remove a specific Dispatcher Host +// RemoveDispatcherHost remove a specific Dispatcher Host func (apierSv1 *APIerSv1) RemoveDispatcherHost(arg *utils.TenantIDWithAPIOpts, reply *string) error { if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing return utils.NewErrMandatoryIeMissing(missing...) @@ -609,6 +609,18 @@ func (dS *DispatcherCacheSv1) HasItem(args *utils.ArgsGetCacheItemWithAPIOpts, return dS.dS.CacheSv1HasItem(args, reply) } +// GetItem returns an Item from the cache +func (dS *DispatcherCacheSv1) GetItem(args *utils.ArgsGetCacheItemWithAPIOpts, + reply *interface{}) error { + return dS.dS.CacheSv1GetItem(args, reply) +} + +// GetItemWithRemote returns an Item from local or remote cache +func (dS *DispatcherCacheSv1) GetItemWithRemote(args *utils.ArgsGetCacheItemWithAPIOpts, + reply *interface{}) error { + return dS.dS.CacheSv1GetItemWithRemote(args, reply) +} + // GetItemExpiryTime returns the expiryTime for an item func (dS *DispatcherCacheSv1) GetItemExpiryTime(args *utils.ArgsGetCacheItemWithAPIOpts, reply *time.Time) error { @@ -888,6 +900,47 @@ func (dS *DispatcherRALsV1) Ping(args *utils.CGREvent, reply *string) error { return dS.dS.RALsV1Ping(args, reply) } +// DispatcherCoreSv1 exports RPC from CoreSv1 +type DispatcherCoreSv1 struct { + dS *dispatchers.DispatcherService +} + +func NewDispatcherCoreSv1(dps *dispatchers.DispatcherService) *DispatcherCoreSv1 { + return &DispatcherCoreSv1{dS: dps} +} + +func (dS *DispatcherCoreSv1) Status(args *utils.TenantWithAPIOpts, reply *map[string]interface{}) error { + return dS.dS.CoreSv1Status(args, reply) +} + +func (dS *DispatcherCoreSv1) Ping(args *utils.CGREvent, reply *string) error { + return dS.dS.CoreSv1Ping(args, reply) +} + +func (dS *DispatcherCoreSv1) Sleep(args *utils.DurationArgs, reply *string) error { + return dS.dS.CoreSv1Sleep(args, reply) +} + +func (dS *DispatcherCoreSv1) StartCPUProfiling(args *utils.DirectoryArgs, reply *string) error { + return dS.dS.CoreSv1StartCPUProfiling(args, reply) +} + +func (dS *DispatcherCoreSv1) StopCPUProfiling(args *utils.TenantWithAPIOpts, reply *string) error { + return dS.dS.CoreSv1StopCPUProfiling(args, reply) +} + +func (dS *DispatcherCoreSv1) StartMemoryProfiling(args *utils.MemoryPrf, reply *string) error { + return dS.dS.CoreSv1StartMemoryProfiling(args, reply) +} + +func (dS *DispatcherCoreSv1) StopMemoryProfiling(args *utils.TenantWithAPIOpts, reply *string) error { + return dS.dS.CoreSv1StopMemoryProfiling(args, reply) +} + +func (dS *DispatcherCoreSv1) Panic(args *utils.PanicMessageArgs, reply *string) error { + return dS.dS.CoreSv1Panic(args, reply) +} + type DispatcherReplicatorSv1 struct { dS *dispatchers.DispatcherService } diff --git a/apier/v1/dispatchersv1_it_test.go b/apier/v1/dispatchersv1_it_test.go index 8e03f208b..1eb07d581 100644 --- a/apier/v1/dispatchersv1_it_test.go +++ b/apier/v1/dispatchersv1_it_test.go @@ -140,7 +140,7 @@ func testDspDspv1GetProfileForEvent(t *testing.T) { utils.EventName: "Event1", }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaAny, + utils.MetaSubsys: utils.MetaAny, utils.OptsDispatchersProfilesCount: 1, }, } @@ -189,7 +189,7 @@ func testDspDspv1GetProfileForEvent(t *testing.T) { utils.EventName: "Event1", }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaAny, + utils.MetaSubsys: utils.MetaAny, utils.OptsDispatchersProfilesCount: 1, }, } @@ -211,7 +211,7 @@ func testDspDspv1GetProfileForEventWithMethod(t *testing.T) { ID: "testDspv2", Event: map[string]interface{}{}, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaAny, + utils.MetaSubsys: utils.MetaAny, "*method": utils.DispatcherSv1GetProfilesForEvent, utils.OptsDispatchersProfilesCount: 1, }, diff --git a/apier/v1/sessions_process_event_it_test.go b/apier/v1/sessions_process_event_it_test.go index c261c6cb7..9302a93e4 100644 --- a/apier/v1/sessions_process_event_it_test.go +++ b/apier/v1/sessions_process_event_it_test.go @@ -33,8 +33,8 @@ import ( "github.com/cgrates/cgrates/utils" ) -//Use from sessionsv1_it_test.go -//functions insted of duplicate them here +// Use from sessionsv1_it_test.go +// functions insted of duplicate them here // eg: initCfg,ResetDB,StopEngine,etc... var sTestSessionSv1ProcessEvent = []func(t *testing.T){ testSSv1ItInitCfg, @@ -247,7 +247,7 @@ func testSSv1ItProcessEventAuth(t *testing.T) { utils.Usage: 300000000000.0, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaSessionS, + utils.MetaSubsys: utils.MetaSessionS, utils.OptsResourcesUnits: 1., utils.OptsResourcesUsageID: "testSSv1ItProcessEvent", utils.OptsAttributesProfileIDs: nil, @@ -326,7 +326,7 @@ func testSSv1ItProcessEventInitiateSession(t *testing.T) { utils.Usage: 300000000000.0, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaSessionS, + utils.MetaSubsys: utils.MetaSessionS, utils.OptsResourcesUnits: 1., utils.OptsResourcesUsageID: "testSSv1ItProcessEvent", utils.OptsAttributesProfileIDs: nil, @@ -394,7 +394,7 @@ func testSSv1ItProcessEventUpdateSession(t *testing.T) { utils.Usage: 300000000000.0, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaSessionS, + utils.MetaSubsys: utils.MetaSessionS, utils.OptsAttributesProfileIDs: nil, }, }, diff --git a/apier/v1/sessionsv1_it_test.go b/apier/v1/sessionsv1_it_test.go index 569977613..6ee9a3a3a 100644 --- a/apier/v1/sessionsv1_it_test.go +++ b/apier/v1/sessionsv1_it_test.go @@ -283,7 +283,7 @@ func testSSv1ItAuth(t *testing.T) { utils.Usage: 300000000000.0, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaSessionS, + utils.MetaSubsys: utils.MetaSessionS, utils.OptsAttributesProfileIDs: nil, utils.OptsResourcesUsageID: "TestSSv1It1", utils.OptsResourcesUnits: 1., @@ -400,7 +400,7 @@ func testSSv1ItInitiateSession(t *testing.T) { utils.Usage: 300000000000.0, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaSessionS, + utils.MetaSubsys: utils.MetaSessionS, utils.OptsAttributesProfileIDs: nil, utils.OptsResourcesUnits: 1., utils.OptsResourcesUsageID: "TestSSv1It1", @@ -517,7 +517,7 @@ func testSSv1ItUpdateSession(t *testing.T) { utils.Usage: 300000000000.0, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaSessionS, + utils.MetaSubsys: utils.MetaSessionS, utils.OptsAttributesProfileIDs: nil, }, }, @@ -664,7 +664,7 @@ func testSSv1ItProcessEvent(t *testing.T) { utils.Usage: 300000000000.0, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaSessionS, + utils.MetaSubsys: utils.MetaSessionS, utils.OptsAttributesProfileIDs: nil, utils.OptsResourcesUnits: 1., utils.OptsResourcesUsageID: "TestSSv1It2", @@ -848,7 +848,7 @@ func testSSv1ItForceUpdateSession(t *testing.T) { utils.Usage: 300000000000.0, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaSessionS, + utils.MetaSubsys: utils.MetaSessionS, utils.OptsAttributesProfileIDs: nil, }, }, diff --git a/config/cachecfg.go b/config/cachecfg.go index 94c9c1784..23eae0639 100644 --- a/config/cachecfg.go +++ b/config/cachecfg.go @@ -32,6 +32,7 @@ type CacheParamCfg struct { TTL time.Duration StaticTTL bool Precache bool + Remote bool Replicate bool } @@ -48,6 +49,9 @@ func (cParam *CacheParamCfg) loadFromJSONCfg(jsnCfg *CacheParamJsonCfg) (err err if jsnCfg.Precache != nil { cParam.Precache = *jsnCfg.Precache } + if jsnCfg.Remote != nil { + cParam.Remote = *jsnCfg.Remote + } if jsnCfg.Replicate != nil { cParam.Replicate = *jsnCfg.Replicate } @@ -63,6 +67,7 @@ func (cParam *CacheParamCfg) AsMapInterface() (initialMP map[string]interface{}) utils.LimitCfg: cParam.Limit, utils.StaticTTLCfg: cParam.StaticTTL, utils.PrecacheCfg: cParam.Precache, + utils.RemoteCfg: cParam.Remote, utils.ReplicateCfg: cParam.Replicate, } if cParam.TTL != 0 { @@ -78,6 +83,7 @@ func (cParam CacheParamCfg) Clone() (cln *CacheParamCfg) { TTL: cParam.TTL, StaticTTL: cParam.StaticTTL, Precache: cParam.Precache, + Remote: cParam.Remote, Replicate: cParam.Replicate, } } @@ -86,6 +92,7 @@ func (cParam CacheParamCfg) Clone() (cln *CacheParamCfg) { type CacheCfg struct { Partitions map[string]*CacheParamCfg ReplicationConns []string + RemoteConns []string } func (cCfg *CacheCfg) loadFromJSONCfg(jsnCfg *CacheJsonCfg) (err error) { @@ -110,6 +117,12 @@ func (cCfg *CacheCfg) loadFromJSONCfg(jsnCfg *CacheJsonCfg) (err error) { cCfg.ReplicationConns[idx] = connID } } + if jsnCfg.Remote_conns != nil { + cCfg.RemoteConns = make([]string, len(*jsnCfg.Remote_conns)) + for idx, connID := range *jsnCfg.Remote_conns { + cCfg.RemoteConns[idx] = connID + } + } return nil } @@ -135,15 +148,18 @@ func (cCfg *CacheCfg) AddTmpCaches() { } // AsMapInterface returns the config as a map[string]interface{} -func (cCfg *CacheCfg) AsMapInterface() (initialMP map[string]interface{}) { - initialMP = make(map[string]interface{}) +func (cCfg *CacheCfg) AsMapInterface() (mp map[string]interface{}) { + mp = make(map[string]interface{}) partitions := make(map[string]interface{}, len(cCfg.Partitions)) for key, value := range cCfg.Partitions { partitions[key] = value.AsMapInterface() } - initialMP[utils.PartitionsCfg] = partitions + mp[utils.PartitionsCfg] = partitions if cCfg.ReplicationConns != nil { - initialMP[utils.ReplicationConnsCfg] = cCfg.ReplicationConns + mp[utils.ReplicationConnsCfg] = cCfg.ReplicationConns + } + if cCfg.RemoteConns != nil { + mp[utils.RemoteConnsCfg] = cCfg.RemoteConns } return } @@ -157,10 +173,10 @@ func (cCfg CacheCfg) Clone() (cln *CacheCfg) { cln.Partitions[key] = par.Clone() } if cCfg.ReplicationConns != nil { - cln.ReplicationConns = make([]string, len(cCfg.ReplicationConns)) - for i, c := range cCfg.ReplicationConns { - cln.ReplicationConns[i] = c - } + cln.ReplicationConns = utils.CloneStringSlice(cCfg.ReplicationConns) + } + if cCfg.RemoteConns != nil { + cln.RemoteConns = utils.CloneStringSlice(cCfg.RemoteConns) } return } diff --git a/config/cachecfg_test.go b/config/cachecfg_test.go index f4a3ec070..4ea84d583 100644 --- a/config/cachecfg_test.go +++ b/config/cachecfg_test.go @@ -160,13 +160,13 @@ func TestCachesCfgAsMapInterface1(t *testing.T) { cfgJSONStr := `{ "caches":{ "partitions": { - "*destinations": {"limit": 10000, "static_ttl": false, "precache": true, "replicate": true}, + "*destinations": {"limit": 10000, "static_ttl": false, "precache": true, "remote": true, "replicate": true}, }, }, }` eMap := map[string]interface{}{ utils.PartitionsCfg: map[string]interface{}{ - utils.MetaDestinations: map[string]interface{}{"limit": 10000, "static_ttl": false, "precache": true, "replicate": true}, + utils.MetaDestinations: map[string]interface{}{"limit": 10000, "static_ttl": false, "precache": true, "remote": true, "replicate": true}, }, utils.ReplicationConnsCfg: []string{}, } diff --git a/config/config_defaults.go b/config/config_defaults.go index 18f1c24bf..97a63f097 100644 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -260,54 +260,55 @@ const CGRATES_CFG_JSON = ` "caches":{ "partitions": { - "*destinations": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // destination caching - "*reverse_destinations": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // reverse destinations index caching - "*rating_plans": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // rating plans caching - "*rating_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // rating profiles caching - "*actions": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // actions caching - "*action_plans": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // action plans caching - "*account_action_plans": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // account action plans index caching - "*action_triggers": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // action triggers caching - "*shared_groups": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // shared groups caching - "*timings": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // timings caching - "*resource_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // control resource profiles caching - "*resources": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // control resources caching - "*event_resources": {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // matching resources to events - "*statqueue_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // statqueue profiles - "*statqueues": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // statqueues with metrics - "*threshold_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // control threshold profiles caching - "*thresholds": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // control thresholds caching - "*filters": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // control filters caching - "*route_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // control route profile caching - "*attribute_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // control attribute profile caching - "*charger_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // control charger profile caching - "*dispatcher_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // control dispatcher profile caching - "*dispatcher_hosts": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // control dispatcher hosts caching - "*resource_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // control resource filter indexes caching - "*stat_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // control stat filter indexes caching - "*threshold_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // control threshold filter indexes caching - "*route_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // control route filter indexes caching - "*attribute_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // control attribute filter indexes caching - "*charger_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // control charger filter indexes caching - "*dispatcher_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // control dispatcher filter indexes caching - "*reverse_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // control reverse filter indexes caching used only for set and remove filters - "*dispatcher_routes": {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // control dispatcher routes caching - "*dispatcher_loads": {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // control dispatcher load( in case of *ratio ConnParams is present) - "*dispatchers": {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // control dispatcher interface - "*diameter_messages": {"limit": -1, "ttl": "3h", "static_ttl": false, "replicate": false}, // diameter messages caching - "*rpc_responses": {"limit": 0, "ttl": "2s", "static_ttl": false, "replicate": false}, // RPC responses caching - "*closed_sessions": {"limit": -1, "ttl": "10s", "static_ttl": false, "replicate": false}, // closed sessions cached for CDRs - "*event_charges": {"limit": 0, "ttl": "10s", "static_ttl": false, "replicate": false}, // events proccessed by ChargerS - "*cdr_ids": {"limit": -1, "ttl": "10m", "static_ttl": false, "replicate": false}, // protects CDRs against double-charging - "*load_ids": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "replicate": false}, // control the load_ids for items - "*rpc_connections": {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // RPC connections caching - "*uch": {"limit": -1, "ttl": "3h", "static_ttl": false, "replicate": false}, // User cache - "*stir": {"limit": -1, "ttl": "3h", "static_ttl": false, "replicate": false}, // stirShaken cache keys - "*apiban":{"limit": -1, "ttl": "2m", "static_ttl": false, "replicate": false}, - "*caps_events": {"limit": -1, "ttl": "", "static_ttl": false, "replicate": false}, // caps cached samples - "*replication_hosts": {"limit": 0, "ttl": "", "static_ttl": false, "replicate": false}, // the replication hosts cache(used when replication_filtered is enbled) + "*destinations": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // destination caching + "*reverse_destinations": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // reverse destinations index caching + "*rating_plans": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // rating plans caching + "*rating_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // rating profiles caching + "*actions": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // actions caching + "*action_plans": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // action plans caching + "*account_action_plans": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // account action plans index caching + "*action_triggers": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // action triggers caching + "*shared_groups": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // shared groups caching + "*timings": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // timings caching + "*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 + "*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 + "*thresholds": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control thresholds caching + "*filters": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control filters caching + "*route_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control route profile caching + "*attribute_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control attribute profile caching + "*charger_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control charger profile caching + "*dispatcher_profiles": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control dispatcher profile caching + "*dispatcher_hosts": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control dispatcher hosts caching + "*resource_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // control resource filter indexes caching + "*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 + "*attribute_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // control attribute filter indexes caching + "*charger_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // control charger filter indexes caching + "*dispatcher_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // control dispatcher filter indexes caching + "*reverse_filter_indexes" : {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // control reverse filter indexes caching used only for set and remove filters + "*dispatcher_routes": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // control dispatcher routes caching + "*dispatcher_loads": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // control dispatcher load( in case of *ratio ConnParams is present) + "*dispatchers": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // control dispatcher interface + "*diameter_messages": {"limit": -1, "ttl": "3h", "static_ttl": false, "remote":false, "replicate": false}, // diameter messages caching + "*rpc_responses": {"limit": 0, "ttl": "2s", "static_ttl": false, "remote":false, "replicate": false}, // RPC responses caching + "*closed_sessions": {"limit": -1, "ttl": "10s", "static_ttl": false, "remote":false, "replicate": false}, // closed sessions cached for CDRs + "*event_charges": {"limit": 0, "ttl": "10s", "static_ttl": false, "remote":false, "replicate": false}, // events proccessed by ChargerS + "*cdr_ids": {"limit": -1, "ttl": "10m", "static_ttl": false, "remote":false, "replicate": false}, // protects CDRs against double-charging + "*load_ids": {"limit": -1, "ttl": "", "static_ttl": false, "precache": false, "remote":false, "replicate": false}, // control the load_ids for items + "*rpc_connections": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // RPC connections caching + "*uch": {"limit": -1, "ttl": "3h", "static_ttl": false, "remote":false, "replicate": false}, // User cache + "*stir": {"limit": -1, "ttl": "3h", "static_ttl": false, "remote":false, "replicate": false}, // stirShaken cache keys + "*apiban":{"limit": -1, "ttl": "2m", "static_ttl": false, "remote":false, "replicate": false}, + "*caps_events": {"limit": -1, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // caps cached samples + "*replication_hosts": {"limit": 0, "ttl": "", "static_ttl": false, "remote":false, "replicate": false}, // the replication hosts cache(used when replication_filtered is enbled) }, "replication_conns": [], + "remote_conns": [], // the conns that are queried when the items are not found in cache }, diff --git a/config/config_json_test.go b/config/config_json_test.go index f06bc8c6e..e6f3157ce 100644 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -88,146 +88,147 @@ func TestCacheJsonCfg(t *testing.T) { Partitions: &map[string]*CacheParamJsonCfg{ utils.CacheDestinations: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheReverseDestinations: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheRatingPlans: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheRatingProfiles: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheActions: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheActionPlans: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheAccountActionPlans: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheActionTriggers: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheSharedGroups: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheTimings: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheResourceProfiles: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheResources: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheEventResources: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Replicate: 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), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheStatQueues: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheThresholdProfiles: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheThresholds: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheFilters: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheRouteProfiles: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheAttributeProfiles: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheChargerProfiles: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheDispatcherProfiles: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheDispatcherHosts: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheResourceFilterIndexes: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Replicate: 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), - Replicate: utils.BoolPointer(false)}, + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheThresholdFilterIndexes: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheRouteFilterIndexes: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheAttributeFilterIndexes: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheChargerFilterIndexes: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheDispatcherFilterIndexes: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheReverseFilterIndexes: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheDispatcherRoutes: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheDispatcherLoads: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheDispatchers: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheDiameterMessages: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer("3h"), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheRPCResponses: {Limit: utils.IntPointer(0), Ttl: utils.StringPointer("2s"), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheClosedSessions: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer("10s"), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheEventCharges: {Limit: utils.IntPointer(0), Ttl: utils.StringPointer("10s"), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheCDRIDs: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer("10m"), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheLoadIDs: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Precache: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, + Precache: utils.BoolPointer(false), Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheRPCConnections: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheUCH: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer("3h"), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheSTIR: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer("3h"), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheCapsEvents: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false), + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false), }, utils.MetaAPIBan: {Limit: utils.IntPointer(-1), Ttl: utils.StringPointer("2m"), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, utils.CacheReplicationHosts: {Limit: utils.IntPointer(0), Ttl: utils.StringPointer(""), Static_ttl: utils.BoolPointer(false), - Replicate: utils.BoolPointer(false)}, + Remote: utils.BoolPointer(false), Replicate: utils.BoolPointer(false)}, }, Replication_conns: &[]string{}, + Remote_conns: &[]string{}, } dfCgrJSONCfg, err := NewCgrJsonCfgFromBytes([]byte(CGRATES_CFG_JSON)) if err != nil { diff --git a/config/config_test.go b/config/config_test.go index 39b1564d2..7a15f711c 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -528,99 +528,100 @@ func TestCgrCfgJSONDefaultsCacheCFG(t *testing.T) { eCacheCfg := &CacheCfg{ Partitions: map[string]*CacheParamCfg{ utils.CacheDestinations: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheReverseDestinations: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheRatingPlans: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheRatingProfiles: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheActions: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheActionPlans: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheAccountActionPlans: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheActionTriggers: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheSharedGroups: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheTimings: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheResourceProfiles: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheResources: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheEventResources: {Limit: -1, - TTL: 0, StaticTTL: false}, + TTL: 0, Remote: false, StaticTTL: false}, utils.CacheStatQueueProfiles: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheStatQueues: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheThresholdProfiles: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheThresholds: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheFilters: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheRouteProfiles: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheAttributeProfiles: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheChargerProfiles: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheDispatcherProfiles: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheDispatcherHosts: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheResourceFilterIndexes: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheStatFilterIndexes: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheThresholdFilterIndexes: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheRouteFilterIndexes: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheAttributeFilterIndexes: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheChargerFilterIndexes: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheDispatcherFilterIndexes: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheReverseFilterIndexes: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheDispatcherRoutes: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheDispatcherLoads: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheDispatchers: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheDiameterMessages: {Limit: -1, - TTL: 3 * time.Hour, StaticTTL: false}, + TTL: 3 * time.Hour, Remote: false, StaticTTL: false}, utils.CacheRPCResponses: {Limit: 0, - TTL: 2 * time.Second, StaticTTL: false}, + TTL: 2 * time.Second, Remote: false, StaticTTL: false}, utils.CacheClosedSessions: {Limit: -1, - TTL: 10 * time.Second, StaticTTL: false}, + TTL: 10 * time.Second, Remote: false, StaticTTL: false}, utils.CacheEventCharges: {Limit: 0, - TTL: 10 * time.Second, StaticTTL: false}, + TTL: 10 * time.Second, Remote: false, StaticTTL: false}, utils.CacheCDRIDs: {Limit: -1, - TTL: 10 * time.Minute, StaticTTL: false}, + TTL: 10 * time.Minute, Remote: false, StaticTTL: false}, utils.CacheLoadIDs: {Limit: -1, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, utils.CacheRPCConnections: {Limit: -1, - TTL: 0, StaticTTL: false}, + TTL: 0, Remote: false, StaticTTL: false}, utils.CacheUCH: {Limit: -1, - TTL: 3 * time.Hour, StaticTTL: false}, + TTL: 3 * time.Hour, Remote: false, StaticTTL: false}, utils.CacheSTIR: {Limit: -1, - TTL: 3 * time.Hour, StaticTTL: false}, + TTL: 3 * time.Hour, Remote: false, StaticTTL: false}, utils.CacheCapsEvents: {Limit: -1}, utils.MetaAPIBan: {Limit: -1, - TTL: 2 * time.Minute, StaticTTL: false, Precache: false}, + TTL: 2 * time.Minute, Remote: false, StaticTTL: false, Precache: false}, utils.CacheReplicationHosts: {Limit: 0, - TTL: 0, StaticTTL: false, Precache: false}, + TTL: 0, Remote: false, StaticTTL: false, Precache: false}, }, ReplicationConns: []string{}, + RemoteConns: []string{}, } if !reflect.DeepEqual(eCacheCfg, cgrCfg.CacheCfg()) { @@ -3679,6 +3680,7 @@ func TestV1GetConfigCache(t *testing.T) { expected := map[string]interface{}{ CACHE_JSN: map[string]interface{}{ utils.PartitionsCfg: map[string]interface{}{}, + utils.RemoteConnsCfg: []string{}, utils.ReplicationConnsCfg: []string{}, }, } @@ -4379,6 +4381,7 @@ func TestV1GetConfigSectionEES(t *testing.T) { utils.LimitCfg: -1, utils.PrecacheCfg: false, utils.ReplicateCfg: false, + utils.RemoteCfg: false, utils.TTLCfg: "5s", utils.StaticTTLCfg: false, }, @@ -4791,7 +4794,7 @@ func TestV1GetConfigAsJSONTls(t *testing.T) { func TestV1GetConfigAsJSONTCache(t *testing.T) { var reply string - expected := `{"caches":{"partitions":{"*account_action_plans":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*action_plans":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*action_triggers":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*actions":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*apiban":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false,"ttl":"2m0s"},"*attribute_filter_indexes":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*attribute_profiles":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*caps_events":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*cdr_ids":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false,"ttl":"10m0s"},"*charger_filter_indexes":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*charger_profiles":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*closed_sessions":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false,"ttl":"10s"},"*destinations":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*diameter_messages":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*dispatcher_filter_indexes":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*dispatcher_hosts":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*dispatcher_loads":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*dispatcher_profiles":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*dispatcher_routes":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*dispatchers":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*event_charges":{"limit":0,"precache":false,"replicate":false,"static_ttl":false,"ttl":"10s"},"*event_resources":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*filters":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*load_ids":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*rating_plans":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*rating_profiles":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*replication_hosts":{"limit":0,"precache":false,"replicate":false,"static_ttl":false},"*resource_filter_indexes":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*resource_profiles":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*resources":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*reverse_destinations":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*reverse_filter_indexes":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*route_filter_indexes":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*route_profiles":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*rpc_connections":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*rpc_responses":{"limit":0,"precache":false,"replicate":false,"static_ttl":false,"ttl":"2s"},"*shared_groups":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*stat_filter_indexes":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*statqueue_profiles":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*statqueues":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*stir":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*threshold_filter_indexes":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*threshold_profiles":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*thresholds":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*timings":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*uch":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"}},"replication_conns":[]}}` + expected := `{"caches":{"partitions":{"*account_action_plans":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*action_plans":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*action_triggers":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*actions":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*apiban":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"2m0s"},"*attribute_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*attribute_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*caps_events":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*cdr_ids":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"10m0s"},"*charger_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*charger_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*closed_sessions":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"10s"},"*destinations":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*diameter_messages":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*dispatcher_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_hosts":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_loads":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_routes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatchers":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*event_charges":{"limit":0,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"10s"},"*event_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},"*rating_plans":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rating_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*replication_hosts":{"limit":0,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*resource_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*resource_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*resources":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*reverse_destinations":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*reverse_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*route_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*route_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rpc_connections":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rpc_responses":{"limit":0,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"2s"},"*shared_groups":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*stat_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*statqueue_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*statqueues":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*stir":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*threshold_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*threshold_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*thresholds":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*timings":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*uch":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"}},"remote_conns":[],"replication_conns":[]}}` cfgCgr := NewDefaultCGRConfig() if err := cfgCgr.V1GetConfigAsJSON(&SectionWithAPIOpts{Section: CACHE_JSN}, &reply); err != nil { t.Error(err) @@ -5090,7 +5093,7 @@ func TestV1GetConfigAsJSONApierS(t *testing.T) { func TestV1GetConfigAsJSONCfgEES(t *testing.T) { var reply string - expected := `{"ees":{"attributes_conns":[],"cache":{"*file_csv":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false,"ttl":"5s"}},"enabled":false,"exporters":[{"attempts":1,"attribute_context":"","attribute_ids":[],"concurrent_requests":0,"export_path":"/var/spool/cgrates/ees","failed_posts_dir":"/var/spool/cgrates/failed_posts","fields":[],"filters":[],"flags":[],"id":"*default","opts":{},"synchronous":false,"timezone":"","type":"*none"}]}}` + expected := `{"ees":{"attributes_conns":[],"cache":{"*file_csv":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"}},"enabled":false,"exporters":[{"attempts":1,"attribute_context":"","attribute_ids":[],"concurrent_requests":0,"export_path":"/var/spool/cgrates/ees","failed_posts_dir":"/var/spool/cgrates/failed_posts","fields":[],"filters":[],"flags":[],"id":"*default","opts":{},"synchronous":false,"timezone":"","type":"*none"}]}}` cgrCfg := NewDefaultCGRConfig() if err := cgrCfg.V1GetConfigAsJSON(&SectionWithAPIOpts{Section: EEsJson}, &reply); err != nil { t.Error(err) @@ -5270,7 +5273,7 @@ func TestV1GetConfigAsJSONAllConfig(t *testing.T) { }` var reply string cgrCfg, err := NewCGRConfigFromJSONStringWithDefaults(cfgJSON) - expected := `{"analyzers":{"cleanup_interval":"1h0m0s","db_path":"/var/spool/cgrates/analyzers","enabled":false,"index_type":"*scorch","ttl":"24h0m0s"},"apiban":{"enabled":false,"keys":[]},"apiers":{"attributes_conns":[],"caches_conns":["*internal"],"ees_conns":[],"enabled":false,"scheduler_conns":[]},"asterisk_agent":{"asterisk_conns":[{"address":"127.0.0.1:8088","alias":"","connect_attempts":3,"max_reconnect_interval":"0s","password":"CGRateS.org","reconnects":5,"user":"cgrates"}],"create_cdr":false,"enabled":false,"sessions_conns":["*birpc_internal"]},"attributes":{"any_context":true,"apiers_conns":[],"enabled":false,"indexed_selects":true,"nested_fields":false,"opts":{"*processRuns":1,"*profileIDs":[],"*profileIgnoreFilters":false,"*profileRuns":0},"prefix_indexed_fields":[],"resources_conns":[],"stats_conns":[],"suffix_indexed_fields":[]},"caches":{"partitions":{"*account_action_plans":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*action_plans":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*action_triggers":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*actions":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*apiban":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false,"ttl":"2m0s"},"*attribute_filter_indexes":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*attribute_profiles":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*caps_events":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*cdr_ids":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false,"ttl":"10m0s"},"*charger_filter_indexes":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*charger_profiles":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*closed_sessions":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false,"ttl":"10s"},"*destinations":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*diameter_messages":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*dispatcher_filter_indexes":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*dispatcher_hosts":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*dispatcher_loads":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*dispatcher_profiles":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*dispatcher_routes":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*dispatchers":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*event_charges":{"limit":0,"precache":false,"replicate":false,"static_ttl":false,"ttl":"10s"},"*event_resources":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*filters":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*load_ids":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*rating_plans":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*rating_profiles":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*replication_hosts":{"limit":0,"precache":false,"replicate":false,"static_ttl":false},"*resource_filter_indexes":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*resource_profiles":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*resources":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*reverse_destinations":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*reverse_filter_indexes":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*route_filter_indexes":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*route_profiles":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*rpc_connections":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*rpc_responses":{"limit":0,"precache":false,"replicate":false,"static_ttl":false,"ttl":"2s"},"*shared_groups":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*stat_filter_indexes":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*statqueue_profiles":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*statqueues":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*stir":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*threshold_filter_indexes":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*threshold_profiles":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*thresholds":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*timings":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false},"*uch":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"}},"replication_conns":[]},"cdrs":{"attributes_conns":[],"chargers_conns":[],"ees_conns":[],"enabled":false,"extra_fields":[],"online_cdr_exports":[],"rals_conns":[],"scheduler_conns":[],"session_cost_retries":5,"stats_conns":[],"store_cdrs":true,"thresholds_conns":[]},"chargers":{"attributes_conns":[],"enabled":false,"indexed_selects":true,"nested_fields":false,"prefix_indexed_fields":[],"suffix_indexed_fields":[]},"configs":{"enabled":false,"root_dir":"/var/spool/cgrates/configs","url":"/configs/"},"cores":{"caps":0,"caps_stats_interval":"0","caps_strategy":"*busy","shutdown_timeout":"1s"},"data_db":{"db_host":"127.0.0.1","db_name":"10","db_password":"","db_port":6379,"db_type":"*redis","db_user":"cgrates","items":{"*account_action_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*accounts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*action_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*action_triggers":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*actions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*attribute_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*attribute_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*charger_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*charger_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*destinations":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_hosts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*filters":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*load_ids":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rating_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rating_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*resource_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*resource_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*resources":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*reverse_destinations":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*reverse_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*route_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*route_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*shared_groups":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*stat_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*statqueue_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*statqueues":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*threshold_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*threshold_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*thresholds":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*timings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*versions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false}},"opts":{"mongoQueryTimeout":"10s","redisCACertificate":"","redisClientCertificate":"","redisClientKey":"","redisCluster":false,"redisClusterOndownDelay":"0s","redisClusterSync":"5s","redisConnectAttempts":20,"redisConnectTimeout":"0s","redisMaxConns":10,"redisReadTimeout":"0s","redisSentinel":"","redisTLS":false,"redisWriteTimeout":"0s"},"remote_conn_id":"","remote_conns":[],"replication_cache":"","replication_conns":[],"replication_filtered":false},"diameter_agent":{"asr_template":"","concurrent_requests":-1,"dictionaries_path":"/usr/share/cgrates/diameter/dict/","enabled":false,"forced_disconnect":"*none","listen":"127.0.0.1:3868","listen_net":"tcp","origin_host":"CGR-DA","origin_realm":"cgrates.org","product_name":"CGRateS","rar_template":"","request_processors":[],"sessions_conns":["*birpc_internal"],"synced_conn_requests":false,"vendor_id":0},"dispatchers":{"any_subsystem":true,"attributes_conns":[],"enabled":false,"indexed_selects":true,"nested_fields":false,"prefix_indexed_fields":[],"suffix_indexed_fields":[]},"dns_agent":{"enabled":false,"listen":"127.0.0.1:2053","listen_net":"udp","request_processors":[],"sessions_conns":["*internal"],"timezone":""},"ees":{"attributes_conns":[],"cache":{"*file_csv":{"limit":-1,"precache":false,"replicate":false,"static_ttl":false,"ttl":"5s"}},"enabled":false,"exporters":[{"attempts":1,"attribute_context":"","attribute_ids":[],"concurrent_requests":0,"export_path":"/var/spool/cgrates/ees","failed_posts_dir":"/var/spool/cgrates/failed_posts","fields":[],"filters":[],"flags":[],"id":"*default","opts":{},"synchronous":false,"timezone":"","type":"*none"}]},"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","opts":{"csvFieldSeparator":",","csvHeaderDefineChar":":","csvRowLength":0,"natsSubject":"cgrates_cdrs","partialCacheAction":"*none","partialOrderField":"~*req.AnswerTime","xmlRootPath":""},"partial_commit_fields":[],"processed_path":"/var/spool/cgrates/ers/out","run_delay":"0","source_path":"/var/spool/cgrates/ers/in","tenant":"","timezone":"","type":"*none"}],"sessions_conns":["*internal"]},"filters":{"apiers_conns":[],"resources_conns":[],"stats_conns":[]},"freeswitch_agent":{"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}],"extra_fields":"","low_balance_ann_file":"","max_wait_connection":"2s","sessions_conns":["*birpc_internal"],"subscribe_park":true},"general":{"connect_attempts":5,"connect_timeout":"1s","dbdata_encoding":"*msgpack","default_caching":"*reload","default_category":"call","default_request_type":"*rated","default_tenant":"cgrates.org","default_timezone":"Local","digest_equal":":","digest_separator":",","failed_posts_dir":"/var/spool/cgrates/failed_posts","failed_posts_ttl":"5s","locking_timeout":"0","log_level":6,"logger":"*syslog","max_parallel_conns":100,"max_reconnect_interval":"0","node_id":"ENGINE1","poster_attempts":3,"reconnects":-1,"reply_timeout":"2s","rounding_decimals":5,"rsr_separator":";","tpexport_dir":"/var/spool/cgrates/tpe"},"http":{"auth_users":{},"client_opts":{"dialFallbackDelay":"300ms","dialKeepAlive":"30s","dialTimeout":"30s","disableCompression":false,"disableKeepAlives":false,"expectContinueTimeout":"0s","forceAttemptHttp2":true,"idleConnTimeout":"1m30s","maxConnsPerHost":0,"maxIdleConns":100,"maxIdleConnsPerHost":2,"responseHeaderTimeout":"0s","skipTlsVerify":false,"tlsHandshakeTimeout":"10s"},"freeswitch_cdrs_url":"/freeswitch_json","http_cdrs":"/cdr_http","json_rpc_url":"/jsonrpc","registrars_url":"/registrar","use_basic_auth":false,"ws_url":"/ws"},"http_agent":[],"kamailio_agent":{"create_cdr":false,"enabled":false,"evapi_conns":[{"address":"127.0.0.1:8448","alias":"","max_reconnect_interval":"0s","reconnects":5}],"sessions_conns":["*birpc_internal"],"timezone":""},"listen":{"http":"127.0.0.1:2080","http_tls":"127.0.0.1:2280","rpc_gob":"127.0.0.1:2013","rpc_gob_tls":"127.0.0.1:2023","rpc_json":"127.0.0.1:2012","rpc_json_tls":"127.0.0.1:2022"},"loader":{"caches_conns":["*localhost"],"data_path":"./","disable_reverse":false,"field_separator":",","gapi_credentials":".gapi/credentials.json","gapi_token":".gapi/token.json","scheduler_conns":["*localhost"],"tpid":""},"loaders":[{"caches_conns":["*internal"],"data":[{"fields":[{"mandatory":true,"path":"Tenant","tag":"TenantID","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ProfileID","type":"*variable","value":"~*req.1"},{"path":"Contexts","tag":"Contexts","type":"*variable","value":"~*req.2"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.3"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.4"},{"path":"AttributeFilterIDs","tag":"AttributeFilterIDs","type":"*variable","value":"~*req.5"},{"path":"Path","tag":"Path","type":"*variable","value":"~*req.6"},{"path":"Type","tag":"Type","type":"*variable","value":"~*req.7"},{"path":"Value","tag":"Value","type":"*variable","value":"~*req.8"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.9"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.10"}],"file_name":"Attributes.csv","flags":null,"type":"*attributes"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Type","tag":"Type","type":"*variable","value":"~*req.2"},{"path":"Element","tag":"Element","type":"*variable","value":"~*req.3"},{"path":"Values","tag":"Values","type":"*variable","value":"~*req.4"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.5"}],"file_name":"Filters.csv","flags":null,"type":"*filters"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"UsageTTL","tag":"TTL","type":"*variable","value":"~*req.4"},{"path":"Limit","tag":"Limit","type":"*variable","value":"~*req.5"},{"path":"AllocationMessage","tag":"AllocationMessage","type":"*variable","value":"~*req.6"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.7"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.8"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.9"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.10"}],"file_name":"Resources.csv","flags":null,"type":"*resources"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"QueueLength","tag":"QueueLength","type":"*variable","value":"~*req.4"},{"path":"TTL","tag":"TTL","type":"*variable","value":"~*req.5"},{"path":"MinItems","tag":"MinItems","type":"*variable","value":"~*req.6"},{"path":"MetricIDs","tag":"MetricIDs","type":"*variable","value":"~*req.7"},{"path":"MetricFilterIDs","tag":"MetricFilterIDs","type":"*variable","value":"~*req.8"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.9"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.10"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.11"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.12"}],"file_name":"Stats.csv","flags":null,"type":"*stats"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"MaxHits","tag":"MaxHits","type":"*variable","value":"~*req.4"},{"path":"MinHits","tag":"MinHits","type":"*variable","value":"~*req.5"},{"path":"MinSleep","tag":"MinSleep","type":"*variable","value":"~*req.6"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.7"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.8"},{"path":"ActionIDs","tag":"ActionIDs","type":"*variable","value":"~*req.9"},{"path":"Async","tag":"Async","type":"*variable","value":"~*req.10"}],"file_name":"Thresholds.csv","flags":null,"type":"*thresholds"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"Sorting","tag":"Sorting","type":"*variable","value":"~*req.4"},{"path":"SortingParameters","tag":"SortingParameters","type":"*variable","value":"~*req.5"},{"path":"RouteID","tag":"RouteID","type":"*variable","value":"~*req.6"},{"path":"RouteFilterIDs","tag":"RouteFilterIDs","type":"*variable","value":"~*req.7"},{"path":"RouteAccountIDs","tag":"RouteAccountIDs","type":"*variable","value":"~*req.8"},{"path":"RouteRatingPlanIDs","tag":"RouteRatingPlanIDs","type":"*variable","value":"~*req.9"},{"path":"RouteResourceIDs","tag":"RouteResourceIDs","type":"*variable","value":"~*req.10"},{"path":"RouteStatIDs","tag":"RouteStatIDs","type":"*variable","value":"~*req.11"},{"path":"RouteWeight","tag":"RouteWeight","type":"*variable","value":"~*req.12"},{"path":"RouteBlocker","tag":"RouteBlocker","type":"*variable","value":"~*req.13"},{"path":"RouteParameters","tag":"RouteParameters","type":"*variable","value":"~*req.14"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.15"}],"file_name":"Routes.csv","flags":null,"type":"*routes"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"RunID","tag":"RunID","type":"*variable","value":"~*req.4"},{"path":"AttributeIDs","tag":"AttributeIDs","type":"*variable","value":"~*req.5"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.6"}],"file_name":"Chargers.csv","flags":null,"type":"*chargers"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Contexts","tag":"Contexts","type":"*variable","value":"~*req.2"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.3"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.4"},{"path":"Strategy","tag":"Strategy","type":"*variable","value":"~*req.5"},{"path":"StrategyParameters","tag":"StrategyParameters","type":"*variable","value":"~*req.6"},{"path":"ConnID","tag":"ConnID","type":"*variable","value":"~*req.7"},{"path":"ConnFilterIDs","tag":"ConnFilterIDs","type":"*variable","value":"~*req.8"},{"path":"ConnWeight","tag":"ConnWeight","type":"*variable","value":"~*req.9"},{"path":"ConnBlocker","tag":"ConnBlocker","type":"*variable","value":"~*req.10"},{"path":"ConnParameters","tag":"ConnParameters","type":"*variable","value":"~*req.11"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.12"}],"file_name":"DispatcherProfiles.csv","flags":null,"type":"*dispatchers"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Address","tag":"Address","type":"*variable","value":"~*req.2"},{"path":"Transport","tag":"Transport","type":"*variable","value":"~*req.3"},{"path":"ConnectAttempts","tag":"ConnectAttempts","type":"*variable","value":"~*req.4"},{"path":"Reconnects","tag":"Reconnects","type":"*variable","value":"~*req.5"},{"path":"MaxReconnectInterval","tag":"MaxReconnectInterval","type":"*variable","value":"~*req.6"},{"path":"ConnectTimeout","tag":"ConnectTimeout","type":"*variable","value":"~*req.7"},{"path":"ReplyTimeout","tag":"ReplyTimeout","type":"*variable","value":"~*req.8"},{"path":"TLS","tag":"TLS","type":"*variable","value":"~*req.9"},{"path":"ClientKey","tag":"ClientKey","type":"*variable","value":"~*req.10"},{"path":"ClientCertificate","tag":"ClientCertificate","type":"*variable","value":"~*req.11"},{"path":"CaCertificate","tag":"CaCertificate","type":"*variable","value":"~*req.12"}],"file_name":"DispatcherHosts.csv","flags":null,"type":"*dispatcher_hosts"}],"dry_run":false,"enabled":false,"field_separator":",","id":"*default","lockfile_path":".cgr.lck","run_delay":"0","tenant":"","tp_in_dir":"/var/spool/cgrates/loader/in","tp_out_dir":"/var/spool/cgrates/loader/out"}],"mailer":{"auth_password":"CGRateS.org","auth_user":"cgrates","from_address":"cgr-mailer@localhost.localdomain","server":"localhost"},"migrator":{"out_datadb_encoding":"msgpack","out_datadb_host":"127.0.0.1","out_datadb_name":"10","out_datadb_opts":{"mongoQueryTimeout":"0s","redisCACertificate":"","redisClientCertificate":"","redisClientKey":"","redisCluster":false,"redisClusterOndownDelay":"0s","redisClusterSync":"5s","redisConnectAttempts":20,"redisConnectTimeout":"0s","redisMaxConns":10,"redisReadTimeout":"0s","redisSentinel":"","redisTLS":false,"redisWriteTimeout":"0s"},"out_datadb_password":"","out_datadb_port":"6379","out_datadb_type":"redis","out_datadb_user":"cgrates","out_stordb_host":"127.0.0.1","out_stordb_name":"cgrates","out_stordb_opts":{"mongoQueryTimeout":"0s","mysqlDSNParams":null,"mysqlLocation":"","pgSSLMode":"","sqlConnMaxLifetime":"0s","sqlMaxIdleConns":0,"sqlMaxOpenConns":0},"out_stordb_password":"","out_stordb_port":"3306","out_stordb_type":"mysql","out_stordb_user":"cgrates","users_filters":[]},"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"]},"rals":{"balance_rating_subject":{"*any":"*zero1ns","*voice":"*zero1s"},"enabled":false,"max_computed_usage":{"*any":"189h0m0s","*data":"107374182400","*mms":"10000","*sms":"10000","*voice":"72h0m0s"},"max_increments":1000000,"remove_expired":true,"rp_subject_prefix_matching":false,"stats_conns":[],"thresholds_conns":[]},"registrarc":{"dispatchers":{"hosts":[],"refresh_interval":"5m0s","registrars_conns":[]},"rpc":{"hosts":[],"refresh_interval":"5m0s","registrars_conns":[]}},"resources":{"enabled":false,"indexed_selects":true,"nested_fields":false,"opts":{"*units":1,"*usageID":""},"prefix_indexed_fields":[],"store_interval":"","suffix_indexed_fields":[],"thresholds_conns":[]},"routes":{"attributes_conns":[],"default_ratio":1,"enabled":false,"indexed_selects":true,"nested_fields":false,"opts":{"*context":"*routes","*ignoreErrors":false,"*maxCost":""},"prefix_indexed_fields":[],"rals_conns":[],"resources_conns":[],"stats_conns":[],"suffix_indexed_fields":[]},"rpc_conns":{"*bijson_localhost":{"conns":[{"address":"127.0.0.1:2014","transport":"*birpc_json"}],"poolSize":0,"strategy":"*first"},"*birpc_internal":{"conns":[{"address":"*birpc_internal","transport":""}],"poolSize":0,"strategy":"*first"},"*internal":{"conns":[{"address":"*internal","transport":""}],"poolSize":0,"strategy":"*first"},"*localhost":{"conns":[{"address":"127.0.0.1:2012","transport":"*json"}],"poolSize":0,"strategy":"*first"}},"schedulers":{"cdrs_conns":[],"dynaprepaid_actionplans":[],"enabled":false,"filters":[],"stats_conns":[],"thresholds_conns":[]},"sessions":{"alterable_fields":[],"attributes_conns":[],"cdrs_conns":[],"channel_sync_interval":"0","chargers_conns":[],"client_protocol":1,"debit_interval":"0","default_usage":{"*any":"3h0m0s","*data":"1048576","*sms":"1","*voice":"3h0m0s"},"enabled":false,"listen_bigob":"","listen_bijson":"127.0.0.1:2014","min_dur_low_balance":"0","rals_conns":[],"replication_conns":[],"resources_conns":[],"routes_conns":[],"scheduler_conns":[],"session_indexes":[],"session_ttl":"0","stats_conns":[],"stir":{"allowed_attest":["*any"],"default_attest":"A","payload_maxduration":"-1","privatekey_path":"","publickey_path":""},"store_session_costs":false,"terminate_attempts":5,"thresholds_conns":[]},"sip_agent":{"enabled":false,"listen":"127.0.0.1:5060","listen_net":"udp","request_processors":[],"retransmission_timer":1000000000,"sessions_conns":["*internal"],"timezone":""},"stats":{"enabled":false,"indexed_selects":true,"nested_fields":false,"opts":{"*profileIDs":[],"*profileIgnoreFilters":false},"prefix_indexed_fields":[],"store_interval":"","store_uncompressed_limit":0,"suffix_indexed_fields":[],"thresholds_conns":[]},"stor_db":{"db_host":"127.0.0.1","db_name":"cgrates","db_password":"","db_port":3306,"db_type":"*mysql","db_user":"cgrates","items":{"*cdrs":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*session_costs":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_account_actions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_action_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_action_triggers":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_actions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_attributes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_chargers":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_destination_rates":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_destinations":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_dispatcher_hosts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_dispatcher_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_filters":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rates":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rating_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rating_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_resources":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_routes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_shared_groups":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_stats":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_thresholds":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_timings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*versions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false}},"opts":{"mongoQueryTimeout":"10s","mysqlDSNParams":{},"mysqlLocation":"Local","pgSSLMode":"disable","sqlConnMaxLifetime":"0s","sqlMaxIdleConns":10,"sqlMaxOpenConns":100},"prefix_indexed_fields":[],"remote_conns":null,"replication_conns":null,"string_indexed_fields":[]},"suretax":{"bill_to_number":"","business_unit":"","client_number":"","client_tracking":"~*req.CGRID","customer_number":"~*req.Subject","include_local_cost":false,"orig_number":"~*req.Subject","p2pplus4":"","p2pzipcode":"","plus4":"","regulatory_code":"03","response_group":"03","response_type":"D4","return_file_code":"0","sales_type_code":"R","tax_exemption_code_list":"","tax_included":"0","tax_situs_rule":"04","term_number":"~*req.Destination","timezone":"UTC","trans_type_code":"010101","unit_type":"00","units":"1","url":"","validation_key":"","zipcode":""},"templates":{"*asr":[{"mandatory":true,"path":"*diamreq.Session-Id","tag":"SessionId","type":"*variable","value":"~*req.Session-Id"},{"mandatory":true,"path":"*diamreq.Origin-Host","tag":"OriginHost","type":"*variable","value":"~*req.Destination-Host"},{"mandatory":true,"path":"*diamreq.Origin-Realm","tag":"OriginRealm","type":"*variable","value":"~*req.Destination-Realm"},{"mandatory":true,"path":"*diamreq.Destination-Realm","tag":"DestinationRealm","type":"*variable","value":"~*req.Origin-Realm"},{"mandatory":true,"path":"*diamreq.Destination-Host","tag":"DestinationHost","type":"*variable","value":"~*req.Origin-Host"},{"mandatory":true,"path":"*diamreq.Auth-Application-Id","tag":"AuthApplicationId","type":"*variable","value":"~*vars.*appid"}],"*cca":[{"mandatory":true,"path":"*rep.Session-Id","tag":"SessionId","type":"*variable","value":"~*req.Session-Id"},{"path":"*rep.Result-Code","tag":"ResultCode","type":"*constant","value":"2001"},{"mandatory":true,"path":"*rep.Origin-Host","tag":"OriginHost","type":"*variable","value":"~*vars.OriginHost"},{"mandatory":true,"path":"*rep.Origin-Realm","tag":"OriginRealm","type":"*variable","value":"~*vars.OriginRealm"},{"mandatory":true,"path":"*rep.Auth-Application-Id","tag":"AuthApplicationId","type":"*variable","value":"~*vars.*appid"},{"mandatory":true,"path":"*rep.CC-Request-Type","tag":"CCRequestType","type":"*variable","value":"~*req.CC-Request-Type"},{"mandatory":true,"path":"*rep.CC-Request-Number","tag":"CCRequestNumber","type":"*variable","value":"~*req.CC-Request-Number"}],"*cdrLog":[{"mandatory":true,"path":"*cdr.ToR","tag":"ToR","type":"*variable","value":"~*req.BalanceType"},{"mandatory":true,"path":"*cdr.OriginHost","tag":"OriginHost","type":"*constant","value":"127.0.0.1"},{"mandatory":true,"path":"*cdr.RequestType","tag":"RequestType","type":"*constant","value":"*none"},{"mandatory":true,"path":"*cdr.Tenant","tag":"Tenant","type":"*variable","value":"~*req.Tenant"},{"mandatory":true,"path":"*cdr.Account","tag":"Account","type":"*variable","value":"~*req.Account"},{"mandatory":true,"path":"*cdr.Subject","tag":"Subject","type":"*variable","value":"~*req.Account"},{"mandatory":true,"path":"*cdr.Cost","tag":"Cost","type":"*variable","value":"~*req.Cost"},{"mandatory":true,"path":"*cdr.Source","tag":"Source","type":"*constant","value":"*cdrLog"},{"mandatory":true,"path":"*cdr.Usage","tag":"Usage","type":"*constant","value":"1"},{"mandatory":true,"path":"*cdr.RunID","tag":"RunID","type":"*variable","value":"~*req.ActionType"},{"mandatory":true,"path":"*cdr.SetupTime","tag":"SetupTime","type":"*constant","value":"*now"},{"mandatory":true,"path":"*cdr.AnswerTime","tag":"AnswerTime","type":"*constant","value":"*now"},{"mandatory":true,"path":"*cdr.PreRated","tag":"PreRated","type":"*constant","value":"true"}],"*err":[{"mandatory":true,"path":"*rep.Session-Id","tag":"SessionId","type":"*variable","value":"~*req.Session-Id"},{"mandatory":true,"path":"*rep.Origin-Host","tag":"OriginHost","type":"*variable","value":"~*vars.OriginHost"},{"mandatory":true,"path":"*rep.Origin-Realm","tag":"OriginRealm","type":"*variable","value":"~*vars.OriginRealm"}],"*errSip":[{"mandatory":true,"path":"*rep.Request","tag":"Request","type":"*constant","value":"SIP/2.0 500 Internal Server Error"}],"*rar":[{"mandatory":true,"path":"*diamreq.Session-Id","tag":"SessionId","type":"*variable","value":"~*req.Session-Id"},{"mandatory":true,"path":"*diamreq.Origin-Host","tag":"OriginHost","type":"*variable","value":"~*req.Destination-Host"},{"mandatory":true,"path":"*diamreq.Origin-Realm","tag":"OriginRealm","type":"*variable","value":"~*req.Destination-Realm"},{"mandatory":true,"path":"*diamreq.Destination-Realm","tag":"DestinationRealm","type":"*variable","value":"~*req.Origin-Realm"},{"mandatory":true,"path":"*diamreq.Destination-Host","tag":"DestinationHost","type":"*variable","value":"~*req.Origin-Host"},{"mandatory":true,"path":"*diamreq.Auth-Application-Id","tag":"AuthApplicationId","type":"*variable","value":"~*vars.*appid"},{"path":"*diamreq.Re-Auth-Request-Type","tag":"ReAuthRequestType","type":"*constant","value":"0"}]},"thresholds":{"enabled":false,"indexed_selects":true,"nested_fields":false,"opts":{"*profileIDs":[],"*profileIgnoreFilters":false},"prefix_indexed_fields":[],"store_interval":"","suffix_indexed_fields":[]},"tls":{"ca_certificate":"","client_certificate":"","client_key":"","server_certificate":"","server_key":"","server_name":"","server_policy":4}}` + expected := `{"analyzers":{"cleanup_interval":"1h0m0s","db_path":"/var/spool/cgrates/analyzers","enabled":false,"index_type":"*scorch","ttl":"24h0m0s"},"apiban":{"enabled":false,"keys":[]},"apiers":{"attributes_conns":[],"caches_conns":["*internal"],"ees_conns":[],"enabled":false,"scheduler_conns":[]},"asterisk_agent":{"asterisk_conns":[{"address":"127.0.0.1:8088","alias":"","connect_attempts":3,"max_reconnect_interval":"0s","password":"CGRateS.org","reconnects":5,"user":"cgrates"}],"create_cdr":false,"enabled":false,"sessions_conns":["*birpc_internal"]},"attributes":{"any_context":true,"apiers_conns":[],"enabled":false,"indexed_selects":true,"nested_fields":false,"opts":{"*processRuns":1,"*profileIDs":[],"*profileIgnoreFilters":false,"*profileRuns":0},"prefix_indexed_fields":[],"resources_conns":[],"stats_conns":[],"suffix_indexed_fields":[]},"caches":{"partitions":{"*account_action_plans":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*action_plans":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*action_triggers":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*actions":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*apiban":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"2m0s"},"*attribute_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*attribute_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*caps_events":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*cdr_ids":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"10m0s"},"*charger_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*charger_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*closed_sessions":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"10s"},"*destinations":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*diameter_messages":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*dispatcher_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_hosts":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_loads":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_routes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*dispatchers":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*event_charges":{"limit":0,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"10s"},"*event_resources":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*filters":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*load_ids":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rating_plans":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rating_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*replication_hosts":{"limit":0,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*resource_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*resource_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*resources":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*reverse_destinations":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*reverse_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*route_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*route_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rpc_connections":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*rpc_responses":{"limit":0,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"2s"},"*shared_groups":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*stat_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*statqueue_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*statqueues":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*stir":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"},"*threshold_filter_indexes":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*threshold_profiles":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*thresholds":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*timings":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false},"*uch":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"3h0m0s"}},"remote_conns":[],"replication_conns":[]},"cdrs":{"attributes_conns":[],"chargers_conns":[],"ees_conns":[],"enabled":false,"extra_fields":[],"online_cdr_exports":[],"rals_conns":[],"scheduler_conns":[],"session_cost_retries":5,"stats_conns":[],"store_cdrs":true,"thresholds_conns":[]},"chargers":{"attributes_conns":[],"enabled":false,"indexed_selects":true,"nested_fields":false,"prefix_indexed_fields":[],"suffix_indexed_fields":[]},"configs":{"enabled":false,"root_dir":"/var/spool/cgrates/configs","url":"/configs/"},"cores":{"caps":0,"caps_stats_interval":"0","caps_strategy":"*busy","shutdown_timeout":"1s"},"data_db":{"db_host":"127.0.0.1","db_name":"10","db_password":"","db_port":6379,"db_type":"*redis","db_user":"cgrates","items":{"*account_action_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*accounts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*action_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*action_triggers":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*actions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*attribute_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*attribute_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*charger_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*charger_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*destinations":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_hosts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*dispatcher_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*filters":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*load_ids":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rating_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*rating_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*resource_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*resource_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*resources":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*reverse_destinations":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*reverse_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*route_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*route_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*shared_groups":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*stat_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*statqueue_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*statqueues":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*threshold_filter_indexes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*threshold_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*thresholds":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*timings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*versions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false}},"opts":{"mongoQueryTimeout":"10s","redisCACertificate":"","redisClientCertificate":"","redisClientKey":"","redisCluster":false,"redisClusterOndownDelay":"0s","redisClusterSync":"5s","redisConnectAttempts":20,"redisConnectTimeout":"0s","redisMaxConns":10,"redisReadTimeout":"0s","redisSentinel":"","redisTLS":false,"redisWriteTimeout":"0s"},"remote_conn_id":"","remote_conns":[],"replication_cache":"","replication_conns":[],"replication_filtered":false},"diameter_agent":{"asr_template":"","concurrent_requests":-1,"dictionaries_path":"/usr/share/cgrates/diameter/dict/","enabled":false,"forced_disconnect":"*none","listen":"127.0.0.1:3868","listen_net":"tcp","origin_host":"CGR-DA","origin_realm":"cgrates.org","product_name":"CGRateS","rar_template":"","request_processors":[],"sessions_conns":["*birpc_internal"],"synced_conn_requests":false,"vendor_id":0},"dispatchers":{"any_subsystem":true,"attributes_conns":[],"enabled":false,"indexed_selects":true,"nested_fields":false,"prefix_indexed_fields":[],"suffix_indexed_fields":[]},"dns_agent":{"enabled":false,"listen":"127.0.0.1:2053","listen_net":"udp","request_processors":[],"sessions_conns":["*internal"],"timezone":""},"ees":{"attributes_conns":[],"cache":{"*file_csv":{"limit":-1,"precache":false,"remote":false,"replicate":false,"static_ttl":false,"ttl":"5s"}},"enabled":false,"exporters":[{"attempts":1,"attribute_context":"","attribute_ids":[],"concurrent_requests":0,"export_path":"/var/spool/cgrates/ees","failed_posts_dir":"/var/spool/cgrates/failed_posts","fields":[],"filters":[],"flags":[],"id":"*default","opts":{},"synchronous":false,"timezone":"","type":"*none"}]},"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","opts":{"csvFieldSeparator":",","csvHeaderDefineChar":":","csvRowLength":0,"natsSubject":"cgrates_cdrs","partialCacheAction":"*none","partialOrderField":"~*req.AnswerTime","xmlRootPath":""},"partial_commit_fields":[],"processed_path":"/var/spool/cgrates/ers/out","run_delay":"0","source_path":"/var/spool/cgrates/ers/in","tenant":"","timezone":"","type":"*none"}],"sessions_conns":["*internal"]},"filters":{"apiers_conns":[],"resources_conns":[],"stats_conns":[]},"freeswitch_agent":{"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}],"extra_fields":"","low_balance_ann_file":"","max_wait_connection":"2s","sessions_conns":["*birpc_internal"],"subscribe_park":true},"general":{"connect_attempts":5,"connect_timeout":"1s","dbdata_encoding":"*msgpack","default_caching":"*reload","default_category":"call","default_request_type":"*rated","default_tenant":"cgrates.org","default_timezone":"Local","digest_equal":":","digest_separator":",","failed_posts_dir":"/var/spool/cgrates/failed_posts","failed_posts_ttl":"5s","locking_timeout":"0","log_level":6,"logger":"*syslog","max_parallel_conns":100,"max_reconnect_interval":"0","node_id":"ENGINE1","poster_attempts":3,"reconnects":-1,"reply_timeout":"2s","rounding_decimals":5,"rsr_separator":";","tpexport_dir":"/var/spool/cgrates/tpe"},"http":{"auth_users":{},"client_opts":{"dialFallbackDelay":"300ms","dialKeepAlive":"30s","dialTimeout":"30s","disableCompression":false,"disableKeepAlives":false,"expectContinueTimeout":"0s","forceAttemptHttp2":true,"idleConnTimeout":"1m30s","maxConnsPerHost":0,"maxIdleConns":100,"maxIdleConnsPerHost":2,"responseHeaderTimeout":"0s","skipTlsVerify":false,"tlsHandshakeTimeout":"10s"},"freeswitch_cdrs_url":"/freeswitch_json","http_cdrs":"/cdr_http","json_rpc_url":"/jsonrpc","registrars_url":"/registrar","use_basic_auth":false,"ws_url":"/ws"},"http_agent":[],"kamailio_agent":{"create_cdr":false,"enabled":false,"evapi_conns":[{"address":"127.0.0.1:8448","alias":"","max_reconnect_interval":"0s","reconnects":5}],"sessions_conns":["*birpc_internal"],"timezone":""},"listen":{"http":"127.0.0.1:2080","http_tls":"127.0.0.1:2280","rpc_gob":"127.0.0.1:2013","rpc_gob_tls":"127.0.0.1:2023","rpc_json":"127.0.0.1:2012","rpc_json_tls":"127.0.0.1:2022"},"loader":{"caches_conns":["*localhost"],"data_path":"./","disable_reverse":false,"field_separator":",","gapi_credentials":".gapi/credentials.json","gapi_token":".gapi/token.json","scheduler_conns":["*localhost"],"tpid":""},"loaders":[{"caches_conns":["*internal"],"data":[{"fields":[{"mandatory":true,"path":"Tenant","tag":"TenantID","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ProfileID","type":"*variable","value":"~*req.1"},{"path":"Contexts","tag":"Contexts","type":"*variable","value":"~*req.2"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.3"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.4"},{"path":"AttributeFilterIDs","tag":"AttributeFilterIDs","type":"*variable","value":"~*req.5"},{"path":"Path","tag":"Path","type":"*variable","value":"~*req.6"},{"path":"Type","tag":"Type","type":"*variable","value":"~*req.7"},{"path":"Value","tag":"Value","type":"*variable","value":"~*req.8"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.9"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.10"}],"file_name":"Attributes.csv","flags":null,"type":"*attributes"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Type","tag":"Type","type":"*variable","value":"~*req.2"},{"path":"Element","tag":"Element","type":"*variable","value":"~*req.3"},{"path":"Values","tag":"Values","type":"*variable","value":"~*req.4"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.5"}],"file_name":"Filters.csv","flags":null,"type":"*filters"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"UsageTTL","tag":"TTL","type":"*variable","value":"~*req.4"},{"path":"Limit","tag":"Limit","type":"*variable","value":"~*req.5"},{"path":"AllocationMessage","tag":"AllocationMessage","type":"*variable","value":"~*req.6"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.7"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.8"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.9"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.10"}],"file_name":"Resources.csv","flags":null,"type":"*resources"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"QueueLength","tag":"QueueLength","type":"*variable","value":"~*req.4"},{"path":"TTL","tag":"TTL","type":"*variable","value":"~*req.5"},{"path":"MinItems","tag":"MinItems","type":"*variable","value":"~*req.6"},{"path":"MetricIDs","tag":"MetricIDs","type":"*variable","value":"~*req.7"},{"path":"MetricFilterIDs","tag":"MetricFilterIDs","type":"*variable","value":"~*req.8"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.9"},{"path":"Stored","tag":"Stored","type":"*variable","value":"~*req.10"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.11"},{"path":"ThresholdIDs","tag":"ThresholdIDs","type":"*variable","value":"~*req.12"}],"file_name":"Stats.csv","flags":null,"type":"*stats"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"MaxHits","tag":"MaxHits","type":"*variable","value":"~*req.4"},{"path":"MinHits","tag":"MinHits","type":"*variable","value":"~*req.5"},{"path":"MinSleep","tag":"MinSleep","type":"*variable","value":"~*req.6"},{"path":"Blocker","tag":"Blocker","type":"*variable","value":"~*req.7"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.8"},{"path":"ActionIDs","tag":"ActionIDs","type":"*variable","value":"~*req.9"},{"path":"Async","tag":"Async","type":"*variable","value":"~*req.10"}],"file_name":"Thresholds.csv","flags":null,"type":"*thresholds"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"Sorting","tag":"Sorting","type":"*variable","value":"~*req.4"},{"path":"SortingParameters","tag":"SortingParameters","type":"*variable","value":"~*req.5"},{"path":"RouteID","tag":"RouteID","type":"*variable","value":"~*req.6"},{"path":"RouteFilterIDs","tag":"RouteFilterIDs","type":"*variable","value":"~*req.7"},{"path":"RouteAccountIDs","tag":"RouteAccountIDs","type":"*variable","value":"~*req.8"},{"path":"RouteRatingPlanIDs","tag":"RouteRatingPlanIDs","type":"*variable","value":"~*req.9"},{"path":"RouteResourceIDs","tag":"RouteResourceIDs","type":"*variable","value":"~*req.10"},{"path":"RouteStatIDs","tag":"RouteStatIDs","type":"*variable","value":"~*req.11"},{"path":"RouteWeight","tag":"RouteWeight","type":"*variable","value":"~*req.12"},{"path":"RouteBlocker","tag":"RouteBlocker","type":"*variable","value":"~*req.13"},{"path":"RouteParameters","tag":"RouteParameters","type":"*variable","value":"~*req.14"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.15"}],"file_name":"Routes.csv","flags":null,"type":"*routes"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.2"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.3"},{"path":"RunID","tag":"RunID","type":"*variable","value":"~*req.4"},{"path":"AttributeIDs","tag":"AttributeIDs","type":"*variable","value":"~*req.5"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.6"}],"file_name":"Chargers.csv","flags":null,"type":"*chargers"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Contexts","tag":"Contexts","type":"*variable","value":"~*req.2"},{"path":"FilterIDs","tag":"FilterIDs","type":"*variable","value":"~*req.3"},{"path":"ActivationInterval","tag":"ActivationInterval","type":"*variable","value":"~*req.4"},{"path":"Strategy","tag":"Strategy","type":"*variable","value":"~*req.5"},{"path":"StrategyParameters","tag":"StrategyParameters","type":"*variable","value":"~*req.6"},{"path":"ConnID","tag":"ConnID","type":"*variable","value":"~*req.7"},{"path":"ConnFilterIDs","tag":"ConnFilterIDs","type":"*variable","value":"~*req.8"},{"path":"ConnWeight","tag":"ConnWeight","type":"*variable","value":"~*req.9"},{"path":"ConnBlocker","tag":"ConnBlocker","type":"*variable","value":"~*req.10"},{"path":"ConnParameters","tag":"ConnParameters","type":"*variable","value":"~*req.11"},{"path":"Weight","tag":"Weight","type":"*variable","value":"~*req.12"}],"file_name":"DispatcherProfiles.csv","flags":null,"type":"*dispatchers"},{"fields":[{"mandatory":true,"path":"Tenant","tag":"Tenant","type":"*variable","value":"~*req.0"},{"mandatory":true,"path":"ID","tag":"ID","type":"*variable","value":"~*req.1"},{"path":"Address","tag":"Address","type":"*variable","value":"~*req.2"},{"path":"Transport","tag":"Transport","type":"*variable","value":"~*req.3"},{"path":"ConnectAttempts","tag":"ConnectAttempts","type":"*variable","value":"~*req.4"},{"path":"Reconnects","tag":"Reconnects","type":"*variable","value":"~*req.5"},{"path":"MaxReconnectInterval","tag":"MaxReconnectInterval","type":"*variable","value":"~*req.6"},{"path":"ConnectTimeout","tag":"ConnectTimeout","type":"*variable","value":"~*req.7"},{"path":"ReplyTimeout","tag":"ReplyTimeout","type":"*variable","value":"~*req.8"},{"path":"TLS","tag":"TLS","type":"*variable","value":"~*req.9"},{"path":"ClientKey","tag":"ClientKey","type":"*variable","value":"~*req.10"},{"path":"ClientCertificate","tag":"ClientCertificate","type":"*variable","value":"~*req.11"},{"path":"CaCertificate","tag":"CaCertificate","type":"*variable","value":"~*req.12"}],"file_name":"DispatcherHosts.csv","flags":null,"type":"*dispatcher_hosts"}],"dry_run":false,"enabled":false,"field_separator":",","id":"*default","lockfile_path":".cgr.lck","run_delay":"0","tenant":"","tp_in_dir":"/var/spool/cgrates/loader/in","tp_out_dir":"/var/spool/cgrates/loader/out"}],"mailer":{"auth_password":"CGRateS.org","auth_user":"cgrates","from_address":"cgr-mailer@localhost.localdomain","server":"localhost"},"migrator":{"out_datadb_encoding":"msgpack","out_datadb_host":"127.0.0.1","out_datadb_name":"10","out_datadb_opts":{"mongoQueryTimeout":"0s","redisCACertificate":"","redisClientCertificate":"","redisClientKey":"","redisCluster":false,"redisClusterOndownDelay":"0s","redisClusterSync":"5s","redisConnectAttempts":20,"redisConnectTimeout":"0s","redisMaxConns":10,"redisReadTimeout":"0s","redisSentinel":"","redisTLS":false,"redisWriteTimeout":"0s"},"out_datadb_password":"","out_datadb_port":"6379","out_datadb_type":"redis","out_datadb_user":"cgrates","out_stordb_host":"127.0.0.1","out_stordb_name":"cgrates","out_stordb_opts":{"mongoQueryTimeout":"0s","mysqlDSNParams":null,"mysqlLocation":"","pgSSLMode":"","sqlConnMaxLifetime":"0s","sqlMaxIdleConns":0,"sqlMaxOpenConns":0},"out_stordb_password":"","out_stordb_port":"3306","out_stordb_type":"mysql","out_stordb_user":"cgrates","users_filters":[]},"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"]},"rals":{"balance_rating_subject":{"*any":"*zero1ns","*voice":"*zero1s"},"enabled":false,"max_computed_usage":{"*any":"189h0m0s","*data":"107374182400","*mms":"10000","*sms":"10000","*voice":"72h0m0s"},"max_increments":1000000,"remove_expired":true,"rp_subject_prefix_matching":false,"stats_conns":[],"thresholds_conns":[]},"registrarc":{"dispatchers":{"hosts":[],"refresh_interval":"5m0s","registrars_conns":[]},"rpc":{"hosts":[],"refresh_interval":"5m0s","registrars_conns":[]}},"resources":{"enabled":false,"indexed_selects":true,"nested_fields":false,"opts":{"*units":1,"*usageID":""},"prefix_indexed_fields":[],"store_interval":"","suffix_indexed_fields":[],"thresholds_conns":[]},"routes":{"attributes_conns":[],"default_ratio":1,"enabled":false,"indexed_selects":true,"nested_fields":false,"opts":{"*context":"*routes","*ignoreErrors":false,"*maxCost":""},"prefix_indexed_fields":[],"rals_conns":[],"resources_conns":[],"stats_conns":[],"suffix_indexed_fields":[]},"rpc_conns":{"*bijson_localhost":{"conns":[{"address":"127.0.0.1:2014","transport":"*birpc_json"}],"poolSize":0,"strategy":"*first"},"*birpc_internal":{"conns":[{"address":"*birpc_internal","transport":""}],"poolSize":0,"strategy":"*first"},"*internal":{"conns":[{"address":"*internal","transport":""}],"poolSize":0,"strategy":"*first"},"*localhost":{"conns":[{"address":"127.0.0.1:2012","transport":"*json"}],"poolSize":0,"strategy":"*first"}},"schedulers":{"cdrs_conns":[],"dynaprepaid_actionplans":[],"enabled":false,"filters":[],"stats_conns":[],"thresholds_conns":[]},"sessions":{"alterable_fields":[],"attributes_conns":[],"cdrs_conns":[],"channel_sync_interval":"0","chargers_conns":[],"client_protocol":1,"debit_interval":"0","default_usage":{"*any":"3h0m0s","*data":"1048576","*sms":"1","*voice":"3h0m0s"},"enabled":false,"listen_bigob":"","listen_bijson":"127.0.0.1:2014","min_dur_low_balance":"0","rals_conns":[],"replication_conns":[],"resources_conns":[],"routes_conns":[],"scheduler_conns":[],"session_indexes":[],"session_ttl":"0","stats_conns":[],"stir":{"allowed_attest":["*any"],"default_attest":"A","payload_maxduration":"-1","privatekey_path":"","publickey_path":""},"store_session_costs":false,"terminate_attempts":5,"thresholds_conns":[]},"sip_agent":{"enabled":false,"listen":"127.0.0.1:5060","listen_net":"udp","request_processors":[],"retransmission_timer":1000000000,"sessions_conns":["*internal"],"timezone":""},"stats":{"enabled":false,"indexed_selects":true,"nested_fields":false,"opts":{"*profileIDs":[],"*profileIgnoreFilters":false},"prefix_indexed_fields":[],"store_interval":"","store_uncompressed_limit":0,"suffix_indexed_fields":[],"thresholds_conns":[]},"stor_db":{"db_host":"127.0.0.1","db_name":"cgrates","db_password":"","db_port":3306,"db_type":"*mysql","db_user":"cgrates","items":{"*cdrs":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*session_costs":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_account_actions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_action_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_action_triggers":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_actions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_attributes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_chargers":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_destination_rates":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_destinations":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_dispatcher_hosts":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_dispatcher_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_filters":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rates":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rating_plans":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_rating_profiles":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_resources":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_routes":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_shared_groups":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_stats":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_thresholds":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*tp_timings":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false},"*versions":{"limit":-1,"remote":false,"replicate":false,"static_ttl":false}},"opts":{"mongoQueryTimeout":"10s","mysqlDSNParams":{},"mysqlLocation":"Local","pgSSLMode":"disable","sqlConnMaxLifetime":"0s","sqlMaxIdleConns":10,"sqlMaxOpenConns":100},"prefix_indexed_fields":[],"remote_conns":null,"replication_conns":null,"string_indexed_fields":[]},"suretax":{"bill_to_number":"","business_unit":"","client_number":"","client_tracking":"~*req.CGRID","customer_number":"~*req.Subject","include_local_cost":false,"orig_number":"~*req.Subject","p2pplus4":"","p2pzipcode":"","plus4":"","regulatory_code":"03","response_group":"03","response_type":"D4","return_file_code":"0","sales_type_code":"R","tax_exemption_code_list":"","tax_included":"0","tax_situs_rule":"04","term_number":"~*req.Destination","timezone":"UTC","trans_type_code":"010101","unit_type":"00","units":"1","url":"","validation_key":"","zipcode":""},"templates":{"*asr":[{"mandatory":true,"path":"*diamreq.Session-Id","tag":"SessionId","type":"*variable","value":"~*req.Session-Id"},{"mandatory":true,"path":"*diamreq.Origin-Host","tag":"OriginHost","type":"*variable","value":"~*req.Destination-Host"},{"mandatory":true,"path":"*diamreq.Origin-Realm","tag":"OriginRealm","type":"*variable","value":"~*req.Destination-Realm"},{"mandatory":true,"path":"*diamreq.Destination-Realm","tag":"DestinationRealm","type":"*variable","value":"~*req.Origin-Realm"},{"mandatory":true,"path":"*diamreq.Destination-Host","tag":"DestinationHost","type":"*variable","value":"~*req.Origin-Host"},{"mandatory":true,"path":"*diamreq.Auth-Application-Id","tag":"AuthApplicationId","type":"*variable","value":"~*vars.*appid"}],"*cca":[{"mandatory":true,"path":"*rep.Session-Id","tag":"SessionId","type":"*variable","value":"~*req.Session-Id"},{"path":"*rep.Result-Code","tag":"ResultCode","type":"*constant","value":"2001"},{"mandatory":true,"path":"*rep.Origin-Host","tag":"OriginHost","type":"*variable","value":"~*vars.OriginHost"},{"mandatory":true,"path":"*rep.Origin-Realm","tag":"OriginRealm","type":"*variable","value":"~*vars.OriginRealm"},{"mandatory":true,"path":"*rep.Auth-Application-Id","tag":"AuthApplicationId","type":"*variable","value":"~*vars.*appid"},{"mandatory":true,"path":"*rep.CC-Request-Type","tag":"CCRequestType","type":"*variable","value":"~*req.CC-Request-Type"},{"mandatory":true,"path":"*rep.CC-Request-Number","tag":"CCRequestNumber","type":"*variable","value":"~*req.CC-Request-Number"}],"*cdrLog":[{"mandatory":true,"path":"*cdr.ToR","tag":"ToR","type":"*variable","value":"~*req.BalanceType"},{"mandatory":true,"path":"*cdr.OriginHost","tag":"OriginHost","type":"*constant","value":"127.0.0.1"},{"mandatory":true,"path":"*cdr.RequestType","tag":"RequestType","type":"*constant","value":"*none"},{"mandatory":true,"path":"*cdr.Tenant","tag":"Tenant","type":"*variable","value":"~*req.Tenant"},{"mandatory":true,"path":"*cdr.Account","tag":"Account","type":"*variable","value":"~*req.Account"},{"mandatory":true,"path":"*cdr.Subject","tag":"Subject","type":"*variable","value":"~*req.Account"},{"mandatory":true,"path":"*cdr.Cost","tag":"Cost","type":"*variable","value":"~*req.Cost"},{"mandatory":true,"path":"*cdr.Source","tag":"Source","type":"*constant","value":"*cdrLog"},{"mandatory":true,"path":"*cdr.Usage","tag":"Usage","type":"*constant","value":"1"},{"mandatory":true,"path":"*cdr.RunID","tag":"RunID","type":"*variable","value":"~*req.ActionType"},{"mandatory":true,"path":"*cdr.SetupTime","tag":"SetupTime","type":"*constant","value":"*now"},{"mandatory":true,"path":"*cdr.AnswerTime","tag":"AnswerTime","type":"*constant","value":"*now"},{"mandatory":true,"path":"*cdr.PreRated","tag":"PreRated","type":"*constant","value":"true"}],"*err":[{"mandatory":true,"path":"*rep.Session-Id","tag":"SessionId","type":"*variable","value":"~*req.Session-Id"},{"mandatory":true,"path":"*rep.Origin-Host","tag":"OriginHost","type":"*variable","value":"~*vars.OriginHost"},{"mandatory":true,"path":"*rep.Origin-Realm","tag":"OriginRealm","type":"*variable","value":"~*vars.OriginRealm"}],"*errSip":[{"mandatory":true,"path":"*rep.Request","tag":"Request","type":"*constant","value":"SIP/2.0 500 Internal Server Error"}],"*rar":[{"mandatory":true,"path":"*diamreq.Session-Id","tag":"SessionId","type":"*variable","value":"~*req.Session-Id"},{"mandatory":true,"path":"*diamreq.Origin-Host","tag":"OriginHost","type":"*variable","value":"~*req.Destination-Host"},{"mandatory":true,"path":"*diamreq.Origin-Realm","tag":"OriginRealm","type":"*variable","value":"~*req.Destination-Realm"},{"mandatory":true,"path":"*diamreq.Destination-Realm","tag":"DestinationRealm","type":"*variable","value":"~*req.Origin-Realm"},{"mandatory":true,"path":"*diamreq.Destination-Host","tag":"DestinationHost","type":"*variable","value":"~*req.Origin-Host"},{"mandatory":true,"path":"*diamreq.Auth-Application-Id","tag":"AuthApplicationId","type":"*variable","value":"~*vars.*appid"},{"path":"*diamreq.Re-Auth-Request-Type","tag":"ReAuthRequestType","type":"*constant","value":"0"}]},"thresholds":{"enabled":false,"indexed_selects":true,"nested_fields":false,"opts":{"*profileIDs":[],"*profileIgnoreFilters":false},"prefix_indexed_fields":[],"store_interval":"","suffix_indexed_fields":[]},"tls":{"ca_certificate":"","client_certificate":"","client_key":"","server_certificate":"","server_key":"","server_name":"","server_policy":4}}` if err != nil { t.Fatal(err) } diff --git a/config/eescfg_test.go b/config/eescfg_test.go index 80fd0e620..d4aa291fe 100644 --- a/config/eescfg_test.go +++ b/config/eescfg_test.go @@ -639,6 +639,7 @@ func TestEEsCfgAsMapInterface(t *testing.T) { utils.LimitCfg: -2, utils.PrecacheCfg: false, utils.ReplicateCfg: false, + utils.RemoteCfg: false, utils.TTLCfg: "1s", utils.StaticTTLCfg: false, }, diff --git a/config/libconfig_json.go b/config/libconfig_json.go index 6a927c882..b5e5ff819 100644 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -462,12 +462,14 @@ type CacheParamJsonCfg struct { Ttl *string Static_ttl *bool Precache *bool + Remote *bool Replicate *bool } type CacheJsonCfg struct { Partitions *map[string]*CacheParamJsonCfg Replication_conns *[]string + Remote_conns *[]string } // SM-Kamailio config section diff --git a/cores/core.go b/cores/core.go index 86a59dc61..56263ea40 100644 --- a/cores/core.go +++ b/cores/core.go @@ -202,3 +202,8 @@ func (cS *CoreService) StopMemoryProfiling() (err error) { cS.StopChanMemProf() return } + +// Panic is used print the Message sent as a panic +func (cS *CoreService) Panic(args *utils.PanicMessageArgs, _ *string) error { + panic(args.Message) +} diff --git a/data/conf/samples/dispatcher_opts/cgrates.json b/data/conf/samples/dispatcher_opts/cgrates.json new file mode 100644 index 000000000..65debab34 --- /dev/null +++ b/data/conf/samples/dispatcher_opts/cgrates.json @@ -0,0 +1,52 @@ +{ + + "general": { + "node_id": "DispatcherOpts", + "log_level": 7 + }, + + "listen": { + "rpc_json": ":2012", + "rpc_gob": ":2013", + "http": ":2080" + }, + + "data_db": { + "db_type": "redis", + "db_port": 6379, + "db_name": "10" + }, + + "stor_db": { + "db_password": "CGRateS.org" + }, + + "attributes": { + "enabled": true + }, + + + "dispatchers":{ + "enabled": true + }, + + "caches":{ + "remote_conns": ["gob_cache"] + }, + + "rpc_conns": { + "gob_cache": { + "strategy": "*first", + "conns": [ + {"address": "127.0.0.1:2013", "transport":"*gob"} + ] + } + }, + + + "apiers": { + "enabled": true + } + + +} \ No newline at end of file diff --git a/data/conf/samples/dispatcher_opts_apier/cgrates.json b/data/conf/samples/dispatcher_opts_apier/cgrates.json new file mode 100644 index 000000000..aebba42fb --- /dev/null +++ b/data/conf/samples/dispatcher_opts_apier/cgrates.json @@ -0,0 +1,51 @@ +{ + + "general": { + "node_id": "DispatcherOpts_APIer", + "log_level": 7 + }, + + + "listen": { + "rpc_json": ":4012", + "rpc_gob": ":4013", + "http": ":4080" + }, + + "data_db": { + "db_type": "redis", + "db_port": 6379, + "db_name": "10" + }, + + "stor_db": { + "db_password": "CGRateS.org" + }, + + + "dispatchers":{ + "enabled": true + }, + + "caches":{ + //"remote_conns": ["*internal"], + }, + + + "apiers": { + "enabled": true, + // "caches_conns":["broadcast_cache"] + }, + + // "rpc_conns": { + // "broadcast_cache": { + // "strategy": "*broadcast", + // "conns": [ + // {"address": "127.0.0.1:2012", "transport":"*json"}, + // {"address": "127.0.0.1:4012", "transport":"*json"}, + // {"address": "127.0.0.1:6012", "transport":"*json"} + // ] + // } + // } + +} \ No newline at end of file diff --git a/data/conf/samples/dispatcher_opts_setter/cgrates.json b/data/conf/samples/dispatcher_opts_setter/cgrates.json new file mode 100644 index 000000000..c8285a587 --- /dev/null +++ b/data/conf/samples/dispatcher_opts_setter/cgrates.json @@ -0,0 +1,51 @@ +{ + + "general": { + "node_id": "DispatcherOpts_Setter", + "log_level": 7 + }, + + + "listen": { + "rpc_json": ":6012", + "rpc_gob": ":6013", + "http": ":6080" + }, + + "data_db": { + "db_type": "redis", + "db_port": 6379, + "db_name": "10" + }, + + "stor_db": { + "db_password": "CGRateS.org" + }, + + + "dispatchers":{ + "enabled": false + }, + + "caches":{ + //"remote_conns": ["*internal"], + }, + + + "apiers": { + "enabled": true, + "caches_conns":["broadcast_cache"] + }, + + "rpc_conns": { + "broadcast_cache": { + "strategy": "*broadcast", + "conns": [ + {"address": "127.0.0.1:2012", "transport":"*json"}, + {"address": "127.0.0.1:4012", "transport":"*json"}, + {"address": "127.0.0.1:6012", "transport":"*json"} + ] + } + } + +} \ No newline at end of file diff --git a/dispatchers/caches.go b/dispatchers/caches.go index 8a7c1677c..de5bd08de 100644 --- a/dispatchers/caches.go +++ b/dispatchers/caches.go @@ -84,6 +84,25 @@ func (dS *DispatcherService) CacheSv1HasItem(args *utils.ArgsGetCacheItemWithAPI utils.MetaCaches, utils.CacheSv1HasItem, args, reply) } +func (dS *DispatcherService) CacheSv1GetItem(args *utils.ArgsGetCacheItemWithAPIOpts, reply *interface{}) (err error) { + tnt := dS.cfg.GeneralCfg().DefaultTenant + if args != nil && len(args.Tenant) != 0 { + tnt = args.Tenant + } + ev := make(map[string]interface{}) + opts := make(map[string]interface{}) + if args != nil { + opts = args.APIOpts + } + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.CacheSv1GetItem, tnt, + utils.IfaceAsString(opts[utils.OptsAPIKey]), utils.TimePointer(time.Now())); err != nil { + return + } + } + return dS.Dispatch(&utils.CGREvent{Tenant: tnt, Event: ev, APIOpts: opts}, utils.MetaCaches, utils.CacheSv1GetItem, args, reply) +} + // CacheSv1GetItemExpiryTime returns the expiryTime for an item func (dS *DispatcherService) CacheSv1GetItemExpiryTime(args *utils.ArgsGetCacheItemWithAPIOpts, reply *time.Time) (err error) { @@ -198,6 +217,25 @@ func (dS *DispatcherService) CacheSv1PrecacheStatus(args *utils.AttrCacheIDsWith }, utils.MetaCaches, utils.CacheSv1PrecacheStatus, args, reply) } +func (dS *DispatcherService) CacheSv1GetItemWithRemote(args *utils.ArgsGetCacheItemWithAPIOpts, reply *interface{}) (err error) { + tnt := dS.cfg.GeneralCfg().DefaultTenant + if args != nil && len(args.Tenant) != 0 { + tnt = args.Tenant + } + ev := make(map[string]interface{}) + opts := make(map[string]interface{}) + if args != nil { + opts = args.APIOpts + } + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.CacheSv1GetItemWithRemote, tnt, + utils.IfaceAsString(opts[utils.OptsAPIKey]), utils.TimePointer(time.Now())); err != nil { + return + } + } + return dS.Dispatch(&utils.CGREvent{Tenant: tnt, Event: ev, APIOpts: opts}, utils.MetaCaches, utils.CacheSv1GetItemWithRemote, args, reply) +} + // CacheSv1HasGroup checks existence of a group in cache func (dS *DispatcherService) CacheSv1HasGroup(args *utils.ArgsGetGroupWithAPIOpts, reply *bool) (err error) { diff --git a/dispatchers/cores.go b/dispatchers/cores.go new file mode 100644 index 000000000..ad604c074 --- /dev/null +++ b/dispatchers/cores.go @@ -0,0 +1,175 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package dispatchers + +import ( + "time" + + "github.com/cgrates/cgrates/utils" +) + +func (dS *DispatcherService) CoreSv1Panic(args *utils.PanicMessageArgs, reply *string) (err error) { + tnt := dS.cfg.GeneralCfg().DefaultTenant + if args != nil && len(args.Tenant) != 0 { + tnt = args.Tenant + } + ev := make(map[string]interface{}) + opts := make(map[string]interface{}) + if args != nil { + opts = args.APIOpts + } + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.CoreSv1Panic, tnt, + utils.IfaceAsString(opts[utils.OptsAPIKey]), utils.TimePointer(time.Now())); err != nil { + return + } + } + return dS.Dispatch(&utils.CGREvent{Tenant: tnt, Event: ev, APIOpts: opts}, utils.MetaCore, utils.CoreSv1Panic, args, reply) +} +func (dS *DispatcherService) CoreSv1Ping(args *utils.CGREvent, reply *string) (err error) { + tnt := dS.cfg.GeneralCfg().DefaultTenant + if args != nil && len(args.Tenant) != 0 { + tnt = args.Tenant + } + ev := make(map[string]interface{}) + if args != nil { + ev = args.Event + } + opts := make(map[string]interface{}) + if args != nil { + opts = args.APIOpts + } + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.CoreSv1Ping, tnt, + utils.IfaceAsString(opts[utils.OptsAPIKey]), utils.TimePointer(time.Now())); err != nil { + return + } + } + return dS.Dispatch(&utils.CGREvent{Tenant: tnt, Event: ev, APIOpts: opts}, utils.MetaCore, utils.CoreSv1Ping, args, reply) +} + +func (dS *DispatcherService) CoreSv1Sleep(args *utils.DurationArgs, reply *string) (err error) { + tnt := dS.cfg.GeneralCfg().DefaultTenant + if args != nil && len(args.Tenant) != 0 { + tnt = args.Tenant + } + ev := make(map[string]interface{}) + opts := make(map[string]interface{}) + if args != nil { + opts = args.APIOpts + } + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.CoreSv1Sleep, tnt, + utils.IfaceAsString(opts[utils.OptsAPIKey]), utils.TimePointer(time.Now())); err != nil { + return + } + } + return dS.Dispatch(&utils.CGREvent{Tenant: tnt, Event: ev, APIOpts: opts}, utils.MetaCore, utils.CoreSv1Sleep, args, reply) +} + +func (dS *DispatcherService) CoreSv1StartCPUProfiling(args *utils.DirectoryArgs, reply *string) (err error) { + tnt := dS.cfg.GeneralCfg().DefaultTenant + if args != nil && len(args.Tenant) != 0 { + tnt = args.Tenant + } + ev := make(map[string]interface{}) + opts := make(map[string]interface{}) + if args != nil { + opts = args.APIOpts + } + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.CoreSv1StartCPUProfiling, tnt, + utils.IfaceAsString(opts[utils.OptsAPIKey]), utils.TimePointer(time.Now())); err != nil { + return + } + } + return dS.Dispatch(&utils.CGREvent{Tenant: tnt, Event: ev, APIOpts: opts}, utils.MetaCore, utils.CoreSv1StartCPUProfiling, args, reply) +} +func (dS *DispatcherService) CoreSv1StartMemoryProfiling(args *utils.MemoryPrf, reply *string) (err error) { + tnt := dS.cfg.GeneralCfg().DefaultTenant + if args != nil && len(args.Tenant) != 0 { + tnt = args.Tenant + } + ev := make(map[string]interface{}) + opts := make(map[string]interface{}) + if args != nil { + opts = args.APIOpts + } + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.CoreSv1StartMemoryProfiling, tnt, + utils.IfaceAsString(opts[utils.OptsAPIKey]), utils.TimePointer(time.Now())); err != nil { + return + } + } + return dS.Dispatch(&utils.CGREvent{Tenant: tnt, Event: ev, APIOpts: opts}, utils.MetaCore, utils.CoreSv1StartMemoryProfiling, args, reply) +} +func (dS *DispatcherService) CoreSv1Status(args *utils.TenantWithAPIOpts, reply *map[string]interface{}) (err error) { + tnt := dS.cfg.GeneralCfg().DefaultTenant + if args != nil && len(args.Tenant) != 0 { + tnt = args.Tenant + } + ev := make(map[string]interface{}) + opts := make(map[string]interface{}) + if args != nil { + opts = args.APIOpts + } + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.CoreSv1Status, tnt, + utils.IfaceAsString(opts[utils.OptsAPIKey]), utils.TimePointer(time.Now())); err != nil { + return + } + } + return dS.Dispatch(&utils.CGREvent{Tenant: tnt, Event: ev, APIOpts: opts}, utils.MetaCore, utils.CoreSv1Status, args, reply) +} +func (dS *DispatcherService) CoreSv1StopCPUProfiling(args *utils.TenantWithAPIOpts, reply *string) (err error) { + tnt := dS.cfg.GeneralCfg().DefaultTenant + if args != nil && len(args.Tenant) != 0 { + tnt = args.Tenant + } + ev := make(map[string]interface{}) + opts := make(map[string]interface{}) + if args != nil { + opts = args.APIOpts + } + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.CoreSv1StopCPUProfiling, + tnt, utils.IfaceAsString(opts[utils.OptsAPIKey]), utils.TimePointer(time.Now())); err != nil { + return + } + } + return dS.Dispatch(&utils.CGREvent{Tenant: tnt, Event: ev, APIOpts: opts}, utils.MetaCore, utils.CoreSv1StopCPUProfiling, args, reply) +} +func (dS *DispatcherService) CoreSv1StopMemoryProfiling(args *utils.TenantWithAPIOpts, reply *string) (err error) { + tnt := dS.cfg.GeneralCfg().DefaultTenant + if args != nil && len(args.Tenant) != 0 { + tnt = args.Tenant + } + ev := make(map[string]interface{}) + opts := make(map[string]interface{}) + if args != nil { + opts = args.APIOpts + } + if len(dS.cfg.DispatcherSCfg().AttributeSConns) != 0 { + if err = dS.authorize(utils.CoreSv1StopMemoryProfiling, + tnt, utils.IfaceAsString(opts[utils.OptsAPIKey]), utils.TimePointer(time.Now())); err != nil { + return + } + } + return dS.Dispatch(&utils.CGREvent{Tenant: tnt, Event: ev, APIOpts: opts}, utils.MetaCore, utils.CoreSv1StopMemoryProfiling, args, reply) +} diff --git a/dispatchers/dispatchers.go b/dispatchers/dispatchers.go index 708de8a04..46bd454ce 100644 --- a/dispatchers/dispatchers.go +++ b/dispatchers/dispatchers.go @@ -26,6 +26,7 @@ import ( "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/guardian" "github.com/cgrates/cgrates/utils" "github.com/cgrates/rpcclient" ) @@ -81,7 +82,7 @@ func (dS *DispatcherService) authorize(method, tenant string, apiKey string, evT Event: map[string]interface{}{ utils.APIKey: apiKey, }, - APIOpts: map[string]interface{}{utils.Subsys: utils.MetaDispatchers}, + APIOpts: map[string]interface{}{utils.MetaSubsys: utils.MetaDispatchers}, } var rplyEv engine.AttrSProcessEventReply if err = dS.authorizeEvent(ev, &rplyEv); err != nil { @@ -187,6 +188,49 @@ func (dS *DispatcherService) Dispatch(ev *utils.CGREvent, subsys string, if tnt == utils.EmptyString { tnt = dS.cfg.GeneralCfg().DefaultTenant } + var shouldDispatch bool + if shouldDispatch, err = utils.GetBoolOpts(ev, true, utils.OptsDispatchers); err != nil { + return utils.NewErrDispatcherS(err) + } else if !shouldDispatch { + return callDH( + newInternalHost(tnt), utils.EmptyString, nil, + serviceMethod, args, reply) + } + var dR *DispatcherRoute + var dPrfls engine.DispatcherProfiles + routeID := utils.IfaceAsString(ev.APIOpts[utils.OptsRouteID]) + if routeID != utils.EmptyString { // overwrite routeID with RouteID:Subsystem for subsystem correct routing + routeID = utils.ConcatenatedKey(routeID, subsys) + guardID := utils.ConcatenatedKey(utils.DispatcherSv1, utils.OptsRouteID, routeID) + refID := guardian.Guardian.GuardIDs("", dS.cfg.GeneralCfg().LockingTimeout, + guardID) // lock the routeID so we can make sure we have time to execute only once before caching + defer guardian.Guardian.UnguardIDs(refID) + // use previously discovered route + argsCache := &utils.ArgsGetCacheItemWithAPIOpts{ + Tenant: ev.Tenant, + APIOpts: map[string]interface{}{ + utils.MetaSubsys: utils.MetaDispatchers, + utils.MetaNodeID: dS.cfg.GeneralCfg().NodeID, + }, + ArgsGetCacheItem: utils.ArgsGetCacheItem{ + CacheID: utils.CacheDispatcherRoutes, + ItemID: routeID, + }} + // item + var itmRemote interface{} + if err = dS.connMgr.Call(dS.cfg.CacheCfg().RemoteConns, nil, + utils.CacheSv1GetItem, argsCache, &itmRemote); err != nil && + err.Error() != utils.ErrNotFound.Error() { + return utils.NewErrDispatcherS(err) + } else if err == nil { // not found + dR = itmRemote.(*DispatcherRoute) + routeID = utils.EmptyString // cancel cache replication + } + } + if dR != nil { + dPrfls = engine.DispatcherProfiles{ + &engine.DispatcherProfile{Tenant: dR.Tenant, ID: dR.ProfileID}} // will be used bellow to retrieve the dispatcher + } evNm := utils.MapStorage{ utils.MetaReq: ev.Event, utils.MetaOpts: ev.APIOpts, @@ -194,10 +238,19 @@ func (dS *DispatcherService) Dispatch(ev *utils.CGREvent, subsys string, utils.MetaMethod: serviceMethod, }, } - var dPrfls engine.DispatcherProfiles - if dPrfls, err = dS.dispatcherProfilesForEvent(tnt, ev, evNm, subsys); err != nil { - return utils.NewErrDispatcherS(err) + if dPrfls == nil { // did not discover it yet + if dPrfls, err = dS.dispatcherProfilesForEvent(tnt, ev, evNm, subsys); err != nil { + return utils.NewErrDispatcherS(err) + } } + if len(dPrfls) == 0 { + return utils.NewErrDispatcherS(utils.ErrPrefixNotFound("PROFILE")) + } + if ev.APIOpts == nil { + ev.APIOpts = make(map[string]interface{}) + } + ev.APIOpts[utils.MetaSubsys] = utils.MetaDispatchers // inject into args + ev.APIOpts[utils.MetaNodeID] = dS.cfg.GeneralCfg().NodeID for _, dPrfl := range dPrfls { tntID := dPrfl.TenantID() // get or build the Dispatcher for the config @@ -205,13 +258,37 @@ func (dS *DispatcherService) Dispatch(ev *utils.CGREvent, subsys string, if x, ok := engine.Cache.Get(utils.CacheDispatchers, tntID); ok && x != nil { d = x.(Dispatcher) - } else if d, err = newDispatcher(dPrfl); err != nil { - return utils.NewErrDispatcherS(err) + } else { // dispatcher is not cached, build it here + if dPrfl.Hosts == nil { // dispatcher profile was not retrieved but built artificially above, try retrieving + if dPrfl, err = dS.dm.GetDispatcherProfile(dPrfl.Tenant, dPrfl.ID, + true, true, utils.NonTransactional); err != nil { + if err != utils.ErrNotFound { + return + } + // profile was not found + utils.Logger.Warning(fmt.Sprintf("<%s> could not find profile with tenant: <%s> and ID <%s> for routeID: <%s>", + utils.DispatcherS, dR.Tenant, dR.ProfileID, routeID)) + if len(dPrfls) == 1 { // the only profile set does not exist anymore + return utils.NewErrDispatcherS(utils.ErrPrefixNotFound("PROFILE")) + } + continue + } + } + if d, err = newDispatcher(dPrfl); err != nil { + return utils.NewErrDispatcherS(err) + } else if err = engine.Cache.Set(utils.CacheDispatchers, tntID, d, // cache the built Dispatcher + nil, true, utils.EmptyString); err != nil { + return utils.NewErrDispatcherS(err) + } } - if err = engine.Cache.Set(utils.CacheDispatchers, tntID, d, nil, true, utils.EmptyString); err != nil { - return utils.NewErrDispatcherS(err) + if routeID != utils.EmptyString && dR == nil { // first time we cache the route + dR = &DispatcherRoute{ + Tenant: dPrfl.Tenant, + ProfileID: dPrfl.ID, + } } - if err = d.Dispatch(dS.dm, dS.fltrS, evNm, tnt, utils.IfaceAsString(ev.APIOpts[utils.OptsRouteID]), subsys, serviceMethod, args, reply); !rpcclient.IsNetworkError(err) { + if err = d.Dispatch(dS.dm, dS.fltrS, evNm, tnt, routeID, dR, + serviceMethod, args, reply); !rpcclient.IsNetworkError(err) { return } } @@ -230,7 +307,7 @@ func (dS *DispatcherService) V1GetProfilesForEvent(ev *utils.CGREvent, utils.MetaVars: utils.MapStorage{ utils.MetaMethod: ev.APIOpts[utils.MetaMethod], }, - }, utils.IfaceAsString(ev.APIOpts[utils.Subsys])) + }, utils.IfaceAsString(ev.APIOpts[utils.MetaSubsys])) if errDpfl != nil { return utils.NewErrDispatcherS(errDpfl) } diff --git a/dispatchers/dispatchers_test.go b/dispatchers/dispatchers_test.go index d7b2ec23e..2db47a9e5 100644 --- a/dispatchers/dispatchers_test.go +++ b/dispatchers/dispatchers_test.go @@ -79,11 +79,11 @@ func TestDispatcherServiceDispatcherProfileForEventGetDispatcherProfileNF(t *tes "RunID": utils.MetaDefault, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaAccounts, + utils.MetaSubsys: utils.MetaAccounts, }, } tnt := ev.Tenant - subsys := utils.IfaceAsString(ev.APIOpts[utils.Subsys]) + subsys := utils.IfaceAsString(ev.APIOpts[utils.MetaSubsys]) _, err = dss.dispatcherProfilesForEvent(tnt, ev, utils.MapStorage{ utils.MetaReq: ev.Event, utils.MetaOpts: ev.APIOpts, @@ -104,7 +104,7 @@ func TestDispatcherServiceDispatcherProfileForEventMIIDENotFound(t *testing.T) { dss := NewDispatcherService(dm, cfg, nil, connMng) ev := &utils.CGREvent{} tnt := "" - subsys := utils.IfaceAsString(ev.APIOpts[utils.Subsys]) + subsys := utils.IfaceAsString(ev.APIOpts[utils.MetaSubsys]) _, err := dss.dispatcherProfilesForEvent(tnt, ev, utils.MapStorage{ utils.MetaReq: ev.Event, utils.MetaOpts: ev.APIOpts, @@ -573,11 +573,11 @@ func TestDispatcherServiceDispatcherProfileForEventErrNil(t *testing.T) { "RunID": utils.MetaDefault, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaAccounts, + utils.MetaSubsys: utils.MetaAccounts, }, } tnt := ev.Tenant - subsys := utils.IfaceAsString(ev.APIOpts[utils.Subsys]) + subsys := utils.IfaceAsString(ev.APIOpts[utils.MetaSubsys]) _, err = dss.dispatcherProfilesForEvent(tnt, ev, utils.MapStorage{ utils.MetaReq: ev.Event, utils.MetaOpts: ev.APIOpts, @@ -620,11 +620,11 @@ func TestDispatcherV1GetProfileForEventReturn(t *testing.T) { "RunID": utils.MetaDefault, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaAccounts, + utils.MetaSubsys: utils.MetaAccounts, }, } tnt := ev.Tenant - subsys := utils.IfaceAsString(ev.APIOpts[utils.Subsys]) + subsys := utils.IfaceAsString(ev.APIOpts[utils.MetaSubsys]) _, err = dss.dispatcherProfilesForEvent(tnt, ev, utils.MapStorage{ utils.MetaReq: ev.Event, utils.MetaOpts: ev.APIOpts, @@ -673,11 +673,11 @@ func TestDispatcherServiceDispatcherProfileForEventErrNotFound(t *testing.T) { "RunID": utils.MetaDefault, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaAny, + utils.MetaSubsys: utils.MetaAny, }, } tnt := ev.Tenant - subsys := utils.IfaceAsString(ev.APIOpts[utils.Subsys]) + subsys := utils.IfaceAsString(ev.APIOpts[utils.MetaSubsys]) _, err = dss.dispatcherProfilesForEvent(tnt, ev, utils.MapStorage{ utils.MetaReq: ev.Event, utils.MetaOpts: ev.APIOpts, @@ -720,11 +720,11 @@ func TestDispatcherServiceDispatcherProfileForEventErrNotFound2(t *testing.T) { "RunID": utils.MetaDefault, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaAccounts, + utils.MetaSubsys: utils.MetaAccounts, }, } tnt := "" - subsys := utils.IfaceAsString(ev.APIOpts[utils.Subsys]) + subsys := utils.IfaceAsString(ev.APIOpts[utils.MetaSubsys]) _, err = dss.dispatcherProfilesForEvent(tnt, ev, utils.MapStorage{ utils.MetaReq: ev.Event, utils.MetaOpts: ev.APIOpts, @@ -772,11 +772,11 @@ func TestDispatcherServiceDispatcherProfileForEventErrNotFoundTime(t *testing.T) "RunID": utils.MetaDefault, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaAccounts, + utils.MetaSubsys: utils.MetaAccounts, }, } tnt := ev.Tenant - subsys := utils.IfaceAsString(ev.APIOpts[utils.Subsys]) + subsys := utils.IfaceAsString(ev.APIOpts[utils.MetaSubsys]) _, err = dss.dispatcherProfilesForEvent(tnt, ev, utils.MapStorage{ utils.MetaReq: ev.Event, utils.MetaOpts: ev.APIOpts, @@ -820,11 +820,11 @@ func TestDispatcherServiceDispatcherProfileForEventErrNotFoundFilter(t *testing. "RunID": utils.MetaDefault, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaAccounts, + utils.MetaSubsys: utils.MetaAccounts, }, } tnt := ev.Tenant - subsys := utils.IfaceAsString(ev.APIOpts[utils.Subsys]) + subsys := utils.IfaceAsString(ev.APIOpts[utils.MetaSubsys]) _, err = dss.dispatcherProfilesForEvent(tnt, ev, utils.MapStorage{ utils.MetaReq: ev.Event, utils.MetaOpts: ev.APIOpts, @@ -868,10 +868,10 @@ func TestDispatcherServiceDispatchDspErr(t *testing.T) { "RunID": utils.MetaDefault, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaAccounts, + utils.MetaSubsys: utils.MetaAccounts, }, } - subsys := utils.IfaceAsString(ev.APIOpts[utils.Subsys]) + subsys := utils.IfaceAsString(ev.APIOpts[utils.MetaSubsys]) err = dss.Dispatch(ev, subsys, "", "", "") expected := "DISPATCHER_ERROR:unsupported dispatch strategy: <>" if err == nil || err.Error() != expected { @@ -921,10 +921,10 @@ func TestDispatcherServiceDispatchDspErrHostNotFound(t *testing.T) { "RunID": utils.MetaDefault, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaAccounts, + utils.MetaSubsys: utils.MetaAccounts, }, } - subsys := utils.IfaceAsString(ev.APIOpts[utils.Subsys]) + subsys := utils.IfaceAsString(ev.APIOpts[utils.MetaSubsys]) err = dss.Dispatch(ev, subsys, "", "", "") expected := "HOST_NOT_FOUND" if err == nil || err.Error() != expected { @@ -980,11 +980,11 @@ func TestDispatcherServiceDispatcherProfileForEventFoundFilter(t *testing.T) { "RunID": utils.MetaDefault, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaAccounts, + utils.MetaSubsys: utils.MetaAccounts, }, } tnt := ev.Tenant - subsys := utils.IfaceAsString(ev.APIOpts[utils.Subsys]) + subsys := utils.IfaceAsString(ev.APIOpts[utils.MetaSubsys]) _, err = dss.dispatcherProfilesForEvent(tnt, ev, utils.MapStorage{ utils.MetaReq: ev.Event, utils.MetaOpts: ev.APIOpts, @@ -1023,11 +1023,11 @@ func TestDispatcherServiceDispatcherProfileForEventNotNotFound(t *testing.T) { "RunID": utils.MetaDefault, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaAccounts, + utils.MetaSubsys: utils.MetaAccounts, }, } tnt := ev.Tenant - subsys := utils.IfaceAsString(ev.APIOpts[utils.Subsys]) + subsys := utils.IfaceAsString(ev.APIOpts[utils.MetaSubsys]) _, err := dss.dispatcherProfilesForEvent(tnt, ev, utils.MapStorage{ utils.MetaReq: ev.Event, utils.MetaOpts: ev.APIOpts, @@ -1085,11 +1085,11 @@ func TestDispatcherServiceDispatcherProfileForEventGetDispatcherError(t *testing "RunID": utils.MetaDefault, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaAccounts, + utils.MetaSubsys: utils.MetaAccounts, }, } tnt := ev.Tenant - subsys := utils.IfaceAsString(ev.APIOpts[utils.Subsys]) + subsys := utils.IfaceAsString(ev.APIOpts[utils.MetaSubsys]) _, err = dss.dispatcherProfilesForEvent(tnt, ev, utils.MapStorage{ utils.MetaReq: ev.Event, utils.MetaOpts: ev.APIOpts, @@ -1141,10 +1141,10 @@ func TestDispatcherServiceDispatchDspErrHostNotFound2(t *testing.T) { "RunID": utils.MetaDefault, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaAccounts, + utils.MetaSubsys: utils.MetaAccounts, }, } - subsys := utils.IfaceAsString(ev.APIOpts[utils.Subsys]) + subsys := utils.IfaceAsString(ev.APIOpts[utils.MetaSubsys]) err = dss.Dispatch(ev, subsys, "", "", "") expected := "HOST_NOT_FOUND" if err == nil || err.Error() != expected { @@ -1204,10 +1204,10 @@ func TestDispatcherServiceDispatchDspErrHostNotFound3(t *testing.T) { "RunID": utils.MetaDefault, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaAccounts, + utils.MetaSubsys: utils.MetaAccounts, }, } - subsys := utils.IfaceAsString(ev.APIOpts[utils.Subsys]) + subsys := utils.IfaceAsString(ev.APIOpts[utils.MetaSubsys]) err = dss.Dispatch(ev, subsys, "", "", "") expected := "DISPATCHER_ERROR:NOT_IMPLEMENTED" if err == nil || err.Error() != expected { @@ -1238,7 +1238,7 @@ func TestDispatcherServiceCall2(t *testing.T) { "RunID": utils.MetaDefault, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaDispatchers, + utils.MetaSubsys: utils.MetaDispatchers, }, } err := dsp.Call("DispatcherService.Test", args, &reply) @@ -1270,7 +1270,7 @@ func TestDispatcherServiceCall3(t *testing.T) { "RunID": utils.MetaDefault, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaDispatchers, + utils.MetaSubsys: utils.MetaDispatchers, }, } err := dsp.Call("DispatcherService.Test2", args, &reply) @@ -1302,7 +1302,7 @@ func TestDispatcherServiceCall4(t *testing.T) { "RunID": utils.MetaDefault, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaDispatchers, + utils.MetaSubsys: utils.MetaDispatchers, }, } err := dsp.Call("DispatcherService.Test3", args, &reply) diff --git a/dispatchers/libdispatcher.go b/dispatchers/libdispatcher.go index 5a65d0daa..c6b9d945d 100644 --- a/dispatchers/libdispatcher.go +++ b/dispatchers/libdispatcher.go @@ -24,6 +24,7 @@ import ( "math/rand" "sort" "sync" + "time" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" @@ -33,7 +34,12 @@ import ( func init() { gob.Register(new(LoadMetrics)) + gob.Register(new(DispatcherRoute)) +} +// DispatcherRoute is bounded to a routeID +type DispatcherRoute struct { + Tenant, ProfileID, HostID string } // Dispatcher is responsible for routing requests to pool of connections @@ -41,7 +47,7 @@ func init() { type Dispatcher interface { // Dispatch is used to send the method over the connections given Dispatch(dm *engine.DataManager, flts *engine.FilterS, - ev utils.DataProvider, tnt, routeID, subsystem string, + ev utils.DataProvider, tnt, routeID string, dR *DispatcherRoute, serviceMethod string, args interface{}, reply interface{}) (err error) } @@ -69,6 +75,7 @@ func newDispatcher(pfl *engine.DispatcherProfile) (d Dispatcher, err error) { return } +// getDispatcherHosts returns a list of host IDs matching the event with filters func getDispatcherHosts(fltrs *engine.FilterS, ev utils.DataProvider, tnt string, hosts engine.DispatcherHostProfiles) (hostIDs engine.DispatcherHostIDs, err error) { hostIDs = make(engine.DispatcherHostIDs, 0, len(hosts)) for _, host := range hosts { @@ -86,16 +93,19 @@ func getDispatcherHosts(fltrs *engine.FilterS, ev utils.DataProvider, tnt string return } +// hostSorter is the sorting interface used by singleDispatcher type hostSorter interface { Sort(fltrs *engine.FilterS, ev utils.DataProvider, tnt string, hosts engine.DispatcherHostProfiles) (hostIDs engine.DispatcherHostIDs, err error) } +// noSort will just return the matching hosts for the event type noSort struct{} func (noSort) Sort(fltrs *engine.FilterS, ev utils.DataProvider, tnt string, hosts engine.DispatcherHostProfiles) (hostIDs engine.DispatcherHostIDs, err error) { return getDispatcherHosts(fltrs, ev, tnt, hosts) } +// randomSort will randomize the matching hosts for the event type randomSort struct{} func (randomSort) Sort(fltrs *engine.FilterS, ev utils.DataProvider, tnt string, hosts engine.DispatcherHostProfiles) (hostIDs engine.DispatcherHostIDs, err error) { @@ -105,6 +115,8 @@ func (randomSort) Sort(fltrs *engine.FilterS, ev utils.DataProvider, tnt string, return getDispatcherHosts(fltrs, ev, tnt, hosts) } +// roundRobinSort will sort the matching hosts for the event in a round-robin fashion via nextIDx +// which will be increased on each Sort iteration type roundRobinSort struct{ nextIDx int } func (rs *roundRobinSort) Sort(fltrs *engine.FilterS, ev utils.DataProvider, tnt string, hosts engine.DispatcherHostProfiles) (hostIDs engine.DispatcherHostIDs, err error) { @@ -124,6 +136,7 @@ func (rs *roundRobinSort) Sort(fltrs *engine.FilterS, ev utils.DataProvider, tnt return getDispatcherHosts(fltrs, ev, tnt, dh) } +// newSingleDispatcher is the constructor for singleDispatcher struct func newSingleDispatcher(hosts engine.DispatcherHostProfiles, params map[string]interface{}, tntID string, sorter hostSorter) (_ Dispatcher, err error) { if dflt, has := params[utils.MetaDefaultRatio]; has { var ratio int64 @@ -153,69 +166,64 @@ func newSingleDispatcher(hosts engine.DispatcherHostProfiles, params map[string] }, nil } +// singleResultDispatcher routes the event to a single host +// implements Dispatcher interface type singleResultDispatcher struct { sorter hostSorter hosts engine.DispatcherHostProfiles } func (sd *singleResultDispatcher) Dispatch(dm *engine.DataManager, flts *engine.FilterS, - ev utils.DataProvider, tnt, routeID, subsystem string, + ev utils.DataProvider, tnt, routeID string, dR *DispatcherRoute, serviceMethod string, args interface{}, reply interface{}) (err error) { - var dH *engine.DispatcherHost - if routeID != utils.EmptyString { - // overwrite routeID with RouteID:Subsystem - routeID = utils.ConcatenatedKey(routeID, subsystem) - // use previously discovered route - if x, ok := engine.Cache.Get(utils.CacheDispatcherRoutes, - routeID); ok && x != nil { - dH = x.(*engine.DispatcherHost) - if err = dH.Call(serviceMethod, args, reply); !rpcclient.IsNetworkError(err) { - return - } + if routeID != utils.EmptyString && dR.HostID != utils.EmptyString { // route to previously discovered route + if err = callDHwithID(tnt, dR.HostID, routeID, dR, dm, + serviceMethod, args, reply); err == nil || + (err != utils.ErrNotFound && !rpcclient.IsNetworkError(err)) { // successful dispatch with normal errors + return } + // not found or network errors will continue + utils.Logger.Warning(fmt.Sprintf("<%s> error <%s> dispatching to host with identity <%q>", + utils.DispatcherS, err.Error(), dR.HostID)) } var hostIDs []string if hostIDs, err = sd.sorter.Sort(flts, ev, tnt, sd.hosts); err != nil { return + } else if len(hostIDs) == 0 { // in case we do not match any host + return utils.ErrHostNotFound } - var called bool for _, hostID := range hostIDs { - if dH, err = dm.GetDispatcherHost(tnt, hostID, true, true, utils.NonTransactional); err != nil { - if err == utils.ErrNotFound { - utils.Logger.Warning(fmt.Sprintf("<%s> could not find host with ID %q", - utils.DispatcherS, hostID)) - err = nil - continue + var dRh *DispatcherRoute + if routeID != utils.EmptyString { + dRh = &DispatcherRoute{ + Tenant: dR.Tenant, + ProfileID: dR.ProfileID, + HostID: hostID, } - err = utils.NewErrDispatcherS(err) + } + if err = callDHwithID(tnt, hostID, routeID, dRh, dm, + serviceMethod, args, reply); err == nil || + (err != utils.ErrNotFound && !rpcclient.IsNetworkError(err)) { // successful dispatch with normal errors return } - called = true - if err = dH.Call(serviceMethod, args, reply); rpcclient.IsNetworkError(err) { - continue + if err != nil { + // not found or network errors will continue with standard dispatching + utils.Logger.Warning(fmt.Sprintf("<%s> error <%s> dispatching to host with identity <%q>", + utils.DispatcherS, err.Error(), hostID)) } - if routeID != utils.EmptyString { // cache the discovered route - if err = engine.Cache.Set(utils.CacheDispatcherRoutes, routeID, dH, - nil, true, utils.EmptyString); err != nil { - return - } - } - break - } - if !called { // in case we do not match any host - err = utils.ErrHostNotFound - return } return } +// broadcastDispatcher routes the event to multiple hosts in a pool +// implements the Dispatcher interface type broadcastDispatcher struct { strategy string hosts engine.DispatcherHostProfiles } func (b *broadcastDispatcher) Dispatch(dm *engine.DataManager, flts *engine.FilterS, - ev utils.DataProvider, tnt, routeID, subsystem string, + ev utils.DataProvider, tnt, routeID string, dR *DispatcherRoute, serviceMethod string, args interface{}, reply interface{}) (err error) { var hostIDs []string if hostIDs, err = getDispatcherHosts(flts, ev, tnt, b.hosts); err != nil { @@ -235,7 +243,19 @@ func (b *broadcastDispatcher) Dispatch(dm *engine.DataManager, flts *engine.Filt return utils.NewErrDispatcherS(err) } hasHosts = true - pool.AddClient(dH) + var dRh *DispatcherRoute + if routeID != utils.EmptyString { + dRh = &DispatcherRoute{ + Tenant: dR.Tenant, + ProfileID: dR.ProfileID, + HostID: hostID, + } + } + pool.AddClient(&lazyDH{ + dh: dH, + routeID: routeID, + dR: dRh, + }) } if !hasHosts { // in case we do not match any host return utils.ErrHostNotFound @@ -251,9 +271,8 @@ type loadDispatcher struct { } func (ld *loadDispatcher) Dispatch(dm *engine.DataManager, flts *engine.FilterS, - ev utils.DataProvider, tnt, routeID, subsystem string, + ev utils.DataProvider, tnt, routeID string, dR *DispatcherRoute, serviceMethod string, args interface{}, reply interface{}) (err error) { - var dH *engine.DispatcherHost var lM *LoadMetrics if x, ok := engine.Cache.Get(utils.CacheDispatcherLoads, ld.tntID); ok && x != nil { var canCast bool @@ -263,56 +282,47 @@ func (ld *loadDispatcher) Dispatch(dm *engine.DataManager, flts *engine.FilterS, } else if lM, err = newLoadMetrics(ld.hosts, ld.defaultRatio); err != nil { return } - - if routeID != utils.EmptyString { - // overwrite routeID with RouteID:Subsystem - routeID = utils.ConcatenatedKey(routeID, subsystem) - // use previously discovered route - if x, ok := engine.Cache.Get(utils.CacheDispatcherRoutes, - routeID); ok && x != nil { - dH = x.(*engine.DispatcherHost) - lM.incrementLoad(dH.ID, ld.tntID) - err = dH.Call(serviceMethod, args, reply) - lM.decrementLoad(dH.ID, ld.tntID) // call ended - if !rpcclient.IsNetworkError(err) { - return - } + if routeID != utils.EmptyString && dR.HostID != utils.EmptyString { // route to previously discovered route + lM.incrementLoad(dR.HostID, ld.tntID) + err = callDHwithID(tnt, dR.HostID, routeID, dR, dm, + serviceMethod, args, reply) + lM.decrementLoad(dR.HostID, ld.tntID) // call ended + if err == nil || + (err != utils.ErrNotFound && !rpcclient.IsNetworkError(err)) { // successful dispatch with normal errors + return } + // not found or network errors will continue with standard dispatching + utils.Logger.Warning(fmt.Sprintf("<%s> error <%s> dispatching to host with id <%q>", + utils.DispatcherS, err.Error(), dR.HostID)) } var hostIDs []string if hostIDs, err = ld.sorter.Sort(flts, ev, tnt, lM.getHosts(ld.hosts)); err != nil { return + } else if len(hostIDs) == 0 { // in case we do not match any host + return utils.ErrHostNotFound } - var called bool for _, hostID := range hostIDs { - if dH, err = dm.GetDispatcherHost(tnt, hostID, true, true, utils.NonTransactional); err != nil { - if err == utils.ErrNotFound { - utils.Logger.Warning(fmt.Sprintf("<%s> could not find host with ID %q", - utils.DispatcherS, hostID)) - err = nil - continue + var dRh *DispatcherRoute + if routeID != utils.EmptyString { + dRh = &DispatcherRoute{ + Tenant: dR.Tenant, + ProfileID: dR.ProfileID, + HostID: hostID, } - err = utils.NewErrDispatcherS(err) + } + lM.incrementLoad(hostID, ld.tntID) + err = callDHwithID(tnt, hostID, routeID, dRh, dm, + serviceMethod, args, reply) + lM.decrementLoad(hostID, ld.tntID) // call ended + if err == nil || + (err != utils.ErrNotFound && !rpcclient.IsNetworkError(err)) { // successful dispatch with normal errors return } - called = true - lM.incrementLoad(hostID, ld.tntID) - err = dH.Call(serviceMethod, args, reply) - lM.decrementLoad(hostID, ld.tntID) // call ended - if rpcclient.IsNetworkError(err) { - continue + if err != nil { + // not found or network errors will continue with standard dispatching + utils.Logger.Warning(fmt.Sprintf("<%s> error <%s> dispatching to host with id <%q>", + utils.DispatcherS, err.Error(), hostID)) } - if routeID != utils.EmptyString { // cache the discovered route - if err = engine.Cache.Set(utils.CacheDispatcherRoutes, routeID, dH, - nil, true, utils.EmptyString); err != nil { - return - } - } - break - } - if !called { // in case we do not match any host - err = utils.ErrHostNotFound - return } return } @@ -390,3 +400,65 @@ func (lM *LoadMetrics) decrementLoad(hostID, tntID string) { engine.Cache.ReplicateSet(utils.CacheDispatcherLoads, tntID, lM) lM.mutex.Unlock() } + +// lazyDH is created for the broadcast strategy so we can make sure host exists during setup phase +type lazyDH struct { + dh *engine.DispatcherHost + routeID string + dR *DispatcherRoute +} + +func (l *lazyDH) Call(method string, args, reply interface{}) (err error) { + return callDH(l.dh, l.routeID, l.dR, method, args, reply) +} + +func callDH(dh *engine.DispatcherHost, routeID string, dR *DispatcherRoute, + method string, args, reply interface{}) (err error) { + if routeID != utils.EmptyString { // cache the discovered route before dispatching + argsCache := &utils.ArgCacheReplicateSet{ + Tenant: dh.Tenant, + APIOpts: map[string]interface{}{ + utils.MetaSubsys: utils.MetaDispatchers, + utils.MetaNodeID: config.CgrConfig().GeneralCfg().NodeID, + }, + CacheID: utils.CacheDispatcherRoutes, + ItemID: routeID, + Value: dR, + } + if err = engine.Cache.SetWithReplicate(argsCache); err != nil { + return + } + } + if err = dh.Call(method, args, reply); err != nil { + return + } + return +} + +// callDHwithID is a wrapper on callDH using ID of the host, will also cache once the call is successful +func callDHwithID(tnt, hostID, routeID string, dR *DispatcherRoute, dm *engine.DataManager, + serviceMethod string, args, reply interface{}) (err error) { + var dH *engine.DispatcherHost + if dH, err = dm.GetDispatcherHost(tnt, hostID, true, true, utils.NonTransactional); err != nil { + return + } + if err = callDH(dH, routeID, dR, serviceMethod, args, reply); err != nil { + return + } + return +} + +// newInternalHost returns an internal host as needed for internal dispatching +func newInternalHost(tnt string) *engine.DispatcherHost { + return &engine.DispatcherHost{ + Tenant: tnt, + RemoteHost: &config.RemoteHost{ + ID: utils.MetaInternal, + Address: utils.MetaInternal, + ConnectAttempts: 1, + Reconnects: 1, + ConnectTimeout: time.Second, + ReplyTimeout: time.Second, + }, + } +} diff --git a/dispatchers/libdispatcher_test.go b/dispatchers/libdispatcher_test.go index bd9774c47..492dace78 100644 --- a/dispatchers/libdispatcher_test.go +++ b/dispatchers/libdispatcher_test.go @@ -388,7 +388,7 @@ func TestLibDispatcherSingleResultDispatcherDispatch(t *testing.T) { wgDsp := &singleResultDispatcher{sorter: new(noSort)} dataDB := engine.NewInternalDB(nil, nil, true, config.CgrConfig().DataDbCfg().Items) dM := engine.NewDataManager(dataDB, config.CgrConfig().CacheCfg(), nil) - err := wgDsp.Dispatch(dM, nil, nil, "", "", "", "", "", "") + err := wgDsp.Dispatch(dM, nil, nil, "", "", &DispatcherRoute{}, "", "", "") expected := "HOST_NOT_FOUND" if err == nil || err.Error() != expected { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", expected, err) @@ -399,7 +399,7 @@ func TestLibDispatcherSingleResultDispatcherDispatchRouteID(t *testing.T) { wgDsp := &singleResultDispatcher{sorter: new(roundRobinSort)} dataDB := engine.NewInternalDB(nil, nil, true, config.CgrConfig().DataDbCfg().Items) dM := engine.NewDataManager(dataDB, config.CgrConfig().CacheCfg(), nil) - err := wgDsp.Dispatch(dM, nil, nil, "", "routeID", "", "", "", "") + err := wgDsp.Dispatch(dM, nil, nil, "", "routeID", &DispatcherRoute{}, "", "", "") expected := "HOST_NOT_FOUND" if err == nil || err.Error() != expected { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", expected, err) @@ -410,7 +410,7 @@ func TestLibDispatcherBroadcastDispatcherDispatch(t *testing.T) { wgDsp := &broadcastDispatcher{hosts: engine.DispatcherHostProfiles{{ID: "testID"}}} dataDB := engine.NewInternalDB(nil, nil, true, config.CgrConfig().DataDbCfg().Items) dM := engine.NewDataManager(dataDB, config.CgrConfig().CacheCfg(), nil) - err := wgDsp.Dispatch(dM, nil, nil, "", "", "", "", "", "") + err := wgDsp.Dispatch(dM, nil, nil, "", "", &DispatcherRoute{}, "", "", "") expected := "HOST_NOT_FOUND" if err == nil || err.Error() != expected { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", expected, err) @@ -421,7 +421,7 @@ func TestLibDispatcherBroadcastDispatcherDispatchRouteID(t *testing.T) { wgDsp := &broadcastDispatcher{hosts: engine.DispatcherHostProfiles{{ID: "testID"}}} dataDB := engine.NewInternalDB(nil, nil, true, config.CgrConfig().DataDbCfg().Items) dM := engine.NewDataManager(dataDB, config.CgrConfig().CacheCfg(), nil) - err := wgDsp.Dispatch(dM, nil, nil, "", "routeID", "", "", "", "") + err := wgDsp.Dispatch(dM, nil, nil, "", "routeID", &DispatcherRoute{}, "", "", "") expected := "HOST_NOT_FOUND" if err == nil || err.Error() != expected { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", expected, err) @@ -432,7 +432,7 @@ func TestLibDispatcherLoadDispatcherDispatch(t *testing.T) { wgDsp := &loadDispatcher{sorter: new(randomSort)} dataDB := engine.NewInternalDB(nil, nil, true, config.CgrConfig().DataDbCfg().Items) dM := engine.NewDataManager(dataDB, config.CgrConfig().CacheCfg(), nil) - err := wgDsp.Dispatch(dM, nil, nil, "", "", "", "", "", "") + err := wgDsp.Dispatch(dM, nil, nil, "", "", &DispatcherRoute{}, "", "", "") expected := "HOST_NOT_FOUND" if err == nil || err.Error() != expected { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", expected, err) @@ -449,7 +449,7 @@ func TestLibDispatcherLoadDispatcherDispatchHostsID(t *testing.T) { } dataDB := engine.NewInternalDB(nil, nil, true, config.CgrConfig().DataDbCfg().Items) dM := engine.NewDataManager(dataDB, config.CgrConfig().CacheCfg(), nil) - err := wgDsp.Dispatch(dM, nil, nil, "", "routeID", "", "", "", "") + err := wgDsp.Dispatch(dM, nil, nil, "", "routeID", &DispatcherRoute{}, "", "", "") expected := "HOST_NOT_FOUND" if err == nil || err.Error() != expected { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", expected, err) @@ -474,7 +474,7 @@ func TestLibDispatcherLoadStrategyDispatchCaseHosts(t *testing.T) { } dataDB := engine.NewInternalDB(nil, nil, true, config.CgrConfig().DataDbCfg().Items) dM := engine.NewDataManager(dataDB, config.CgrConfig().CacheCfg(), nil) - err := wgDsp.Dispatch(dM, nil, nil, "", "", "", "", "", "") + err := wgDsp.Dispatch(dM, nil, nil, "", "", &DispatcherRoute{}, "", "", "") expected := "HOST_NOT_FOUND" if err == nil || err.Error() != expected { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", expected, err) @@ -497,7 +497,7 @@ func TestLibDispatcherLoadStrategyDispatchCaseHostsError(t *testing.T) { defaultRatio: 1, sorter: new(noSort), } - err := wgDsp.Dispatch(nil, nil, nil, "", "", "", "", "", "") + err := wgDsp.Dispatch(nil, nil, nil, "", "", &DispatcherRoute{}, "", "", "") expected := "DISPATCHER_ERROR:NO_DATABASE_CONNECTION" if err == nil || err.Error() != expected { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", expected, err) @@ -527,7 +527,7 @@ func TestLibDispatcherLoadStrategyDispatchCaseHostsCastError(t *testing.T) { defaultRatio: 1, sorter: new(noSort), } - err := wgDsp.Dispatch(nil, nil, nil, "", "", "", "", "", "") + err := wgDsp.Dispatch(nil, nil, nil, "", "", &DispatcherRoute{}, "", "", "") expected := "cannot cast false to *LoadMetrics" if err == nil || err.Error() != expected { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", expected, err) @@ -552,7 +552,7 @@ func TestLibDispatcherLoadStrategyDispatchCaseHostsCastError2(t *testing.T) { defaultRatio: 1, sorter: new(noSort), } - err := wgDsp.Dispatch(nil, nil, nil, "", "", "", "", "", "") + err := wgDsp.Dispatch(nil, nil, nil, "", "", &DispatcherRoute{}, "", "", "") expected := "cannot convert field: false to int" if err == nil || err.Error() != expected { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", expected, err) @@ -577,7 +577,7 @@ func TestLibDispatcherSingleResultDispatcherCastError(t *testing.T) { engine.Cache.SetWithoutReplicate(utils.CacheDispatcherRoutes, "testID:*attributes", value, nil, true, utils.NonTransactional) wgDsp := &singleResultDispatcher{sorter: new(noSort), hosts: engine.DispatcherHostProfiles{{ID: "testID"}}} - err := wgDsp.Dispatch(nil, nil, nil, "", "testID", utils.MetaAttributes, "", "", "") + err := wgDsp.Dispatch(nil, nil, nil, "", "testID", &DispatcherRoute{}, "", "", "") expected := "DISPATCHER_ERROR:NO_DATABASE_CONNECTION" if err == nil || err.Error() != expected { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", expected, err) @@ -615,7 +615,7 @@ func TestLibDispatcherSingleResultDispatcherCastError2(t *testing.T) { engine.Cache.SetWithoutReplicate(utils.CacheDispatcherRoutes, "testID:*attributes", value, nil, true, utils.NonTransactional) wgDsp := &singleResultDispatcher{sorter: new(noSort), hosts: engine.DispatcherHostProfiles{{ID: "testID"}}} - err := wgDsp.Dispatch(nil, nil, nil, "testTenant", "testID", utils.MetaAttributes, utils.AttributeSv1Ping, &utils.CGREvent{}, &wgDsp) + err := wgDsp.Dispatch(nil, nil, nil, "testTenant", "testID", &DispatcherRoute{}, utils.AttributeSv1Ping, &utils.CGREvent{}, &wgDsp) expected := "UNSUPPORTED_SERVICE_METHOD" if err == nil || err.Error() != expected { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", expected, err) @@ -642,7 +642,7 @@ func TestLibDispatcherBroadcastDispatcherDispatchError1(t *testing.T) { engine.Cache.SetWithoutReplicate(utils.CacheDispatcherRoutes, "testID:*attributes", value, nil, true, utils.NonTransactional) wgDsp := &broadcastDispatcher{hosts: engine.DispatcherHostProfiles{{ID: "testID"}}} - err := wgDsp.Dispatch(nil, nil, nil, "testTenant", "testID", utils.MetaAttributes, "", "", "") + err := wgDsp.Dispatch(nil, nil, nil, "testTenant", "testID", &DispatcherRoute{}, "", "", "") expected := "DISPATCHER_ERROR:NO_DATABASE_CONNECTION" if err == nil || err.Error() != expected { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", expected, err) @@ -660,7 +660,7 @@ func TestLibDispatcherBroadcastDispatcherDispatchError2(t *testing.T) { engine.Cache.SetWithoutReplicate(utils.CacheDispatcherHosts, "testTenant:testID", nil, nil, true, utils.NonTransactional) wgDsp := &broadcastDispatcher{hosts: engine.DispatcherHostProfiles{{ID: "testID"}}} - err := wgDsp.Dispatch(nil, nil, nil, "testTenant", "testID", utils.MetaAttributes, "", "", "") + err := wgDsp.Dispatch(nil, nil, nil, "testTenant", "testID", &DispatcherRoute{}, "", "", "") expected := "HOST_NOT_FOUND" if err == nil || err.Error() != expected { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", expected, err) @@ -686,7 +686,7 @@ func TestLibDispatcherBroadcastDispatcherDispatchError3(t *testing.T) { engine.Cache.SetWithoutReplicate(utils.CacheDispatcherHosts, "testTenant:testID", value, nil, true, utils.NonTransactional) wgDsp := &broadcastDispatcher{hosts: engine.DispatcherHostProfiles{{ID: "testID"}}} - err := wgDsp.Dispatch(nil, nil, nil, "testTenant", "testID", utils.MetaAttributes, "", "", "") + err := wgDsp.Dispatch(nil, nil, nil, "testTenant", "testID", &DispatcherRoute{}, "", "", "") if err != nil { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", nil, err) } @@ -711,7 +711,7 @@ func TestLibDispatcherLoadDispatcherCacheError(t *testing.T) { engine.Cache.SetWithoutReplicate(utils.CacheDispatcherRoutes, "testID:*attributes", value, nil, true, utils.NonTransactional) wgDsp := &loadDispatcher{sorter: new(noSort), hosts: engine.DispatcherHostProfiles{{ID: "testID"}}} - err := wgDsp.Dispatch(nil, nil, nil, "testTenant", "testID", utils.MetaAttributes, "", "", "") + err := wgDsp.Dispatch(nil, nil, nil, "testTenant", "testID", &DispatcherRoute{}, "", "", "") expected := "HOST_NOT_FOUND" if err == nil || err.Error() != expected { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", expected, err) @@ -743,7 +743,7 @@ func TestLibDispatcherLoadDispatcherCacheError2(t *testing.T) { engine.Cache.SetWithoutReplicate(utils.CacheDispatcherRoutes, "testID:*attributes", value, nil, true, utils.NonTransactional) wgDsp := &loadDispatcher{sorter: new(noSort), hosts: engine.DispatcherHostProfiles{{ID: "testID"}}} - err := wgDsp.Dispatch(nil, nil, nil, "testTenant", "testID", utils.MetaAttributes, utils.AttributeSv1Ping, &utils.CGREvent{}, &wgDsp) + err := wgDsp.Dispatch(nil, nil, nil, "testTenant", "testID", &DispatcherRoute{}, utils.AttributeSv1Ping, &utils.CGREvent{}, &wgDsp) expected := "UNSUPPORTED_SERVICE_METHOD" if err == nil || err.Error() != expected { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", expected, err) @@ -800,7 +800,7 @@ func TestLibDispatcherLoadDispatcherCacheError3(t *testing.T) { defaultRatio: 0, sorter: new(noSort), } - err := wgDsp.Dispatch(dm, nil, nil, "testTENANT", "testID", utils.MetaAttributes, utils.AttributeSv1Ping, &utils.CGREvent{}, &wgDsp) + err := wgDsp.Dispatch(dm, nil, nil, "testTENANT", "testID", &DispatcherRoute{}, utils.AttributeSv1Ping, &utils.CGREvent{}, &wgDsp) if err != nil { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", nil, err) } @@ -868,7 +868,7 @@ func TestLibDispatcherLoadDispatcherCacheError4(t *testing.T) { defaultRatio: 0, sorter: new(noSort), } - err := wgDsp.Dispatch(dm, nil, nil, "testTENANT", "testID", utils.MetaAttributes, utils.AttributeSv1Ping, &utils.CGREvent{}, &wgDsp) + err := wgDsp.Dispatch(dm, nil, nil, "testTENANT", "testID", &DispatcherRoute{}, utils.AttributeSv1Ping, &utils.CGREvent{}, &wgDsp) expected := "DISCONNECTED" if err == nil || err.Error() != expected { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", expected, err) @@ -921,7 +921,7 @@ func TestLibDispatcherLoadDispatcherCacheError5(t *testing.T) { defaultRatio: 0, sorter: new(noSort), } - err := wgDsp.Dispatch(nil, nil, nil, "testTenant", "testID", utils.MetaAttributes, utils.AttributeSv1Ping, &utils.CGREvent{}, &wgDsp) + err := wgDsp.Dispatch(nil, nil, nil, "testTenant", "testID", &DispatcherRoute{}, utils.AttributeSv1Ping, &utils.CGREvent{}, &wgDsp) if err == nil { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", "connection is shut down", err) } @@ -951,7 +951,7 @@ func TestLibDispatcherSingleResultDispatcherCase1(t *testing.T) { engine.Cache.SetWithoutReplicate(utils.CacheDispatcherHosts, "testTenant:testID", value, nil, true, utils.NonTransactional) wgDsp := &singleResultDispatcher{sorter: new(noSort), hosts: engine.DispatcherHostProfiles{{ID: "testID"}}} - err := wgDsp.Dispatch(dm, nil, nil, "testTenant", "", utils.MetaAttributes, utils.AttributeSv1Ping, &utils.CGREvent{}, &wgDsp) + err := wgDsp.Dispatch(dm, nil, nil, "testTenant", "", &DispatcherRoute{}, utils.AttributeSv1Ping, &utils.CGREvent{}, &wgDsp) if err == nil { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", "connection is shut down", err) } @@ -988,7 +988,7 @@ func TestLibDispatcherSingleResultDispatcherCase2(t *testing.T) { engine.Cache.SetWithoutReplicate(utils.CacheDispatcherHosts, "testTenant:testID", value, nil, true, utils.NonTransactional) wgDsp := &singleResultDispatcher{sorter: new(noSort), hosts: engine.DispatcherHostProfiles{{ID: "testID"}}} - err := wgDsp.Dispatch(dm, nil, nil, "testTenant", "routeID", utils.MetaAttributes, utils.AttributeSv1Ping, &utils.CGREvent{}, &wgDsp) + err := wgDsp.Dispatch(dm, nil, nil, "testTenant", "routeID", &DispatcherRoute{}, utils.AttributeSv1Ping, &utils.CGREvent{}, &wgDsp) if err != nil { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", nil, err) } @@ -1035,7 +1035,7 @@ func TestLibDispatcherSingleResultDispatcherCase3(t *testing.T) { engine.Cache.SetWithoutReplicate(utils.CacheDispatcherHosts, "testTenant:testID", value, nil, true, utils.NonTransactional) wgDsp := &singleResultDispatcher{sorter: new(noSort), hosts: engine.DispatcherHostProfiles{{ID: "testID"}}} - err := wgDsp.Dispatch(dm, nil, nil, "testTenant", "routeID", utils.MetaAttributes, utils.AttributeSv1Ping, &utils.CGREvent{}, &wgDsp) + err := wgDsp.Dispatch(dm, nil, nil, "testTenant", "routeID", &DispatcherRoute{}, utils.AttributeSv1Ping, &utils.CGREvent{}, &wgDsp) expected := "DISCONNECTED" if err == nil || err.Error() != expected { t.Errorf("\nExpected <%+v>, \nReceived <%+v>", expected, err) @@ -1055,7 +1055,7 @@ func TestLibDispatcherDispatchFilterError(t *testing.T) { }}, } expErrMsg := "inline parse error for string: <*wrongType>" - if err := dsp.Dispatch(nil, flts, nil, "", "", "", "", "", ""); err == nil || err.Error() != expErrMsg { + if err := dsp.Dispatch(nil, flts, nil, "", "", &DispatcherRoute{}, "", "", ""); err == nil || err.Error() != expErrMsg { t.Errorf("Expected error: %s received: %v", expErrMsg, err) } dsp = &loadDispatcher{ @@ -1066,7 +1066,7 @@ func TestLibDispatcherDispatchFilterError(t *testing.T) { }}, defaultRatio: 1, } - if err := dsp.Dispatch(nil, flts, nil, "", "", "", "", "", ""); err == nil || err.Error() != expErrMsg { + if err := dsp.Dispatch(nil, flts, nil, "", "", &DispatcherRoute{}, "", "", ""); err == nil || err.Error() != expErrMsg { t.Errorf("Expected error: %s received: %v", expErrMsg, err) } dsp = &broadcastDispatcher{ @@ -1075,7 +1075,7 @@ func TestLibDispatcherDispatchFilterError(t *testing.T) { FilterIDs: []string{"*wrongType"}, }}, } - if err := dsp.Dispatch(nil, flts, nil, "", "", "", "", "", ""); err == nil || err.Error() != expErrMsg { + if err := dsp.Dispatch(nil, flts, nil, "", "", &DispatcherRoute{}, "", "", ""); err == nil || err.Error() != expErrMsg { t.Errorf("Expected error: %s received: %v", expErrMsg, err) } } @@ -1090,7 +1090,7 @@ func TestLibDispatcherDispatchHostNotFound(t *testing.T) { ID: "testID", }}, } - if err := dsp.Dispatch(db, flts, nil, "", "", "", "", "", ""); err != utils.ErrHostNotFound { + if err := dsp.Dispatch(db, flts, nil, "", "", &DispatcherRoute{}, "", "", ""); err != utils.ErrHostNotFound { t.Errorf("Expected error: %s received: %v", utils.ErrHostNotFound, err) } } diff --git a/dispatchers/sessions_it_test.go b/dispatchers/sessions_it_test.go index eb283c649..61bdbfa30 100644 --- a/dispatchers/sessions_it_test.go +++ b/dispatchers/sessions_it_test.go @@ -61,7 +61,7 @@ var sTestsDspSession = []func(t *testing.T){ testDspSessionForceDisconect, } -//Test start here +// Test start here func TestDspSessionS(t *testing.T) { var config1, config2, config3 string switch *dbType { @@ -405,7 +405,7 @@ func testDspSessionUpdate(t *testing.T) { APIOpts: map[string]interface{}{ utils.OptsAPIKey: "ses12345", "*attrProfileIDs": nil, - utils.Subsys: utils.MetaSessionS, + utils.MetaSubsys: utils.MetaSessionS, }, }, } @@ -480,7 +480,7 @@ func testDspSessionUpdate2(t *testing.T) { APIOpts: map[string]interface{}{ utils.OptsAPIKey: "ses12345", "*attrProfileIDs": nil, - utils.Subsys: utils.MetaSessionS, + utils.MetaSubsys: utils.MetaSessionS, }, }, } @@ -637,7 +637,7 @@ func testDspSessionProcessEvent(t *testing.T) { "*attrProfileIDs": nil, "*rsUnits": 1., "*rsUsageID": "TestSSv1It2", - utils.Subsys: utils.MetaSessionS, + utils.MetaSubsys: utils.MetaSessionS, }, }, } @@ -718,7 +718,7 @@ func testDspSessionProcessEvent2(t *testing.T) { "*attrProfileIDs": nil, "*rsUnits": 1., "*rsUsageID": "TestSSv1It2", - utils.Subsys: utils.MetaSessionS, + utils.MetaSubsys: utils.MetaSessionS, }, }, } diff --git a/ees/ees.go b/ees/ees.go index 89846999d..e501d0d0c 100644 --- a/ees/ees.go +++ b/ees/ees.go @@ -106,7 +106,7 @@ func (eeS *EventExporterS) attrSProcessEvent(cgrEv *utils.CGREvent, attrIDs []st if cgrEv.APIOpts == nil { cgrEv.APIOpts = make(map[string]interface{}) } - cgrEv.APIOpts[utils.Subsys] = utils.MetaEEs + cgrEv.APIOpts[utils.MetaSubsys] = utils.MetaEEs cgrEv.APIOpts[utils.OptsAttributesProfileIDs] = attrIDs cgrEv.APIOpts[utils.OptsContext] = utils.FirstNonEmpty(ctx, utils.IfaceAsString(cgrEv.APIOpts[utils.OptsContext]), utils.MetaEEs) diff --git a/ees/rpc_test.go b/ees/rpc_test.go index bc0eb1f59..939c63568 100644 --- a/ees/rpc_test.go +++ b/ees/rpc_test.go @@ -175,7 +175,7 @@ func TestRPCPrepareMap(t *testing.T) { utils.Usage: 21, }, APIOpts: map[string]interface{}{ - utils.Subsys: "*attributes", + utils.MetaSubsys: "*attributes", }, } diff --git a/engine/caches.go b/engine/caches.go index 3a75efc6f..0177d7424 100644 --- a/engine/caches.go +++ b/engine/caches.go @@ -36,19 +36,21 @@ var Cache *CacheS func init() { Cache = NewCacheS(config.CgrConfig(), nil, nil) + // Register objects for cache replication/remotes + //AttributeS gob.Register(new(AttributeProfile)) gob.Register(new(AttributeProfileWithAPIOpts)) - // Threshold + // ThresholdS gob.Register(new(Threshold)) gob.Register(new(ThresholdProfile)) gob.Register(new(ThresholdProfileWithAPIOpts)) gob.Register(new(ThresholdWithAPIOpts)) - // Resource + // ResourceS gob.Register(new(Resource)) gob.Register(new(ResourceProfile)) gob.Register(new(ResourceProfileWithAPIOpts)) gob.Register(new(ResourceWithAPIOpts)) - // Stats + // StatS gob.Register(new(StatQueue)) gob.Register(new(StatQueueProfile)) gob.Register(new(StatQueueProfileWithAPIOpts)) @@ -57,10 +59,10 @@ func init() { // RouteS gob.Register(new(RouteProfile)) gob.Register(new(RouteProfileWithAPIOpts)) - // Filters + // FilterS gob.Register(new(Filter)) gob.Register(new(FilterWithAPIOpts)) - // Dispatcher + // DispatcherS gob.Register(new(DispatcherHost)) gob.Register(new(DispatcherHostProfile)) gob.Register(new(DispatcherHostWithAPIOpts)) @@ -80,6 +82,7 @@ func init() { gob.Register(new(StatAverage)) gob.Register(new(StatDistinct)) + // others gob.Register([]interface{}{}) gob.Register([]map[string]interface{}{}) gob.Register(map[string]interface{}{}) @@ -151,6 +154,21 @@ func (chS *CacheS) Set(chID, itmID string, value interface{}, return chS.ReplicateSet(chID, itmID, value) } +// ReplicateSet replicates an item to ReplicationConns +func (chS *CacheS) ReplicateSet(chID, itmID string, value interface{}) (err error) { + if len(chS.cfg.CacheCfg().ReplicationConns) == 0 || + !chS.cfg.CacheCfg().Partitions[chID].Replicate { + return + } + var reply string + return connMgr.Call(chS.cfg.CacheCfg().ReplicationConns, nil, utils.CacheSv1ReplicateSet, + &utils.ArgCacheReplicateSet{ + CacheID: chID, + ItemID: itmID, + Value: value, + }, &reply) +} + // SetWithoutReplicate is an exported method from TransCache // handled Replicate functionality func (chS *CacheS) SetWithoutReplicate(chID, itmID string, value interface{}, @@ -158,6 +176,18 @@ func (chS *CacheS) SetWithoutReplicate(chID, itmID string, value interface{}, chS.tCache.Set(chID, itmID, value, groupIDs, commit, transID) } +// SetWithReplicate combines local set with replicate, receiving the arguments needed by dispatcher +func (chS *CacheS) SetWithReplicate(args *utils.ArgCacheReplicateSet) (err error) { + chS.tCache.Set(args.CacheID, args.ItemID, args.Value, args.GroupIDs, true, utils.EmptyString) + if len(chS.cfg.CacheCfg().ReplicationConns) == 0 || + !chS.cfg.CacheCfg().Partitions[args.CacheID].Replicate { + return + } + var reply string + return connMgr.Call(chS.cfg.CacheCfg().ReplicationConns, nil, + utils.CacheSv1ReplicateSet, args, &reply) +} + // HasItem is an exported method from TransCache func (chS *CacheS) HasItem(chID, itmID string) (has bool) { return chS.tCache.HasItem(chID, itmID) @@ -168,6 +198,26 @@ func (chS *CacheS) Get(chID, itmID string) (interface{}, bool) { return chS.tCache.Get(chID, itmID) } +// GetWithRemote queries locally the cache, followed by remotes +func (chS *CacheS) GetWithRemote(args *utils.ArgsGetCacheItemWithAPIOpts) (itm interface{}, err error) { + var has bool + if itm, has = chS.tCache.Get(args.CacheID, args.ItemID); has { + return + } + if len(chS.cfg.CacheCfg().RemoteConns) == 0 || + !chS.cfg.CacheCfg().Partitions[args.CacheID].Remote { + return + } + // item was not found locally, query from remote + var itmRemote interface{} + if err = connMgr.Call(chS.cfg.CacheCfg().RemoteConns, nil, + utils.CacheSv1GetItem, args, &itmRemote); err != nil && + err.Error() == utils.ErrNotFound.Error() { + return nil, utils.ErrNotFound // correct the error coming as string type + } + return +} + // GetItemIDs is an exported method from TransCache func (chS *CacheS) GetItemIDs(chID, prfx string) (itmIDs []string) { return chS.tCache.GetItemIDs(chID, prfx) @@ -276,6 +326,28 @@ func (chS *CacheS) V1HasItem(args *utils.ArgsGetCacheItemWithAPIOpts, return } +// V1GetItem returns a single item from the cache +func (chS *CacheS) V1GetItem(args *utils.ArgsGetCacheItemWithAPIOpts, + reply *interface{}) (err error) { + itmIface, has := chS.tCache.Get(args.CacheID, args.ItemID) + if !has { + return utils.ErrNotFound + } + *reply = itmIface + return +} + +// V1GetItemWithRemote queries the item from remote if not found locally +func (chS *CacheS) V1GetItemWithRemote(args *utils.ArgsGetCacheItemWithAPIOpts, + reply *interface{}) (err error) { + var itmIface interface{} + if itmIface, err = chS.GetWithRemote(args); err != nil { + return + } + *reply = itmIface + return +} + func (chS *CacheS) V1GetItemExpiryTime(args *utils.ArgsGetCacheItemWithAPIOpts, reply *time.Time) (err error) { expTime, has := chS.tCache.GetItemExpiryTime(args.CacheID, args.ItemID) @@ -394,7 +466,7 @@ func (chS *CacheS) cacheDataFromDB(attrs *utils.AttrReloadCacheWithAPIOpts, repl return } -//populateCacheLoadIDs populate cacheLoadIDs based on attrs +// populateCacheLoadIDs populate cacheLoadIDs based on attrs func populateCacheLoadIDs(loadIDs map[string]int64, attrs map[string][]string) (cacheLoadIDs map[string]int64) { cacheLoadIDs = make(map[string]int64) //based on IDs of each type populate cacheLoadIDs and add into cache @@ -406,22 +478,7 @@ func populateCacheLoadIDs(loadIDs map[string]int64, attrs map[string][]string) ( return } -// ReplicateSet replicate an item to ReplicationConns -func (chS *CacheS) ReplicateSet(chID, itmID string, value interface{}) (err error) { - if len(chS.cfg.CacheCfg().ReplicationConns) == 0 || - !chS.cfg.CacheCfg().Partitions[chID].Replicate { - return - } - var reply string - return connMgr.Call(chS.cfg.CacheCfg().ReplicationConns, nil, utils.CacheSv1ReplicateSet, - &utils.ArgCacheReplicateSet{ - CacheID: chID, - ItemID: itmID, - Value: value, - }, &reply) -} - -// V1ReplicateSet replicate an item +// V1ReplicateSet receives an item via replication to store in the cache func (chS *CacheS) V1ReplicateSet(args *utils.ArgCacheReplicateSet, reply *string) (err error) { if cmp, canCast := args.Value.(utils.Compiler); canCast { if err = cmp.Compile(); err != nil { diff --git a/engine/cdrs.go b/engine/cdrs.go index de15477d1..8665426d2 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -373,7 +373,7 @@ func (cdrS *CDRServer) attrSProcessEvent(cgrEv *utils.CGREvent) (err error) { if cgrEv.APIOpts == nil { cgrEv.APIOpts = make(map[string]interface{}) } - cgrEv.APIOpts[utils.Subsys] = utils.MetaCDRs + cgrEv.APIOpts[utils.MetaSubsys] = utils.MetaCDRs ctx, has := cgrEv.APIOpts[utils.OptsContext] cgrEv.APIOpts[utils.OptsContext] = utils.FirstNonEmpty( utils.IfaceAsString(ctx), diff --git a/engine/chargers.go b/engine/chargers.go index 8911852f6..20bc1a1c7 100644 --- a/engine/chargers.go +++ b/engine/chargers.go @@ -114,7 +114,7 @@ func (cS *ChargerService) processEvent(tnt string, cgrEv *utils.CGREvent) (rply clonedEv := cgrEv.Clone() clonedEv.Tenant = tnt clonedEv.Event[utils.RunID] = cP.RunID - clonedEv.APIOpts[utils.Subsys] = utils.MetaChargers + clonedEv.APIOpts[utils.MetaSubsys] = utils.MetaChargers rply[i] = &ChrgSProcessEventReply{ ChargerSProfile: cP.ID, CGREvent: clonedEv, diff --git a/engine/chargers_test.go b/engine/chargers_test.go index 5d39a0c73..5c5c39522 100644 --- a/engine/chargers_test.go +++ b/engine/chargers_test.go @@ -192,7 +192,7 @@ func TestChargerMatchingChargerProfilesForEvent(t *testing.T) { utils.Weight: "200.0", }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, }, }, { @@ -359,7 +359,7 @@ func TestChargerProcessEvent(t *testing.T) { utils.Weight: "200.0", }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, }, }, { @@ -521,7 +521,7 @@ func TestChargersmatchingChargerProfilesForEventChargerProfileNotFound(t *testin utils.Weight: "10.0", }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, }, } @@ -567,7 +567,7 @@ func TestChargersmatchingChargerProfilesForEventDoesNotPass(t *testing.T) { utils.Weight: "10.0", }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, }, } @@ -617,7 +617,7 @@ func TestChargersmatchingChargerProfilesForEventErrGetChPrf(t *testing.T) { utils.Weight: "10.0", }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, }, } diff --git a/engine/routes.go b/engine/routes.go index cd1c66e52..fe97a39d3 100644 --- a/engine/routes.go +++ b/engine/routes.go @@ -574,7 +574,7 @@ func (rpS *RouteService) V1GetRoutes(args *utils.CGREvent, reply *SortedRoutesLi if args.APIOpts == nil { args.APIOpts = make(map[string]interface{}) } - args.APIOpts[utils.Subsys] = utils.MetaRoutes + args.APIOpts[utils.MetaSubsys] = utils.MetaRoutes context := utils.GetStringOpts(args, rpS.cgrcfg.RouteSCfg().Opts.Context, utils.OptsContext) args.APIOpts[utils.OptsContext] = utils.FirstNonEmpty(context, utils.MetaRoutes) var rplyEv AttrSProcessEventReply diff --git a/engine/tpreader_test.go b/engine/tpreader_test.go index 7d320d414..10e6d0aeb 100644 --- a/engine/tpreader_test.go +++ b/engine/tpreader_test.go @@ -47,7 +47,7 @@ func TestTPReaderCallCacheNoCaching(t *testing.T) { utils.CacheResources: {}, } opts := map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, } err := CallCache(cM, []string{}, utils.MetaNone, args, []string{}, opts, true, "cgrates.org") @@ -73,7 +73,7 @@ func TestTPReaderCallCacheReloadCacheFirstCallErr(t *testing.T) { utils.CacheSv1ReloadCache: func(args, reply interface{}) error { expArgs := &utils.AttrReloadCacheWithAPIOpts{ APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, }, FilterIDs: []string{"cgrates.org:FLTR_ID1", "cgrates.org:FLTR_ID2"}, Tenant: "cgrates.org", @@ -100,7 +100,7 @@ func TestTPReaderCallCacheReloadCacheFirstCallErr(t *testing.T) { } cacheIDs := []string{} opts := map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, } var buf bytes.Buffer @@ -141,7 +141,7 @@ func TestTPReaderCallCacheReloadCacheSecondCallErr(t *testing.T) { utils.CacheSv1Clear: func(args, reply interface{}) error { expArgs := &utils.AttrCacheIDsWithAPIOpts{ APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, }, CacheIDs: []string{"cacheID"}, Tenant: "cgrates.org", @@ -168,7 +168,7 @@ func TestTPReaderCallCacheReloadCacheSecondCallErr(t *testing.T) { } cacheIDs := []string{"cacheID"} opts := map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, } var buf bytes.Buffer @@ -218,7 +218,7 @@ func TestTPReaderCallCacheLoadCache(t *testing.T) { utils.CacheSv1LoadCache: func(args, reply interface{}) error { expArgs := &utils.AttrReloadCacheWithAPIOpts{ APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, }, FilterIDs: []string{"cgrates.org:FLTR_ID1", "cgrates.org:FLTR_ID2"}, Tenant: "cgrates.org", @@ -235,7 +235,7 @@ func TestTPReaderCallCacheLoadCache(t *testing.T) { utils.CacheSv1Clear: func(args, reply interface{}) error { expArgs := &utils.AttrCacheIDsWithAPIOpts{ APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, }, CacheIDs: []string{"cacheID"}, Tenant: "cgrates.org", @@ -262,7 +262,7 @@ func TestTPReaderCallCacheLoadCache(t *testing.T) { } cacheIDs := []string{"cacheID"} opts := map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, } err := CallCache(cM, cacheConns, caching, args, cacheIDs, opts, false, "cgrates.org") @@ -287,7 +287,7 @@ func TestTPReaderCallCacheRemoveItems(t *testing.T) { utils.CacheSv1RemoveItems: func(args, reply interface{}) error { expArgs := &utils.AttrReloadCacheWithAPIOpts{ APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, }, FilterIDs: []string{"cgrates.org:FLTR_ID1", "cgrates.org:FLTR_ID2"}, Tenant: "cgrates.org", @@ -304,7 +304,7 @@ func TestTPReaderCallCacheRemoveItems(t *testing.T) { utils.CacheSv1Clear: func(args, reply interface{}) error { expArgs := &utils.AttrCacheIDsWithAPIOpts{ APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, }, CacheIDs: []string{"cacheID"}, Tenant: "cgrates.org", @@ -331,7 +331,7 @@ func TestTPReaderCallCacheRemoveItems(t *testing.T) { } cacheIDs := []string{"cacheID"} opts := map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, } err := CallCache(cM, cacheConns, caching, args, cacheIDs, opts, false, "cgrates.org") @@ -356,7 +356,7 @@ func TestTPReaderCallCacheClear(t *testing.T) { utils.CacheSv1Clear: func(args, reply interface{}) error { expArgs := &utils.AttrCacheIDsWithAPIOpts{ APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, }, Tenant: "cgrates.org", } @@ -382,7 +382,7 @@ func TestTPReaderCallCacheClear(t *testing.T) { } cacheIDs := []string{} opts := map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, } err := CallCache(cM, cacheConns, caching, args, cacheIDs, opts, false, "cgrates.org") diff --git a/engine/z_chargers_test.go b/engine/z_chargers_test.go index ac34eac35..4baa17496 100644 --- a/engine/z_chargers_test.go +++ b/engine/z_chargers_test.go @@ -70,7 +70,7 @@ func TestChargersmatchingChargerProfilesForEventErrPass(t *testing.T) { utils.Weight: "10.0", }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, }, Time: &cgrEvTm, } @@ -131,7 +131,7 @@ func TestChargersmatchingChargerProfilesForEventNotActive(t *testing.T) { utils.Weight: "10.0", }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, }, Time: &cgrEvTm, } @@ -192,7 +192,7 @@ func TestChargersprocessEventNoConnIDs(t *testing.T) { utils.Weight: "10.0", }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, utils.OptsAttributesProcessRuns: 2, }, Time: &cgrEvTm, @@ -373,7 +373,7 @@ func TestChargersprocessEventCallErr(t *testing.T) { "RunID": utils.MetaDefault, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, utils.OptsAttributesProfileIDs: []string(nil), }, }, diff --git a/general_tests/dispatcher_opts_it_test.go b/general_tests/dispatcher_opts_it_test.go new file mode 100644 index 000000000..3bd280549 --- /dev/null +++ b/general_tests/dispatcher_opts_it_test.go @@ -0,0 +1,646 @@ +//go:build integration +// +build integration + +/* +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 general_tests + +import ( + "net/rpc" + "path" + "testing" + "time" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +var ( + setterCfgPath string + setterCfg *config.CGRConfig + setterRPC *rpc.Client + + dspOptsCfgPath string + apierCfgPath string + dspOptsCfg *config.CGRConfig + apierCfg *config.CGRConfig + dspOptsRPC *rpc.Client + apierRPC *rpc.Client + dspOptsConfigDIR string + dpsOptsTest = []func(t *testing.T){ + testDispatcherOptsSetterInitCfg, + testDispatcherOptsSetterInitDataDb, + testDispatcherOptsSetterStartEngine, + testDispatcherOptsSetterRPCConn, + // Start engine without Dispatcher on engine 4012 + testDispatcherOptsAPIerInitCfg, + testDispatcherOptsAPIerInitDataDb, + testDispatcherOptsAPIerStartEngine, + testDispatcherOptsAPIerRPCConn, + testDispatcherOptsSetterSetDispatcherProfile, + // testDispatcherOptsAPIerSetDispatcherProfile, + + // Start engine without Dispatcher on engine 2012 with profiles in database (*dispatchers:false) + testDispatcherOptsDSPInitCfg, + testDispatcherOptsDSPStartEngine, + testDispatcherOptsDSPRPCConn, + testDispatcherOptsCoreStatus, // self localhost(:2012) CoresV1Status + + testDispatcherOptsSetterSetDispatcherHost4012, + // testDispatcherOptsAPIerSetDispatcherHost4012, + testDispatcherOptsCoreStatusHost4012, + + testDispatcherOptsSetterSetDispatcherProfileDoubleHost, + // testDispatcherOptsAPIerSetDispatcherProfileDoubleHost, + testDispatcherOptsCoreStatusWithRouteID, + + testDispatcherOptsSetterSetDispatcherHostInexistent, + // testDispatcherOptsAPIerSetDispatcherHostInexistent, + testDispatcherOptsCoreStatusWithRouteID2, + + testDispatcherOptsCoreStatusWithoutRouteID, + + testDispatcherOptsDSPStopEngine, + testDispatcherOptsAPIerStopEngine, + // testDispatcherOptsSetterStopEngine, + } +) + +func TestDispatcherOpts(t *testing.T) { + for _, test := range dpsOptsTest { + t.Run(dspOptsConfigDIR, test) + } +} + +func testDispatcherOptsAPIerInitCfg(t *testing.T) { + dspOptsConfigDIR = "dispatcher_opts_apier" + var err error + apierCfgPath = path.Join(*dataDir, "conf", "samples", dspOptsConfigDIR) + apierCfg, err = config.NewCGRConfigFromPath(apierCfgPath) + if err != nil { + t.Error(err) + } +} + +func testDispatcherOptsAPIerInitDataDb(t *testing.T) { + if err := engine.InitDataDb(apierCfg); err != nil { + t.Fatal(err) + } +} + +// Start CGR Engine woth Dispatcher enabled +func testDispatcherOptsAPIerStartEngine(t *testing.T) { + if _, err := engine.StartEngine(apierCfgPath, *waitRater); err != nil { + t.Fatal(err) + } +} + +func testDispatcherOptsAPIerRPCConn(t *testing.T) { + var err error + apierRPC, err = newRPCClient(apierCfg.ListenCfg()) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } +} + +func testDispatcherOptsAPIerSetDispatcherProfile(t *testing.T) { + // Set DispatcherHost + var replyStr string + setDispatcherHost := &engine.DispatcherHostWithAPIOpts{ + DispatcherHost: &engine.DispatcherHost{ + Tenant: "cgrates.org", + RemoteHost: &config.RemoteHost{ + ID: "SELF_ENGINE", + Address: "127.0.0.1:4012", + Transport: "*json", + ConnectAttempts: 1, + Reconnects: 3, + ConnectTimeout: time.Minute, + ReplyTimeout: 2 * time.Minute, + }, + }, + APIOpts: map[string]interface{}{ + utils.OptsDispatchers: false, + }, + } + if err := apierRPC.Call(utils.APIerSv1SetDispatcherHost, setDispatcherHost, &replyStr); err != nil { + t.Error("Unexpected error when calling APIerSv1.SetDispatcherHost: ", err) + } else if replyStr != utils.OK { + t.Error("Unexpected reply returned", replyStr) + } + + // Set DispatcherProfile + setDispatcherProfile := &engine.DispatcherProfileWithAPIOpts{ + DispatcherProfile: &engine.DispatcherProfile{ + Tenant: "cgrates.org", + ID: "DSP1", + Strategy: "*weight", + Subsystems: []string{utils.MetaAny}, + Weight: 10, + Hosts: engine.DispatcherHostProfiles{ + { + ID: "SELF_ENGINE", + Weight: 5, + }, + }, + }, + APIOpts: map[string]interface{}{ + utils.OptsDispatchers: false, + }, + } + if err := apierRPC.Call(utils.APIerSv1SetDispatcherProfile, setDispatcherProfile, &replyStr); err != nil { + t.Error("Unexpected error when calling APIerSv1.SetDispatcherProfile: ", err) + } else if replyStr != utils.OK { + t.Error("Unexpected reply returned", replyStr) + } +} + +func testDispatcherOptsDSPInitCfg(t *testing.T) { + dspOptsConfigDIR = "dispatcher_opts" //changed with the cfg with dispatcher on + var err error + dspOptsCfgPath = path.Join(*dataDir, "conf", "samples", dspOptsConfigDIR) + dspOptsCfg, err = config.NewCGRConfigFromPath(dspOptsCfgPath) + if err != nil { + t.Error(err) + } +} + +// Start CGR Engine woth Dispatcher enabled +func testDispatcherOptsDSPStartEngine(t *testing.T) { + if _, err := engine.StartEngine(dspOptsCfgPath, *waitRater); err != nil { + t.Fatal(err) + } +} + +func testDispatcherOptsDSPRPCConn(t *testing.T) { + var err error + dspOptsRPC, err = newRPCClient(dspOptsCfg.ListenCfg()) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } +} + +func testDispatcherOptsCoreStatus(t *testing.T) { + //SELF_ENGINE HOST + var reply map[string]interface{} + ev := utils.TenantWithAPIOpts{ + Tenant: "cgrates.org", + APIOpts: map[string]interface{}{ + utils.OptsDispatchers: false, + }, + } + if err := dspOptsRPC.Call(utils.CoreSv1Status, &ev, &reply); err != nil { + t.Error(err) + } else { + /* + t.Errorf("Received: %s", utils.ToJSON(reply)) + */ + } +} + +func testDispatcherOptsAPIerSetDispatcherHost4012(t *testing.T) { + // Set DispatcherHost on 4012 host + var replyStr string + setDispatcherHost := &engine.DispatcherHostWithAPIOpts{ + DispatcherHost: &engine.DispatcherHost{ + Tenant: "cgrates.org", + RemoteHost: &config.RemoteHost{ + ID: "HOST4012", + Address: "127.0.0.1:4012", + Transport: "*json", + ConnectAttempts: 1, + Reconnects: 3, + ConnectTimeout: time.Minute, + ReplyTimeout: 2 * time.Minute, + }, + }, + APIOpts: map[string]interface{}{ + utils.OptsDispatchers: false, + }, + } + if err := apierRPC.Call(utils.APIerSv1SetDispatcherHost, setDispatcherHost, &replyStr); err != nil { + t.Error("Unexpected error when calling APIerSv1.SetDispatcherHost: ", err) + } else if replyStr != utils.OK { + t.Error("Unexpected reply returned", replyStr) + } + + // Set DispatcherProfile + setDispatcherProfile := &engine.DispatcherProfileWithAPIOpts{ + DispatcherProfile: &engine.DispatcherProfile{ + Tenant: "cgrates.org", + ID: "DSP1", + Strategy: "*weight", + Subsystems: []string{utils.MetaAny}, + Weight: 10, + Hosts: engine.DispatcherHostProfiles{ + { + ID: "HOST4012", + Weight: 10, + }, + }, + }, + APIOpts: map[string]interface{}{ + utils.OptsDispatchers: false, + }, + } + if err := apierRPC.Call(utils.APIerSv1SetDispatcherProfile, setDispatcherProfile, &replyStr); err != nil { + t.Error("Unexpected error when calling APIerSv1.SetDispatcherProfile: ", err) + } else if replyStr != utils.OK { + t.Error("Unexpected reply returned", replyStr) + } +} + +func testDispatcherOptsCoreStatusHost4012(t *testing.T) { + // status just for HOST4012 + var reply map[string]interface{} + ev := utils.TenantWithAPIOpts{ + Tenant: "cgrates.org", + } + if err := dspOptsRPC.Call(utils.CoreSv1Status, &ev, &reply); err != nil { + t.Error(err) + } else { + /* + t.Errorf("Received: %s", utils.ToJSON(reply)) + */ + } +} + +func testDispatcherOptsAPIerSetDispatcherProfileDoubleHost(t *testing.T) { + // Set DispatcherProfile with both engines + setDispatcherProfile := &engine.DispatcherProfileWithAPIOpts{ + DispatcherProfile: &engine.DispatcherProfile{ + Tenant: "cgrates.org", + ID: "DSP1", + Strategy: "*weight", + Subsystems: []string{utils.MetaAny}, + Weight: 10, + Hosts: engine.DispatcherHostProfiles{ + { + ID: "SELF_ENGINE", + Weight: 5, + }, + { + ID: "HOST4012", + Weight: 10, + }, + }, + }, + APIOpts: map[string]interface{}{ + utils.OptsDispatchers: false, + }, + } + var replyStr string + if err := apierRPC.Call(utils.APIerSv1SetDispatcherProfile, setDispatcherProfile, &replyStr); err != nil { + t.Error("Unexpected error when calling APIerSv1.SetDispatcherProfile: ", err) + } else if replyStr != utils.OK { + t.Error("Unexpected reply returned", replyStr) + } +} + +func testDispatcherOptsCoreStatusWithRouteID(t *testing.T) { + // now it will dispatch in both engines + var reply map[string]interface{} + ev := utils.TenantWithAPIOpts{ + Tenant: "cgrates.org", + APIOpts: map[string]interface{}{ + utils.OptsRouteID: "account#dan.bogos", + }, + } + if err := dspOptsRPC.Call(utils.CoreSv1Status, &ev, &reply); err != nil { + t.Error(err) + } else { + /* + t.Errorf("Received: %s", utils.ToJSON(reply)) + */ + } +} + +func testDispatcherOptsAPIerSetDispatcherHostInexistent(t *testing.T) { + // Set DispatcherHost on 4012 host + var replyStr string + setDispatcherHost := &engine.DispatcherHostWithAPIOpts{ + DispatcherHost: &engine.DispatcherHost{ + Tenant: "cgrates.org", + RemoteHost: &config.RemoteHost{ + ID: "INEXISTENT", + Address: "127.0.0.1:1223", + Transport: "*json", + ConnectAttempts: 1, + Reconnects: 3, + ConnectTimeout: time.Minute, + ReplyTimeout: 2 * time.Minute, + }, + }, + APIOpts: map[string]interface{}{ + utils.OptsDispatchers: false, + }, + } + if err := apierRPC.Call(utils.APIerSv1SetDispatcherHost, setDispatcherHost, &replyStr); err != nil { + t.Error("Unexpected error when calling APIerSv1.SetDispatcherHost: ", err) + } else if replyStr != utils.OK { + t.Error("Unexpected reply returned", replyStr) + } + + // Set DispatcherProfile Different with an inexistent engine opened, but with a bigger weight(this should match now) + setDispatcherProfile := &engine.DispatcherProfileWithAPIOpts{ + DispatcherProfile: &engine.DispatcherProfile{ + Tenant: "cgrates.org", + ID: "DSP1", + Strategy: "*weight", + Subsystems: []string{utils.MetaAny}, + Weight: 20, + Hosts: engine.DispatcherHostProfiles{ + { + ID: "INEXISTENT", + Weight: 10, + }, + }, + }, + APIOpts: map[string]interface{}{ + utils.OptsDispatchers: false, + }, + } + if err := apierRPC.Call(utils.APIerSv1SetDispatcherProfile, setDispatcherProfile, &replyStr); err != nil { + t.Error("Unexpected error when calling APIerSv1.SetDispatcherProfile: ", err) + } else if replyStr != utils.OK { + t.Error("Unexpected reply returned", replyStr) + } +} + +func testDispatcherOptsCoreStatusWithRouteID2(t *testing.T) { + // because we have the routeID it will match DSP1 and last host matched, host4012 + // so again, both engines will match + var reply map[string]interface{} + ev := utils.TenantWithAPIOpts{ + Tenant: "cgrates.org", + APIOpts: map[string]interface{}{ + utils.OptsRouteID: "account#dan.bogos", + }, + } + if err := dspOptsRPC.Call(utils.CoreSv1Status, &ev, &reply); err != nil { + t.Error(err) + } else { + /* + t.Errorf("Received: %s", utils.ToJSON(reply)) + */ + } +} + +func testDispatcherOptsCoreStatusWithoutRouteID(t *testing.T) { + // because we have the routeID it will match DSP1 and last host matched, host4012 + // so again, both engines will match + var reply map[string]interface{} + ev := utils.TenantWithAPIOpts{ + Tenant: "cgrates.org", + } + if err := dspOptsRPC.Call(utils.CoreSv1Status, &ev, &reply); err != nil { + t.Error(err) + } else { + /* + t.Errorf("Received: %s", utils.ToJSON(reply)) + */ + } +} + +func testDispatcherOptsDSPStopEngine(t *testing.T) { + if err := engine.KillEngine(*waitRater); err != nil { + t.Error(err) + } +} + +func testDispatcherOptsAPIerStopEngine(t *testing.T) { + if err := engine.KillEngine(*waitRater); err != nil { + t.Error(err) + } +} + +// ---------------------------- + +func testDispatcherOptsSetterInitCfg(t *testing.T) { + dspOptsConfigDIR = "dispatcher_opts_setter" + var err error + setterCfgPath = path.Join(*dataDir, "conf", "samples", dspOptsConfigDIR) + setterCfg, err = config.NewCGRConfigFromPath(setterCfgPath) + if err != nil { + t.Error(err) + } +} +func testDispatcherOptsSetterInitDataDb(t *testing.T) { + if err := engine.InitDataDb(setterCfg); err != nil { + t.Fatal(err) + } +} +func testDispatcherOptsSetterStartEngine(t *testing.T) { + if _, err := engine.StartEngine(setterCfgPath, *waitRater); err != nil { + t.Fatal(err) + } +} +func testDispatcherOptsSetterRPCConn(t *testing.T) { + var err error + setterRPC, err = newRPCClient(setterCfg.ListenCfg()) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } +} +func testDispatcherOptsSetterSetDispatcherProfile(t *testing.T) { + // Set DispatcherHost + var replyStr string + setDispatcherHost := &engine.DispatcherHostWithAPIOpts{ + DispatcherHost: &engine.DispatcherHost{ + Tenant: "cgrates.org", + RemoteHost: &config.RemoteHost{ + ID: "SELF_ENGINE", + Address: "127.0.0.1:4012", + Transport: "*json", + ConnectAttempts: 1, + Reconnects: 3, + ConnectTimeout: time.Minute, + ReplyTimeout: 2 * time.Minute, + }, + }, + APIOpts: map[string]interface{}{ + utils.OptsDispatchers: false, + }, + } + if err := setterRPC.Call(utils.APIerSv1SetDispatcherHost, setDispatcherHost, &replyStr); err != nil { + t.Error("Unexpected error when calling APIerSv1.SetDispatcherHost: ", err) + } else if replyStr != utils.OK { + t.Error("Unexpected reply returned", replyStr) + } + + // Set DispatcherProfile + setDispatcherProfile := &engine.DispatcherProfileWithAPIOpts{ + DispatcherProfile: &engine.DispatcherProfile{ + Tenant: "cgrates.org", + ID: "DSP1", + Strategy: "*weight", + Subsystems: []string{utils.MetaAny}, + Weight: 10, + Hosts: engine.DispatcherHostProfiles{ + { + ID: "SELF_ENGINE", + Weight: 5, + }, + }, + }, + APIOpts: map[string]interface{}{ + utils.OptsDispatchers: false, + }, + } + if err := setterRPC.Call(utils.APIerSv1SetDispatcherProfile, setDispatcherProfile, &replyStr); err != nil { + t.Error("Unexpected error when calling APIerSv1.SetDispatcherProfile: ", err) + } else if replyStr != utils.OK { + t.Error("Unexpected reply returned", replyStr) + } +} +func testDispatcherOptsSetterSetDispatcherHost4012(t *testing.T) { + // Set DispatcherHost on 4012 host + var replyStr string + setDispatcherHost := &engine.DispatcherHostWithAPIOpts{ + DispatcherHost: &engine.DispatcherHost{ + Tenant: "cgrates.org", + RemoteHost: &config.RemoteHost{ + ID: "HOST4012", + Address: "127.0.0.1:4012", + Transport: "*json", + ConnectAttempts: 1, + Reconnects: 3, + ConnectTimeout: time.Minute, + ReplyTimeout: 2 * time.Minute, + }, + }, + APIOpts: map[string]interface{}{ + utils.OptsDispatchers: false, + }, + } + if err := setterRPC.Call(utils.APIerSv1SetDispatcherHost, setDispatcherHost, &replyStr); err != nil { + t.Error("Unexpected error when calling APIerSv1.SetDispatcherHost: ", err) + } else if replyStr != utils.OK { + t.Error("Unexpected reply returned", replyStr) + } + + // Set DispatcherProfile + setDispatcherProfile := &engine.DispatcherProfileWithAPIOpts{ + DispatcherProfile: &engine.DispatcherProfile{ + Tenant: "cgrates.org", + ID: "DSP1", + Strategy: "*weight", + Subsystems: []string{utils.MetaAny}, + Weight: 10, + Hosts: engine.DispatcherHostProfiles{ + { + ID: "HOST4012", + Weight: 10, + }, + }, + }, + APIOpts: map[string]interface{}{ + utils.OptsDispatchers: false, + }, + } + if err := setterRPC.Call(utils.APIerSv1SetDispatcherProfile, setDispatcherProfile, &replyStr); err != nil { + t.Error("Unexpected error when calling APIerSv1.SetDispatcherProfile: ", err) + } else if replyStr != utils.OK { + t.Error("Unexpected reply returned", replyStr) + } +} +func testDispatcherOptsSetterSetDispatcherProfileDoubleHost(t *testing.T) { + // Set DispatcherProfile with both engines + setDispatcherProfile := &engine.DispatcherProfileWithAPIOpts{ + DispatcherProfile: &engine.DispatcherProfile{ + Tenant: "cgrates.org", + ID: "DSP1", + Strategy: "*weight", + Subsystems: []string{utils.MetaAny}, + Weight: 10, + Hosts: engine.DispatcherHostProfiles{ + { + ID: "SELF_ENGINE", + Weight: 5, + }, + { + ID: "HOST4012", + Weight: 10, + }, + }, + }, + APIOpts: map[string]interface{}{ + utils.OptsDispatchers: false, + }, + } + var replyStr string + if err := setterRPC.Call(utils.APIerSv1SetDispatcherProfile, setDispatcherProfile, &replyStr); err != nil { + t.Error("Unexpected error when calling APIerSv1.SetDispatcherProfile: ", err) + } else if replyStr != utils.OK { + t.Error("Unexpected reply returned", replyStr) + } +} +func testDispatcherOptsSetterSetDispatcherHostInexistent(t *testing.T) { + // Set DispatcherHost on 4012 host + var replyStr string + setDispatcherHost := &engine.DispatcherHostWithAPIOpts{ + DispatcherHost: &engine.DispatcherHost{ + Tenant: "cgrates.org", + RemoteHost: &config.RemoteHost{ + ID: "INEXISTENT", + Address: "127.0.0.1:1223", + Transport: "*json", + ConnectAttempts: 1, + Reconnects: 3, + ConnectTimeout: time.Minute, + ReplyTimeout: 2 * time.Minute, + }, + }, + APIOpts: map[string]interface{}{ + utils.OptsDispatchers: false, + }, + } + if err := setterRPC.Call(utils.APIerSv1SetDispatcherHost, setDispatcherHost, &replyStr); err != nil { + t.Error("Unexpected error when calling APIerSv1.SetDispatcherHost: ", err) + } else if replyStr != utils.OK { + t.Error("Unexpected reply returned", replyStr) + } + + // Set DispatcherProfile Different with an inexistent engine opened, but with a bigger weight(this should match now) + setDispatcherProfile := &engine.DispatcherProfileWithAPIOpts{ + DispatcherProfile: &engine.DispatcherProfile{ + Tenant: "cgrates.org", + ID: "DSP1", + Strategy: "*weight", + Subsystems: []string{utils.MetaAny}, + Weight: 20, + Hosts: engine.DispatcherHostProfiles{ + { + ID: "INEXISTENT", + Weight: 10, + }, + }, + }, + APIOpts: map[string]interface{}{ + utils.OptsDispatchers: false, + }, + } + if err := setterRPC.Call(utils.APIerSv1SetDispatcherProfile, setDispatcherProfile, &replyStr); err != nil { + t.Error("Unexpected error when calling APIerSv1.SetDispatcherProfile: ", err) + } else if replyStr != utils.OK { + t.Error("Unexpected reply returned", replyStr) + } +} + +func testDispatcherOptsSetterStopEngine(t *testing.T) { + +} diff --git a/general_tests/filters_it_test.go b/general_tests/filters_it_test.go index 55d53608c..f96cd2a03 100644 --- a/general_tests/filters_it_test.go +++ b/general_tests/filters_it_test.go @@ -938,7 +938,7 @@ func testV1FltrChargerSuffix(t *testing.T) { utils.Destination: "999", }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, utils.OptsAttributesProfileIDs: []interface{}{"*constant:*req.Subject:intraState"}, }, }, @@ -976,7 +976,7 @@ func testV1FltrChargerSuffix(t *testing.T) { utils.Destination: "999", }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaChargers, + utils.MetaSubsys: utils.MetaChargers, utils.OptsAttributesProfileIDs: []interface{}{"*constant:*req.Subject:interState"}, }, }, diff --git a/general_tests/session3_it_test.go b/general_tests/session3_it_test.go index 07dac0a47..15e9226a2 100644 --- a/general_tests/session3_it_test.go +++ b/general_tests/session3_it_test.go @@ -185,7 +185,7 @@ func testSes3ItProcessEvent(t *testing.T) { utils.Usage: 300000000000.0, }, APIOpts: map[string]interface{}{ - utils.Subsys: utils.MetaSessionS, + utils.MetaSubsys: utils.MetaSessionS, utils.OptsAPIKey: "ses12345", utils.MetaEventType: utils.StatUpdate, "*attrProfileIDs": nil, diff --git a/services/dispatchers.go b/services/dispatchers.go index b94782917..49ee29bd4 100644 --- a/services/dispatchers.go +++ b/services/dispatchers.go @@ -115,6 +115,9 @@ func (dspS *DispatcherService) Start() (err error) { dspS.server.RpcRegisterName(utils.ChargerSv1, v1.NewDispatcherChargerSv1(dspS.dspS)) + dspS.server.RpcRegisterName(utils.CoreSv1, + v1.NewDispatcherCoreSv1(dspS.dspS)) + dspS.server.RpcRegisterName(utils.Responder, v1.NewDispatcherResponder(dspS.dspS)) diff --git a/servmanager/servmanager.go b/servmanager/servmanager.go index e224aacaa..4f1565003 100644 --- a/servmanager/servmanager.go +++ b/servmanager/servmanager.go @@ -47,7 +47,7 @@ func NewServiceManager(cfg *config.CGRConfig, shdChan *utils.SyncedChan, shdWg * type ServiceManager struct { sync.RWMutex // lock access to any shared data cfg *config.CGRConfig - subsystems map[string]Service + subsystems map[string]Service // active subsystems managed by SM shdChan *utils.SyncedChan shdWg *sync.WaitGroup diff --git a/sessions/sessions.go b/sessions/sessions.go index b6f7af46f..0256ab5dd 100644 --- a/sessions/sessions.go +++ b/sessions/sessions.go @@ -3886,7 +3886,7 @@ func (sS *SessionS) processAttributes(cgrEv *utils.CGREvent, attrIDs []string, if cgrEv.APIOpts == nil { cgrEv.APIOpts = make(engine.MapEvent) } - cgrEv.APIOpts[utils.Subsys] = utils.MetaSessionS + cgrEv.APIOpts[utils.MetaSubsys] = utils.MetaSessionS cgrEv.APIOpts[utils.OptsAttributesProfileIDs] = attrIDs ctx, has := cgrEv.APIOpts[utils.OptsContext] cgrEv.APIOpts[utils.OptsContext] = utils.FirstNonEmpty( diff --git a/utils/apitpdata.go b/utils/apitpdata.go index 926ee40f3..53fd21cc0 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -957,7 +957,7 @@ type AttrDisconnectSession struct { Reason string } -//MetricWithFilters is used in TPStatProfile +// MetricWithFilters is used in TPStatProfile type MetricWithFilters struct { FilterIDs []string MetricID string @@ -1499,11 +1499,12 @@ type DPRArgs struct { } type ArgCacheReplicateSet struct { - CacheID string - ItemID string - Value interface{} - APIOpts map[string]interface{} - Tenant string + CacheID string + ItemID string + Value interface{} + Tenant string + APIOpts map[string]interface{} + GroupIDs []string } // Compiler are objects that need post compiling diff --git a/utils/consts.go b/utils/consts.go index 9585a2436..aa625e978 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -943,6 +943,7 @@ const ( MetaRoutes = "*routes" MetaAttributes = "*attributes" MetaLoadIDs = "*load_ids" + MetaNodeID = "*node_id" ) // MetaMetrics @@ -1127,7 +1128,7 @@ const ( ArgDispatcherField = "ArgDispatcher" ) -//Filter types +// Filter types const ( MetaNot = "*not" MetaString = "*string" @@ -1501,6 +1502,7 @@ const ( CoreSv1 = "CoreSv1" CoreSv1Status = "CoreSv1.Status" CoreSv1Ping = "CoreSv1.Ping" + CoreSv1Panic = "CoreSv1.Panic" CoreSv1Sleep = "CoreSv1.Sleep" CoreSv1StartCPUProfiling = "CoreSv1.StartCPUProfiling" CoreSv1StopCPUProfiling = "CoreSv1.StopCPUProfiling" @@ -1682,6 +1684,8 @@ const ( CacheSv1GetCacheStats = "CacheSv1.GetCacheStats" CacheSv1GetItemIDs = "CacheSv1.GetItemIDs" CacheSv1HasItem = "CacheSv1.HasItem" + CacheSv1GetItem = "CacheSv1.GetItem" + CacheSv1GetItemWithRemote = "CacheSv1.GetItemWithRemote" CacheSv1GetItemExpiryTime = "CacheSv1.GetItemExpiryTime" CacheSv1RemoveItem = "CacheSv1.RemoveItem" CacheSv1RemoveItems = "CacheSv1.RemoveItems" @@ -1737,7 +1741,7 @@ const ( EeSv1ProcessEvent = "EeSv1.ProcessEvent" ) -//cgr_ variables +// cgr_ variables const ( CGRAccount = "cgr_account" CGRRoute = "cgr_route" @@ -1754,7 +1758,7 @@ const ( CGROpts = "cgr_opts" ) -//CSV file name +// CSV file name const ( TimingsCsv = "Timings.csv" DestinationsCsv = "Destinations.csv" @@ -2476,6 +2480,7 @@ const ( OptsAPIKey = "*apiKey" OptsRouteID = "*routeID" OptsDispatchersProfilesCount = "*dispatchersProfilesCount" + OptsDispatchers = "*dispatcherS" // EEs OptsEEsVerbose = "*eesVerbose" // Resources @@ -2504,7 +2509,7 @@ const ( OptsRefund = "*refund" // Others OptsContext = "*context" - Subsys = "*subsys" + MetaSubsys = "*subsys" MetaMethod = "*reqMethod" OptsAttributesProfileIDs = "*attrProfileIDs" OptsAttributesProcessRuns = "*attrProcessRuns" @@ -2691,7 +2696,7 @@ const ( Opts = "Opts" ) -//CMD constants +// CMD constants const ( //Common VerboseCgr = "verbose" diff --git a/utils/coreutils.go b/utils/coreutils.go index 9201b0d49..47e7e13f8 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -167,6 +167,7 @@ func UUIDSha1Prefix() string { // Round return rounded version of x with prec precision. // // Special cases are: +// // Round(±0) = ±0 // Round(±Inf) = ±Inf // Round(NaN) = NaN @@ -202,7 +203,7 @@ func Round(x float64, prec int, method string) float64 { return rounder / pow } -//RoundStatDuration is used in engine package for stat metrics that has duration (e.g acd metric, tcd metric, etc...) +// RoundStatDuration is used in engine package for stat metrics that has duration (e.g acd metric, tcd metric, etc...) func RoundStatDuration(x time.Duration, prec int) time.Duration { return x.Round(time.Duration(math.Pow10(9 - prec))) } @@ -429,7 +430,7 @@ func InfieldSplit(val string) []string { return strings.Split(val, InfieldSep) } -//Splited Unzip in small functions to have better coverage +// Splited Unzip in small functions to have better coverage func Unzip(src, dest string) error { r, err := zip.OpenReader(src) if err != nil { @@ -577,10 +578,11 @@ func Clone(a, b interface{}) error { // Used as generic function logic for various fields // Attributes -// source - the base source -// width - the field width -// strip - if present it will specify the strip strategy, when missing strip will not be allowed -// padding - if present it will specify the padding strategy to use, left, right, zeroleft, zeroright +// +// source - the base source +// width - the field width +// strip - if present it will specify the strip strategy, when missing strip will not be allowed +// padding - if present it will specify the padding strategy to use, left, right, zeroleft, zeroright func FmtFieldWidth(fieldID, source string, width int, strip, padding string, mandatory bool) (string, error) { if mandatory && len(source) == 0 { return "", fmt.Errorf("Empty source value for fieldID: <%s>", fieldID) @@ -1076,19 +1078,19 @@ func VerifyHash(hash string, dataKeys ...string) bool { return err == nil } -//newBoolGen initialize an efficient boolean generator +// newBoolGen initialize an efficient boolean generator func newBoolGen() *boolGen { return &boolGen{src: math_rand.NewSource(time.Now().UnixNano())} } -//boolGen is an efficient boolean generator +// boolGen is an efficient boolean generator type boolGen struct { src math_rand.Source cache int64 remaining int } -//RandomBool generate a random boolean +// RandomBool generate a random boolean func (b *boolGen) RandomBool() bool { if b.remaining == 0 { b.cache, b.remaining = b.src.Int63(), 63 @@ -1148,3 +1150,9 @@ func SplitPath(rule string, sep byte, n int) (splt []string) { splt = append(splt, rule[st:]) return } + +type PanicMessageArgs struct { + Tenant string + APIOpts map[string]interface{} + Message string +}