From 81bd7e8726357d6fea666f3cabb6d700677d40c1 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Wed, 28 Aug 2019 14:59:19 +0300 Subject: [PATCH] Added V1ReloadConfig --- apier/v1/config.go | 4 + apier/v1/dispatcher.go | 5 +- apier/v1/dispatcher_interface.go | 11 + apier/v1/dispatcher_interface_test.go | 14 +- config/config.go | 264 +++++++++++++------- config/config_it_test.go | 54 ++++ console/reload_config.go | 66 +++++ data/tariffplans/dispatchers/Attributes.csv | 2 +- dispatchers/config.go | 19 ++ utils/consts.go | 1 + 10 files changed, 352 insertions(+), 88 deletions(-) create mode 100644 console/reload_config.go diff --git a/apier/v1/config.go b/apier/v1/config.go index 6ca5b2a47..50ee41d33 100644 --- a/apier/v1/config.go +++ b/apier/v1/config.go @@ -36,6 +36,10 @@ func (cSv1 *ConfigSv1) GetJSONSection(section *config.StringWithArgDispatcher, r return cSv1.cfg.V1GetConfigSection(section, reply) } +func (cSv1 *ConfigSv1) ReloadConfig(args *config.ConfigReloadWithArgDispatcher, reply *string) (err error) { + return cSv1.cfg.V1ReloadConfig(args, reply) +} + // Call implements rpcclient.RpcClientConnection interface for internal RPC func (cSv1 *ConfigSv1) Call(serviceMethod string, args interface{}, reply interface{}) error { diff --git a/apier/v1/dispatcher.go b/apier/v1/dispatcher.go index 04bad5401..e0bf60efb 100755 --- a/apier/v1/dispatcher.go +++ b/apier/v1/dispatcher.go @@ -767,11 +767,14 @@ type DispatcherConfigSv1 struct { dS *dispatchers.DispatcherService } -// Ping used to detreminate if component is active func (dS *DispatcherConfigSv1) GetJSONSection(args *config.StringWithArgDispatcher, reply *map[string]interface{}) (err error) { return dS.dS.ConfigSv1GetJSONSection(args, reply) } +func (dS *DispatcherConfigSv1) ReloadConfig(args *config.ConfigReloadWithArgDispatcher, reply *string) (err error) { + return dS.dS.ConfigSv1ReloadConfig(args, reply) +} + func NewDispatcherCoreSv1(dps *dispatchers.DispatcherService) *DispatcherCoreSv1 { return &DispatcherCoreSv1{dS: dps} } diff --git a/apier/v1/dispatcher_interface.go b/apier/v1/dispatcher_interface.go index 8941df070..2754bf56f 100644 --- a/apier/v1/dispatcher_interface.go +++ b/apier/v1/dispatcher_interface.go @@ -21,6 +21,7 @@ package v1 import ( "time" + "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/dispatchers" "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/sessions" @@ -150,3 +151,13 @@ type RALsV1Interface interface { GetRatingPlansCost(arg *utils.RatingPlanCostArg, reply *dispatchers.RatingPlanCost) error Ping(ign *utils.CGREventWithArgDispatcher, reply *string) error } + +type ConfigSv1Interface interface { + GetJSONSection(section *config.StringWithArgDispatcher, reply *map[string]interface{}) (err error) + ReloadConfig(section *config.ConfigReloadWithArgDispatcher, reply *string) (err error) +} + +type CoreSv1Interface interface { + Status(arg *utils.TenantWithArgDispatcher, reply *map[string]interface{}) error + Ping(ign *utils.CGREventWithArgDispatcher, reply *string) error +} diff --git a/apier/v1/dispatcher_interface_test.go b/apier/v1/dispatcher_interface_test.go index 5b8c81888..94bb61a0a 100644 --- a/apier/v1/dispatcher_interface_test.go +++ b/apier/v1/dispatcher_interface_test.go @@ -90,6 +90,16 @@ func TestServiceManagerV1Interface(t *testing.T) { } func TestRALsV1Interface(t *testing.T) { - _ = ServiceManagerV1Interface(NewDispatcherSServiceManagerV1(nil)) - _ = ServiceManagerV1Interface(NewServiceManagerV1(nil)) + _ = RALsV1Interface(NewDispatcherRALsV1(nil)) + _ = RALsV1Interface(NewRALsV1()) +} + +func TestConfigSv1Interface(t *testing.T) { + _ = ConfigSv1Interface(NewDispatcherConfigSv1(nil)) + _ = ConfigSv1Interface(NewConfigSv1(nil)) +} + +func TestCoreSv1Interface(t *testing.T) { + _ = CoreSv1Interface(NewDispatcherCoreSv1(nil)) + _ = CoreSv1Interface(NewCoreSv1(nil)) } diff --git a/config/config.go b/config/config.go index c21863d24..5071d2aa1 100755 --- a/config/config.go +++ b/config/config.go @@ -232,20 +232,14 @@ func NewCGRConfigFromPath(path string) (*CGRConfig, error) { return nil, err } cfg.ConfigPath = path - if isUrl(path) { - return loadConfigFromHttp(cfg, path) // prefix protocol - } - fi, err := os.Stat(path) - if err != nil { - if os.IsNotExist(err) { - return nil, utils.ErrPathNotReachable(path) - } + + if err := updateConfigFromPath(path, func(jsnCfg *CgrJsonCfg) error { + return cfg.loadFromJsonCfg(jsnCfg) + }); err != nil { return nil, err - } else if !fi.IsDir() && path != utils.CONFIG_PATH { // If config dir defined, needs to exist, not checking for default - return nil, fmt.Errorf("Path: %s not a directory.", path) } - if fi.IsDir() { - return loadConfigFromFolder(cfg, path) + if err = cfg.checkConfigSanity(); err != nil { // should we check only the updated sections? + return nil, err } return cfg, nil } @@ -257,64 +251,6 @@ func isHidden(fileName string) bool { return strings.HasPrefix(fileName, ".") } -func loadConfigFromFolder(cfg *CGRConfig, cfgDir string) (*CGRConfig, error) { - jsonFilesFound := false - err := filepath.Walk(cfgDir, func(path string, info os.FileInfo, err error) error { - if !info.IsDir() || isHidden(info.Name()) { // also ignore hidden files and folders - return nil - } - cfgFiles, err := filepath.Glob(filepath.Join(path, "*.json")) - if err != nil { - return err - } - if cfgFiles == nil { // No need of processing further since there are no config files in the folder - return nil - } - if !jsonFilesFound { - jsonFilesFound = true - } - for _, jsonFilePath := range cfgFiles { - if cgrJsonCfg, err := NewCgrJsonCfgFromFile(jsonFilePath); err != nil { - utils.Logger.Err(fmt.Sprintf(" Error <%s> reading config from path: <%s>", err.Error(), jsonFilePath)) - return err - } else if err := cfg.loadFromJsonCfg(cgrJsonCfg); err != nil { - utils.Logger.Err(fmt.Sprintf(" Error <%s> loading config from path: <%s>", err.Error(), jsonFilePath)) - return err - } - } - return nil - }) - if err != nil { - return nil, err - } - if !jsonFilesFound { - return nil, fmt.Errorf("No config file found on path %s", cfgDir) - } - if err := cfg.checkConfigSanity(); err != nil { - return nil, err - } - return cfg, nil -} - -func loadConfigFromHttp(cfg *CGRConfig, urlPaths string) (*CGRConfig, error) { - for _, urlPath := range strings.Split(urlPaths, utils.INFIELD_SEP) { - if _, err := url.ParseRequestURI(urlPath); err != nil { - return nil, err - } - if cgrJsonCfg, err := NewCgrJsonCfgFromHttp(urlPath); err != nil { - utils.Logger.Err(fmt.Sprintf(" Error <%s> reading config from path: <%s>", err.Error(), urlPath)) - return nil, err - } else if err := cfg.loadFromJsonCfg(cgrJsonCfg); err != nil { - utils.Logger.Err(fmt.Sprintf(" Error <%s> loading config from path: <%s>", err.Error(), urlPath)) - return nil, err - } - } - if err := cfg.checkConfigSanity(); err != nil { - return nil, err - } - return cfg, nil -} - // Holds system configuration, defaults are overwritten with values from config file if found type CGRConfig struct { lks map[string]*sync.RWMutex @@ -988,7 +924,7 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) (err error) { jsnLoaderCgrCfg, err := jsnCfg.LoaderCfgJson() if err != nil { - return nil + return err } if self.loaderCgrCfg.loadFromJsonCfg(jsnLoaderCgrCfg); err != nil { return err @@ -996,7 +932,7 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) (err error) { jsnMigratorCgrCfg, err := jsnCfg.MigratorCfgJson() if err != nil { - return nil + return err } if self.migratorCgrCfg.loadFromJsonCfg(jsnMigratorCgrCfg); err != nil { return err @@ -1004,7 +940,7 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) (err error) { jsnTlsCgrCfg, err := jsnCfg.TlsCfgJson() if err != nil { - return nil + return err } if err := self.tlsCfg.loadFromJsonCfg(jsnTlsCgrCfg); err != nil { return err @@ -1012,24 +948,17 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) (err error) { jsnAnalyzerCgrCfg, err := jsnCfg.AnalyzerCfgJson() if err != nil { - return nil + return err } if err := self.analyzerSCfg.loadFromJsonCfg(jsnAnalyzerCgrCfg); err != nil { return err } - jsnApierCfg, err := jsnCfg.ApierCfgJson() - if err != nil { - return nil - } - if err := self.apier.loadFromJsonCfg(jsnApierCfg); err != nil { + if err := self.loadApierCfg(jsnCfg); err != nil { return err } - jsnERsCfg, err := jsnCfg.ERsJsonCfg() - if err != nil { - return nil - } - if err := self.ersCfg.loadFromJsonCfg(jsnERsCfg, self.generalCfg.RSRSep, self.dfltEvRdr); err != nil { + + if err := self.loadErsCfg(jsnCfg); err != nil { return err } @@ -1101,6 +1030,22 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) (err error) { return nil } +func (cfg *CGRConfig) loadApierCfg(jsnCfg *CgrJsonCfg) (err error) { + var jsnApierCfg *ApierJsonCfg + if jsnApierCfg, err = jsnCfg.ApierCfgJson(); err != nil { + return + } + return cfg.apier.loadFromJsonCfg(jsnApierCfg) +} + +func (cfg *CGRConfig) loadErsCfg(jsnCfg *CgrJsonCfg) (err error) { + var jsnERsCfg *ERsJsonCfg + if jsnERsCfg, err = jsnCfg.ERsJsonCfg(); err != nil { + return + } + return cfg.ersCfg.loadFromJsonCfg(jsnERsCfg, cfg.GeneralCfg().RSRSep, self.dfltEvRdr) +} + // Use locking to retrieve the configuration, possibility later for runtime reload func (self *CGRConfig) SureTaxCfg() *SureTaxCfg { cfgChan := <-self.ConfigReloads[utils.SURETAX] // Lock config for read or reloads @@ -1258,6 +1203,7 @@ func (cSv1 *CGRConfig) Call(serviceMethod string, return utils.APIerRPCCall(cSv1, serviceMethod, args, reply) } +// ToDo: move this structure in utils as is used in other packages type StringWithArgDispatcher struct { *utils.ArgDispatcher utils.TenantArg @@ -1332,9 +1278,159 @@ func (cfg *CGRConfig) V1GetConfigSection(args *StringWithArgDispatcher, reply *m jsonString = utils.ToJSON(cfg.CdrcProfiles) case CDRE_JSN: jsonString = utils.ToJSON(cfg.CdreProfiles) + case ERsJson: + jsonString = utils.ToJSON(cfg.ERsCfg()) default: return errors.New("Invalid section") } json.Unmarshal([]byte(jsonString), reply) return } + +type ConfigReloadWithArgDispatcher struct { + *utils.ArgDispatcher + utils.TenantArg + Section string + Path string +} + +func (cfg *CGRConfig) RLockSections() { + for _, lk := range cfg.lks { + lk.RLock() + } +} + +func (cfg *CGRConfig) RUnlockSections() { + for _, lk := range cfg.lks { + lk.RUnlock() + } +} + +func (cfg *CGRConfig) LockSections() { + for _, lk := range cfg.lks { + lk.Lock() + } +} + +func (cfg *CGRConfig) UnlockSections() { + for _, lk := range cfg.lks { + lk.Unlock() + } +} + +func (cfg *CGRConfig) V1ReloadConfig(args *ConfigReloadWithArgDispatcher, reply *string) (err error) { + var reloadFunction func() + if reloadFunction, err = cfg.loadConfig(args.Path, args.Section); err != nil { + return err + } + // lock all sections + cfg.RLockSections() + + err = cfg.checkConfigSanity() + + cfg.RUnlockSections() // unlock before checking the error + + if err != nil { + return err + } + + reloadFunction() + *reply = utils.OK + return nil +} + +func (cfg *CGRConfig) loadConfig(path, section string) (reload func(), err error) { + var parseFunction func(jsnCfg *CgrJsonCfg) error + switch section { + case utils.EmptyString: + cfg.LockSections() + defer cfg.UnlockSections() + parseFunction = func(jsnCfg *CgrJsonCfg) error { + return cfg.loadFromJsonCfg(jsnCfg) + } + reload = func() {} + case ERsJson: + cfg.lks[ERsJson].Lock() + defer cfg.lks[ERsJson].Unlock() + parseFunction = func(jsnCfg *CgrJsonCfg) error { + return cfg.loadErsCfg(jsnCfg) + } + reload = func() { cfg.rldChans[ERsJson] <- struct{}{} } + default: + return nil, fmt.Errorf("Invalid section: <%s>", section) + } + err = updateConfigFromPath(path, parseFunction) + return +} + +// Reads all .json files out of a folder/subfolders and loads them up in lexical order +func updateConfigFromPath(path string, parseFunction func(jsnCfg *CgrJsonCfg) error) error { + if isUrl(path) { + return updateConfigFromHttp(path, parseFunction) // prefix protocol + } + fi, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + return utils.ErrPathNotReachable(path) + } + return err + } else if !fi.IsDir() && path != utils.CONFIG_PATH { // If config dir defined, needs to exist, not checking for default + return fmt.Errorf("Path: %s not a directory.", path) + } + if fi.IsDir() { + return updateConfigFromFolder(path, parseFunction) + } + return nil +} + +func updateConfigFromFolder(cfgDir string, parseFunction func(jsnCfg *CgrJsonCfg) error) error { + jsonFilesFound := false + err := filepath.Walk(cfgDir, func(path string, info os.FileInfo, err error) error { + if !info.IsDir() || isHidden(info.Name()) { // also ignore hidden files and folders + return nil + } + cfgFiles, err := filepath.Glob(filepath.Join(path, "*.json")) + if err != nil { + return err + } + if cfgFiles == nil { // No need of processing further since there are no config files in the folder + return nil + } + if !jsonFilesFound { + jsonFilesFound = true + } + for _, jsonFilePath := range cfgFiles { + if cgrJsonCfg, err := NewCgrJsonCfgFromFile(jsonFilePath); err != nil { + utils.Logger.Err(fmt.Sprintf(" Error <%s> reading config from path: <%s>", err.Error(), jsonFilePath)) + return err + } else if err := parseFunction(cgrJsonCfg); err != nil { + utils.Logger.Err(fmt.Sprintf(" Error <%s> loading config from path: <%s>", err.Error(), jsonFilePath)) + return err + } + } + return nil + }) + if err != nil { + return err + } + if !jsonFilesFound { + return fmt.Errorf("No config file found on path %s", cfgDir) + } + return nil +} + +func updateConfigFromHttp(urlPaths string, parseFunction func(jsnCfg *CgrJsonCfg) error) error { + for _, urlPath := range strings.Split(urlPaths, utils.INFIELD_SEP) { + if _, err := url.ParseRequestURI(urlPath); err != nil { + return err + } + if cgrJsonCfg, err := NewCgrJsonCfgFromHttp(urlPath); err != nil { + utils.Logger.Err(fmt.Sprintf(" Error <%s> reading config from path: <%s>", err.Error(), urlPath)) + return err + } else if err := parseFunction(cgrJsonCfg); err != nil { + utils.Logger.Err(fmt.Sprintf(" Error <%s> loading config from path: <%s>", err.Error(), urlPath)) + return err + } + } + return nil +} diff --git a/config/config_it_test.go b/config/config_it_test.go index 2aa26440b..c09032b15 100644 --- a/config/config_it_test.go +++ b/config/config_it_test.go @@ -72,3 +72,57 @@ func TestNewCGRConfigFromPath(t *testing.T) { } } + +func TestCgrCfgV1ReloadConfigSection(t *testing.T) { + expected := map[string]interface{}{ + "Enabled": true, + "Readers": []interface{}{ + map[string]interface{}{ + "ConcurrentReqs": 0., + "Content_fields": nil, + "Continue": false, + "FieldSep": "", + "Filters": nil, + "Flags": nil, + "Header_fields": nil, + "ID": "file_reader1", + "ProcessedPath": "", + "RunDelay": -1., + "SourceID": "", + "SourcePath": "", + "Tenant": nil, + "Timezone": "", + "Trailer_fields": nil, + "Type": "", + "XmlRootPath": "", + }, + }, + "SessionSConns": []interface{}{ + map[string]interface{}{ + "Address": "*internal", + "Synchronous": false, + "TLS": false, + "Transport": "", + }, + }, + } + + cfg, _ := NewDefaultCGRConfig() + var reply string + var rcv map[string]interface{} + + if err := cfg.V1ReloadConfig(&ConfigReloadWithArgDispatcher{ + Path: "/usr/share/cgrates/conf/samples/ers", + Section: ERsJson, + }, &reply); err != nil { + t.Fatal(err) + } else if reply != utils.OK { + t.Errorf("Expected: %s ,received: %s", utils.OK, reply) + } + + if err := cfg.V1GetConfigSection(&StringWithArgDispatcher{Section: ERsJson}, &rcv); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expected, rcv) { + t.Errorf("Expected: %+v, received: %+v", utils.ToJSON(expected), utils.ToJSON(rcv)) + } +} diff --git a/console/reload_config.go b/console/reload_config.go new file mode 100644 index 000000000..a2b67c40c --- /dev/null +++ b/console/reload_config.go @@ -0,0 +1,66 @@ +/* +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 console + +import ( + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/utils" +) + +func init() { + c := &CmdRelaodConfigSection{ + name: "reload_config", + rpcMethod: utils.ConfigSv1ReloadConfig, + rpcParams: &config.ConfigReloadWithArgDispatcher{}, + } + commands[c.Name()] = c + c.CommandExecuter = &CommandExecuter{c} +} + +// Commander implementation +type CmdRelaodConfigSection struct { + name string + rpcMethod string + rpcParams *config.ConfigReloadWithArgDispatcher + *CommandExecuter +} + +func (self *CmdRelaodConfigSection) Name() string { + return self.name +} + +func (self *CmdRelaodConfigSection) RpcMethod() string { + return self.rpcMethod +} + +func (self *CmdRelaodConfigSection) RpcParams(reset bool) interface{} { + if reset || self.rpcParams == nil { + self.rpcParams = &config.ConfigReloadWithArgDispatcher{ArgDispatcher: new(utils.ArgDispatcher)} + } + return self.rpcParams +} + +func (self *CmdRelaodConfigSection) PostprocessRpcParams() error { + return nil +} + +func (self *CmdRelaodConfigSection) RpcResult() interface{} { + var s string + return &s +} diff --git a/data/tariffplans/dispatchers/Attributes.csv b/data/tariffplans/dispatchers/Attributes.csv index 17a62edff..53ee65943 100644 --- a/data/tariffplans/dispatchers/Attributes.csv +++ b/data/tariffplans/dispatchers/Attributes.csv @@ -17,6 +17,6 @@ cgrates.org,ATTR_API_SCHD_AUTH,*auth,*string:~APIKey:sched12345,,,APIMethods,*co cgrates.org,ATTR_API_CDRS_AUTH,*auth,*string:~APIKey:cdrs12345,,,APIMethods,*constant,CDRsV1.Ping&CDRsV1.ProcessEvent&CDRsV1.GetCDRs&CDRsV1.CountCDRs&CDRsV1.ProcessCDR&CDRsV1.ProcessExternalCDR,false,20 cgrates.org,ATTR_API_DSP_AUTH,*auth,*string:~APIKey:dsp12345,,,APIMethods,*constant,DispatcherSv1.Ping&DispatcherSv1.GetProfileForEvent,false,20 cgrates.org,ATTR_API_PSE_AUTH,*auth,*string:~APIKey:pse12345,,,APIMethods,*constant,SessionSv1.Ping&SessionSv1.AuthorizeEvent&SessionSv1.AuthorizeEventWithDigest&SessionSv1.InitiateSession&SessionSv1.InitiateSessionWithDigest&SessionSv1.UpdateSession&SessionSv1.SyncSessions&SessionSv1.TerminateSession&SessionSv1.ProcessCDR&SessionSv1.ProcessMessage&SessionSv1.GetActiveSessions&SessionSv1.GetActiveSessionsCount&SessionSv1.ForceDisconnect&SessionSv1.GetPassiveSessions&SessionSv1.GetPassiveSessionsCount&SessionSv1.ReplicateSessions&SessionSv1.SetPassiveSession&AttributeSv1.ProcessEvent&Responder.Debit&ResourceSv1.AllocateResources&ChargerSv1.ProcessEvent&Responder.MaxDebit,false,20 -cgrates.org,ATTR_API_CFG_AUTH,*auth,*string:~APIKey:cfg12345,,,APIMethods,*constant,ConfigSv1.GetJSONSection,false,20 +cgrates.org,ATTR_API_CFG_AUTH,*auth,*string:~APIKey:cfg12345,,,APIMethods,*constant,ConfigSv1.GetJSONSection&ConfigSv1.ReloadConfig,false,20 cgrates.org,ATTR_API_APIER_AUTH,*auth,*string:~APIKey:apier12345,,,APIMethods,*constant,ApierV1.GetAttributeProfile&ApierV1.SetAttributeProfile,false,20 cgrates.org,ATTR_API_RALS_AUTH,*auth,*string:~APIKey:rals12345,,,APIMethods,*constant,RALsV1.Ping&RALsV1.GetRatingPlansCost,false,20 diff --git a/dispatchers/config.go b/dispatchers/config.go index 203d7d042..9bb071cdc 100644 --- a/dispatchers/config.go +++ b/dispatchers/config.go @@ -43,3 +43,22 @@ func (dS *DispatcherService) ConfigSv1GetJSONSection(args *config.StringWithArgD return dS.Dispatch(&utils.CGREvent{Tenant: tnt}, utils.MetaConfig, routeID, utils.ConfigSv1GetJSONSection, args, reply) } + +func (dS *DispatcherService) ConfigSv1ReloadConfig(args *config.ConfigReloadWithArgDispatcher, reply *string) (err error) { + tnt := utils.FirstNonEmpty(args.TenantArg.Tenant, dS.cfg.GeneralCfg().DefaultTenant) + if dS.attrS != nil { + if args.ArgDispatcher == nil { + return utils.NewErrMandatoryIeMissing(utils.ArgDispatcherField) + } + if err = dS.authorize(utils.ConfigSv1ReloadConfig, tnt, + args.APIKey, utils.TimePointer(time.Now())); err != nil { + return + } + } + var routeID *string + if args.ArgDispatcher != nil { + routeID = args.ArgDispatcher.RouteID + } + return dS.Dispatch(&utils.CGREvent{Tenant: tnt}, + utils.MetaConfig, routeID, utils.ConfigSv1ReloadConfig, args, reply) +} diff --git a/utils/consts.go b/utils/consts.go index c26cfc1ac..9c45c3b45 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -833,6 +833,7 @@ const ( const ( ConfigSv1 = "ConfigSv1" ConfigSv1GetJSONSection = "ConfigSv1.GetJSONSection" + ConfigSv1ReloadConfig = "ConfigSv1.ReloadConfig" ) const (