Files
cgrates/config/apis.go
2025-10-19 13:15:57 +02:00

630 lines
19 KiB
Go

/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>
*/
package config
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/cgrates/birpc/context"
"github.com/cgrates/cgrates/utils"
)
// getSectionAsMap returns a section from config as a map[string]interface
func (cfg *CGRConfig) getSectionAsMap(section string) (mp any, err error) {
if sec, has := cfg.sections.Get(section); has {
mp = sec.AsMapInterface()
return
}
err = errors.New("Invalid section ")
return
}
// ReloadArgs the API params for V1ReloadConfig
type ReloadArgs struct {
APIOpts map[string]any
Tenant string
Section string
DryRun bool
}
// V1ReloadConfig reloads the configuration
func (cfg *CGRConfig) V1ReloadConfig(ctx *context.Context, args *ReloadArgs, reply *string) (err error) {
cfgV := cfg
if args.DryRun {
cfgV = cfg.Clone()
}
cfgV.reloadDPCache(args.Section)
if err = cfgV.loadCfgWithLocks(ctx, cfg.ConfigPath, args.Section); err != nil {
return
}
sections := []string{args.Section}
allSections := args.Section == utils.MetaEmpty ||
args.Section == utils.MetaAll
if allSections {
sections = cfgV.GetAllSectionIDs()
}
if cfg.db != nil {
if err = cfgV.loadCfgFromDB(ctx, cfg.db, sections, allSections); err != nil {
return
}
}
// lock all sections
cfgV.rLockSections()
err = cfgV.checkConfigSanity()
cfgV.rUnlockSections() // unlock before checking the error
if err != nil {
return
}
if !args.DryRun {
cfgV.reloadSections(sections...)
}
*reply = utils.OK
return
}
// SectionWithAPIOpts the API params for GetConfig
type SectionWithAPIOpts struct {
APIOpts map[string]any
Tenant string
Sections []string
}
// V1GetConfig will retrieve from CGRConfig a section
func (cfg *CGRConfig) V1GetConfig(ctx *context.Context, args *SectionWithAPIOpts, reply *map[string]any) (err error) {
if len(args.Sections) == 0 ||
args.Sections[0] == utils.MetaAll {
args.Sections = cfg.GetAllSectionIDs()
}
mp := make(map[string]any)
sections := utils.StringSet{}
cfg.cacheDPMux.RLock()
for _, section := range args.Sections {
if val, has := cfg.cacheDP[section]; has && val != nil {
mp[section] = val
} else {
sections.Add(section)
}
}
cfg.cacheDPMux.RUnlock()
if sections.Size() == 0 { // all sections were cached
*reply = mp
return
}
if sections.Size() == len(cfg.sections) {
mp = cfg.AsMapInterface()
} else {
for section := range sections {
var val any
if val, err = cfg.getSectionAsMap(section); err != nil {
return
}
mp[section] = val
cfg.cacheDPMux.Lock()
cfg.cacheDP[section] = val
cfg.cacheDPMux.Unlock()
}
}
*reply = mp
return
}
// SetConfigArgs the API params for V1SetConfig
type SetConfigArgs struct {
APIOpts map[string]any
Tenant string
Config map[string]any
DryRun bool
}
// V1SetConfig reloads the sections of config
func (cfg *CGRConfig) V1SetConfig(ctx *context.Context, args *SetConfigArgs, reply *string) (err error) {
if len(args.Config) == 0 {
*reply = utils.OK
return
}
cfgV := cfg
if args.DryRun {
cfgV = cfg.Clone()
}
var oldCfg *CGRConfig
updateDB := cfg.db != nil
if !args.DryRun && updateDB { // need to update the DB but only parts
oldCfg = cfg.Clone()
}
sectionNms := make([]string, 0, len(args.Config))
sections := make(Sections, 0, len(args.Config))
for secNm := range args.Config {
sec, has := cfgV.sections.Get(secNm)
if !has {
return fmt.Errorf("Invalid section <%s> ", secNm)
}
sections = append(sections, sec)
sectionNms = append(sectionNms, secNm)
}
var b []byte
if b, err = json.Marshal(args.Config); err != nil {
return
}
cfgV.reloadDPCache(sectionNms...)
cfgV.LockSections(sectionNms...)
err = loadConfigFromReader(ctx, bytes.NewBuffer(b), sections, false, cfgV)
cfgV.UnlockSections(sectionNms...)
if err != nil {
return
}
// lock all sections
cfgV.rLockSections()
err = cfgV.checkConfigSanity()
cfgV.rUnlockSections() // unlock before checking the error
if err != nil {
return
}
if !args.DryRun {
cfgV.reloadSections(sectionNms...)
if updateDB { // need to update the DB but only parts
if err = storeDiffSections(ctx, sectionNms, cfgV.db, oldCfg, cfgV); err != nil {
return
}
}
}
*reply = utils.OK
return
}
// V1GetConfigAsJSON will retrieve from CGRConfig a section as a string
func (cfg *CGRConfig) V1GetConfigAsJSON(ctx *context.Context, args *SectionWithAPIOpts, reply *string) (err error) {
var mp map[string]any
if err = cfg.V1GetConfig(ctx, args, &mp); err != nil {
return
}
*reply = utils.ToJSON(mp)
return
}
// SetConfigFromJSONArgs the API params for V1SetConfigFromJSON
type SetConfigFromJSONArgs struct {
APIOpts map[string]any
Tenant string
Config string
DryRun bool
}
// V1SetConfigFromJSON reloads the sections of config
func (cfg *CGRConfig) V1SetConfigFromJSON(ctx *context.Context, args *SetConfigFromJSONArgs, reply *string) (err error) {
if len(args.Config) == 0 {
*reply = utils.OK
return
}
var oldCfg *CGRConfig
updateDB := cfg.db != nil
if !args.DryRun && updateDB { // need to update the DB but only parts
oldCfg = cfg.Clone()
}
cfgV := cfg
if args.DryRun {
cfgV = cfg.Clone()
}
sections := cfg.GetAllSectionIDs()
cfgV.reloadDPCache(sections...)
cfg.LockSections(sections...)
err = loadConfigFromReader(ctx, bytes.NewBufferString(args.Config), cfgV.sections, false, cfgV)
cfg.UnlockSections(sections...)
if err != nil {
return
}
// lock all sections
cfgV.rLockSections()
err = cfgV.checkConfigSanity()
cfgV.rUnlockSections() // unlock before checking the error
if err != nil {
return
}
if !args.DryRun {
cfgV.reloadSections(sections...)
if updateDB { // need to update the DB but only parts
if err = storeDiffSections(ctx, sections, cfg.db, oldCfg, cfg); err != nil {
return
}
}
}
*reply = utils.OK
return
}
func (cfg *CGRConfig) reloadDPCache(sections ...string) {
cfg.cacheDPMux.Lock()
delete(cfg.cacheDP, utils.MetaAll)
for _, sec := range sections {
delete(cfg.cacheDP, sec)
}
cfg.cacheDPMux.Unlock()
}
// loadFromJSONDB Loads from json configuration object, will be used for defaults, config from file and reload
// this function ignores the config_db section
func (cfg *CGRConfig) LoadFromDB(ctx *context.Context, jsnCfg ConfigDB) (err error) {
// Load sections out of JSON config, stop on error
cfg.lockSections()
defer cfg.unlockSections()
cfg.db = jsnCfg
if err = cfg.sections.LoadWithout(ctx, jsnCfg, cfg, ConfigDBJSON); err != nil {
return
}
return cfg.checkConfigSanity()
}
// LoadFromPath reads all json files out of a folder/subfolders and loads them up in lexical order
func (cfg *CGRConfig) LoadFromPath(ctx *context.Context, path string) (err error) {
cfg.ConfigPath = path
if err = loadConfigFromPath(ctx, path, cfg.sections, false, cfg); err != nil {
return
}
return cfg.checkConfigSanity()
}
func (cfg *CGRConfig) loadCfgFromDB(ctx *context.Context, db ConfigDB, sections []string, ignoreConfigDB bool) (err error) {
for _, section := range sections {
if section == ConfigDBJSON {
if ignoreConfigDB {
continue
}
return fmt.Errorf("Invalid section: <%s>", section)
}
sec, has := cfg.sections.Get(section)
if !has {
return fmt.Errorf("Invalid section: <%s>", section)
}
cfg.lks[section].Lock()
err = sec.Load(ctx, db, cfg)
cfg.lks[section].Unlock()
if err != nil {
return
}
}
return
}
func (cfg *CGRConfig) V1StoreCfgInDB(ctx *context.Context, args *SectionWithAPIOpts, rply *string) (err error) {
if cfg.db == nil {
return errors.New("no DB connection for config")
}
v1 := NewDefaultCGRConfig()
if err = v1.sections.Load(ctx, cfg.db, cfg); err != nil { // load the config from DB
return
}
if len(args.Sections) != 0 && args.Sections[0] == utils.MetaAll {
args.Sections = cfg.GetAllSectionIDs()
}
err = storeDiffSections(ctx, args.Sections, cfg.db, v1, cfg)
if err != nil {
return
}
*rply = utils.OK
return
}
func storeDiffSections(ctx *context.Context, sections []string, db ConfigDB, v1, v2 *CGRConfig) (err error) {
for _, section := range sections {
if err = storeDiffSection(ctx, section, db, v1, v2); err != nil {
return
}
}
return
}
func storeDiffSection(ctx *context.Context, section string, db ConfigDB, v1, v2 *CGRConfig) (err error) {
switch section {
case GeneralJSON:
jsn := new(GeneralJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffGeneralJsonCfg(jsn, v1.GeneralCfg(), v2.GeneralCfg()))
case LoggerJSON:
jsn := new(LoggerJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffLoggerJsonCfg(jsn, v1.LoggerCfg(), v2.LoggerCfg()))
case EFsJSON:
jsn := new(EfsJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffEFsJsonCfg(jsn, v1.EFsCfg(), v1.EFsCfg()))
case RPCConnsJSON:
jsn := make(RPCConnsJson)
if err = db.GetSection(ctx, section, &jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffRPCConnsJson(jsn, v1.RPCConns(), v2.RPCConns()))
case CacheJSON:
jsn := new(CacheJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffCacheJsonCfg(jsn, v1.CacheCfg(), v2.CacheCfg()))
case ListenJSON:
jsn := new(ListenJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffListenJsonCfg(jsn, v1.ListenCfg(), v2.ListenCfg()))
case HTTPJSON:
jsn := new(HTTPJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffHTTPJsonCfg(jsn, v1.HTTPCfg(), v2.HTTPCfg()))
case DBJSON:
jsn := new(DbJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffDataDBJsonCfg(jsn, v1.DbCfg(), v2.DbCfg()))
case FilterSJSON:
jsn := new(FilterSJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffFilterSJsonCfg(jsn, v1.FilterSCfg(), v2.FilterSCfg()))
case CDRsJSON:
jsn := new(CdrsJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffCdrsJsonCfg(jsn, v1.CdrsCfg(), v2.CdrsCfg()))
case ERsJSON:
jsn := new(ERsJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffERsJsonCfg(jsn, v1.ERsCfg(), v2.ERsCfg()))
case EEsJSON:
jsn := new(EEsJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffEEsJsonCfg(jsn, v1.EEsCfg(), v2.EEsCfg()))
case SessionSJSON:
jsn := new(SessionSJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffSessionSJsonCfg(jsn, v1.SessionSCfg(), v2.SessionSCfg()))
case FreeSWITCHAgentJSON:
jsn := new(FreeswitchAgentJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffFreeswitchAgentJsonCfg(jsn, v1.FsAgentCfg(), v2.FsAgentCfg()))
case KamailioAgentJSON:
jsn := new(KamAgentJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffKamAgentJsonCfg(jsn, v1.KamAgentCfg(), v2.KamAgentCfg()))
case AsteriskAgentJSON:
jsn := new(AsteriskAgentJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffAsteriskAgentJsonCfg(jsn, v1.AsteriskAgentCfg(), v2.AsteriskAgentCfg()))
case DiameterAgentJSON:
jsn := new(DiameterAgentJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffDiameterAgentJsonCfg(jsn, v1.DiameterAgentCfg(), v2.DiameterAgentCfg()))
case RadiusAgentJSON:
jsn := new(RadiusAgentJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffRadiusAgentJsonCfg(jsn, v1.RadiusAgentCfg(), v2.RadiusAgentCfg()))
case HTTPAgentJSON:
jsn := new([]*HttpAgentJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffHttpAgentsJsonCfg(jsn, v1.HTTPAgentCfg(), v2.HTTPAgentCfg()))
case JanusAgentJSON:
jsn := new(JanusAgentJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffJanusAgentSJsonCfg(jsn, v1.JanusAgentCfg(), v2.JanusAgentCfg()))
case DNSAgentJSON:
jsn := new(DNSAgentJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffDNSAgentJsonCfg(jsn, v1.DNSAgentCfg(), v2.DNSAgentCfg()))
case PrometheusAgentJSON:
jsn := new(PrometheusAgentJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffPrometheusAgentJsonCfg(jsn, v1.PrometheusAgentCfg(), v2.PrometheusAgentCfg()))
case AttributeSJSON:
jsn := new(AttributeSJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffAttributeSJsonCfg(jsn, v1.AttributeSCfg(), v2.AttributeSCfg()))
case ChargerSJSON:
jsn := new(ChargerSJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffChargerSJsonCfg(jsn, v1.ChargerSCfg(), v2.ChargerSCfg()))
case ResourceSJSON:
jsn := new(ResourceSJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffResourceSJsonCfg(jsn, v1.ResourceSCfg(), v2.ResourceSCfg()))
case StatSJSON:
jsn := new(StatServJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffStatServJsonCfg(jsn, v1.StatSCfg(), v2.StatSCfg()))
case ThresholdSJSON:
jsn := new(ThresholdSJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffThresholdSJsonCfg(jsn, v1.ThresholdSCfg(), v2.ThresholdSCfg()))
case TrendSJSON:
jsn := new(TrendSJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffTrendsJsonCfg(jsn, v1.TrendSCfg(), v2.TrendSCfg()))
case RankingSJSON:
jsn := new(RankingSJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffRankingsJsonCfg(jsn, v1.RankingSCfg(), v2.RankingSCfg()))
case RouteSJSON:
jsn := new(RouteSJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffRouteSJsonCfg(jsn, v1.RouteSCfg(), v2.RouteSCfg()))
case LoaderSJSON:
jsn := make([]*LoaderJsonCfg, 0)
if err = db.GetSection(ctx, section, &jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffLoadersJsonCfg(jsn, v1.LoaderCfg(), v2.LoaderCfg()))
case SureTaxJSON:
jsn := new(SureTaxJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffSureTaxJsonCfg(jsn, v1.SureTaxCfg(), v2.SureTaxCfg()))
case RegistrarCJSON:
jsn := new(RegistrarCJsonCfgs)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffRegistrarCJsonCfgs(jsn, v1.RegistrarCCfg(), v2.RegistrarCCfg()))
case LoaderJSON:
jsn := new(LoaderCfgJson)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffLoaderCfgJson(jsn, v1.LoaderCgrCfg(), v2.LoaderCgrCfg()))
case MigratorJSON:
jsn := new(MigratorCfgJson)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffMigratorCfgJson(jsn, v1.MigratorCgrCfg(), v2.MigratorCgrCfg()))
case TlsJSON:
jsn := new(TlsJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffTlsJsonCfg(jsn, v1.TLSCfg(), v2.TLSCfg()))
case AnalyzerSJSON:
jsn := new(AnalyzerSJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffAnalyzerSJsonCfg(jsn, v1.AnalyzerSCfg(), v2.AnalyzerSCfg()))
case AdminSJSON:
jsn := new(AdminSJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffAdminSJsonCfg(jsn, v1.AdminSCfg(), v2.AdminSCfg()))
case RateSJSON:
jsn := new(RateSJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffRateSJsonCfg(jsn, v1.RateSCfg(), v2.RateSCfg()))
case SIPAgentJSON:
jsn := new(SIPAgentJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffSIPAgentJsonCfg(jsn, v1.SIPAgentCfg(), v2.SIPAgentCfg()))
case TemplatesJSON:
jsn := make(FcTemplatesJsonCfg)
if err = db.GetSection(ctx, section, &jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffFcTemplatesJsonCfg(jsn, v1.TemplatesCfg(), v2.TemplatesCfg()))
case ConfigSJSON:
jsn := new(ConfigSCfgJson)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffConfigSCfgJson(jsn, v1.ConfigSCfg(), v2.ConfigSCfg()))
case APIBanJSON:
jsn := new(APIBanJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffAPIBanJsonCfg(jsn, v1.APIBanCfg(), v2.APIBanCfg()))
case SentryPeerJSON:
jsn := new(SentryPeerJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffSentryPeerJsonCfg(jsn, v1.SentryPeerCfg(), v2.SentryPeerCfg()))
case CoreSJSON:
jsn := new(CoreSJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffCoreSJsonCfg(jsn, v1.CoreSCfg(), v2.CoreSCfg()))
case ActionSJSON:
jsn := new(ActionSJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffActionSJsonCfg(jsn, v1.ActionSCfg(), v2.ActionSCfg()))
case AccountSJSON:
jsn := new(AccountSJsonCfg)
if err = db.GetSection(ctx, section, jsn); err != nil {
return
}
return db.SetSection(ctx, section, diffAccountSJsonCfg(jsn, v1.AccountSCfg(), v2.AccountSCfg()))
}
return
}