Implemented service,config and api for Sars

This commit is contained in:
gezimbll
2024-05-28 03:04:05 -04:00
committed by Dan Christian Bogos
parent ed9ac8965f
commit 86df598920
11 changed files with 281 additions and 3 deletions

36
apier/v1/sars.go Normal file
View File

@@ -0,0 +1,36 @@
/*
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 <http://www.gnu.org/licenses/>
*/
package v1
import (
"github.com/cgrates/birpc/context"
"github.com/cgrates/cgrates/utils"
)
func NewSArSv1() *SaRSv1 {
return &SaRSv1{}
}
type SaRSv1 struct {
}
func (sa *SaRSv1) Ping(ctx *context.Context, ign *utils.CGREvent, reply *string) error {
*reply = utils.Pong
return nil
}

View File

@@ -154,6 +154,7 @@ func newCGRConfig(config []byte) (cfg *CGRConfig, err error) {
cfg.chargerSCfg = new(ChargerSCfg)
cfg.resourceSCfg = &ResourceSConfig{Opts: &ResourcesOpts{}}
cfg.statsCfg = &StatSCfg{Opts: &StatsOpts{}}
cfg.sarsCfg = new(SarSCfg)
cfg.thresholdSCfg = &ThresholdSCfg{Opts: &ThresholdsOpts{}}
cfg.routeSCfg = &RouteSCfg{Opts: &RoutesOpts{}}
cfg.sureTaxCfg = new(SureTaxCfg)
@@ -313,6 +314,7 @@ type CGRConfig struct {
chargerSCfg *ChargerSCfg // ChargerS config
resourceSCfg *ResourceSConfig // ResourceS config
statsCfg *StatSCfg // StatS config
sarsCfg *SarSCfg //SarS config
thresholdSCfg *ThresholdSCfg // ThresholdS config
routeSCfg *RouteSCfg // RouteS config
sureTaxCfg *SureTaxCfg // SureTax config
@@ -363,7 +365,7 @@ func (cfg *CGRConfig) loadFromJSONCfg(jsnCfg *CgrJsonCfg) (err error) {
cfg.loadFreeswitchAgentCfg, cfg.loadKamAgentCfg,
cfg.loadAsteriskAgentCfg, cfg.loadDiameterAgentCfg, cfg.loadRadiusAgentCfg,
cfg.loadDNSAgentCfg, cfg.loadHTTPAgentCfg, cfg.loadAttributeSCfg,
cfg.loadChargerSCfg, cfg.loadResourceSCfg, cfg.loadStatSCfg,
cfg.loadChargerSCfg, cfg.loadResourceSCfg, cfg.loadStatSCfg, cfg.loadSarSCfg,
cfg.loadThresholdSCfg, cfg.loadRouteSCfg, cfg.loadLoaderSCfg,
cfg.loadMailerCfg, cfg.loadSureTaxCfg, cfg.loadDispatcherSCfg,
cfg.loadLoaderCgrCfg, cfg.loadMigratorCgrCfg, cfg.loadTLSCgrCfg,
@@ -633,6 +635,15 @@ func (cfg *CGRConfig) loadStatSCfg(jsnCfg *CgrJsonCfg) (err error) {
return cfg.statsCfg.loadFromJSONCfg(jsnStatSCfg)
}
// loadSarSCfg loads the SarS section of the configuration
func (cfg *CGRConfig) loadSarSCfg(jsnCfg *CgrJsonCfg) (err error) {
var jsnSarSCfg *SarsJsonCfg
if jsnSarSCfg, err = jsnCfg.SarsJsonCfg(); err != nil {
return
}
return cfg.sarsCfg.loadFromJSONCfg(jsnSarSCfg)
}
// loadThresholdSCfg loads the ThresholdS section of the configuration
func (cfg *CGRConfig) loadThresholdSCfg(jsnCfg *CgrJsonCfg) (err error) {
var jsnThresholdSCfg *ThresholdSJsonCfg
@@ -887,6 +898,13 @@ func (cfg *CGRConfig) StatSCfg() *StatSCfg { // not done
return cfg.statsCfg
}
// SarSCfg returns the config for SarS
func (cfg *CGRConfig) SarSCfg() *SarSCfg {
cfg.lks[SARS_JSON].Lock()
defer cfg.lks[SARS_JSON].Unlock()
return cfg.sarsCfg
}
// ThresholdSCfg returns the config for ThresholdS
func (cfg *CGRConfig) ThresholdSCfg() *ThresholdSCfg {
cfg.lks[THRESHOLDS_JSON].Lock()
@@ -1236,6 +1254,7 @@ func (cfg *CGRConfig) getLoadFunctions() map[string]func(*CgrJsonCfg) error {
ChargerSCfgJson: cfg.loadChargerSCfg,
RESOURCES_JSON: cfg.loadResourceSCfg,
STATS_JSON: cfg.loadStatSCfg,
SARS_JSON: cfg.loadSarSCfg,
THRESHOLDS_JSON: cfg.loadThresholdSCfg,
RouteSJson: cfg.loadRouteSCfg,
LoaderJson: cfg.loadLoaderSCfg,
@@ -1486,6 +1505,8 @@ func (cfg *CGRConfig) reloadSections(sections ...string) {
cfg.rldChans[RESOURCES_JSON] <- struct{}{}
case STATS_JSON:
cfg.rldChans[STATS_JSON] <- struct{}{}
case SARS_JSON:
cfg.rldChans[SARS_JSON] <- struct{}{}
case THRESHOLDS_JSON:
cfg.rldChans[THRESHOLDS_JSON] <- struct{}{}
case RouteSJson:
@@ -1538,6 +1559,7 @@ func (cfg *CGRConfig) AsMapInterface(separator string) (mp map[string]any) {
ChargerSCfgJson: cfg.chargerSCfg.AsMapInterface(),
RESOURCES_JSON: cfg.resourceSCfg.AsMapInterface(),
STATS_JSON: cfg.statsCfg.AsMapInterface(),
SARS_JSON: cfg.sarsCfg.AsMapInterface(),
THRESHOLDS_JSON: cfg.thresholdSCfg.AsMapInterface(),
RouteSJson: cfg.routeSCfg.AsMapInterface(),
SURETAX_JSON: cfg.sureTaxCfg.AsMapInterface(separator),
@@ -1676,6 +1698,8 @@ func (cfg *CGRConfig) V1GetConfig(ctx *context.Context, args *SectionWithAPIOpts
mp = cfg.ResourceSCfg().AsMapInterface()
case STATS_JSON:
mp = cfg.StatSCfg().AsMapInterface()
case SARS_JSON:
mp = cfg.SarSCfg().AsMapInterface()
case THRESHOLDS_JSON:
mp = cfg.ThresholdSCfg().AsMapInterface()
case RouteSJson:
@@ -1844,6 +1868,8 @@ func (cfg *CGRConfig) V1GetConfigAsJSON(ctx *context.Context, args *SectionWithA
mp = cfg.ResourceSCfg().AsMapInterface()
case STATS_JSON:
mp = cfg.StatSCfg().AsMapInterface()
case SARS_JSON:
mp = cfg.SarSCfg().AsMapInterface()
case THRESHOLDS_JSON:
mp = cfg.ThresholdSCfg().AsMapInterface()
case RouteSJson:
@@ -1967,6 +1993,7 @@ func (cfg *CGRConfig) Clone() (cln *CGRConfig) {
chargerSCfg: cfg.chargerSCfg.Clone(),
resourceSCfg: cfg.resourceSCfg.Clone(),
statsCfg: cfg.statsCfg.Clone(),
sarsCfg: cfg.sarsCfg.Clone(),
thresholdSCfg: cfg.thresholdSCfg.Clone(),
routeSCfg: cfg.routeSCfg.Clone(),
sureTaxCfg: cfg.sureTaxCfg.Clone(),

View File

@@ -822,6 +822,10 @@ const CGRATES_CFG_JSON = `
},
},
"sars":{ // SaRS config
"enabled": false, // starts SaRS service: <true|false>.
"stats_conns": [], // connections to StatS ,empty to disable stats functionality: <""|*internal|$rpc_conns_id>
},
"thresholds": { // ThresholdS
"enabled": false, // starts ThresholdS service: <true|false>.

View File

@@ -44,6 +44,7 @@ const (
RESOURCES_JSON = "resources"
STATS_JSON = "stats"
THRESHOLDS_JSON = "thresholds"
SARS_JSON = "sars"
RouteSJson = "routes"
LoaderJson = "loaders"
MAILER_JSN = "mailer"
@@ -72,7 +73,7 @@ const (
var (
sortedCfgSections = []string{GENERAL_JSN, RPCConnsJsonName, DATADB_JSN, STORDB_JSN, LISTEN_JSN, TlsCfgJson, HTTP_JSN, SCHEDULER_JSN,
CACHE_JSN, FilterSjsn, RALS_JSN, CDRS_JSN, ERsJson, SessionSJson, AsteriskAgentJSN, FreeSWITCHAgentJSN,
KamailioAgentJSN, DA_JSN, RA_JSN, HttpAgentJson, DNSAgentJson, ATTRIBUTE_JSN, ChargerSCfgJson, RESOURCES_JSON, STATS_JSON,
KamailioAgentJSN, DA_JSN, RA_JSN, HttpAgentJson, DNSAgentJson, ATTRIBUTE_JSN, ChargerSCfgJson, RESOURCES_JSON, STATS_JSON, SARS_JSON,
THRESHOLDS_JSON, RouteSJson, LoaderJson, MAILER_JSN, SURETAX_JSON, CgrLoaderCfgJson, CgrMigratorCfgJson, DispatcherSJson, JanusAgentJson,
AnalyzerCfgJson, ApierS, EEsJson, SIPAgentJson, RegistrarCJson, TemplatesJson, ConfigSJson, APIBanCfgJson, SentryPeerCfgJson, CoreSCfgJson}
)
@@ -369,6 +370,18 @@ func (jsnCfg CgrJsonCfg) StatSJsonCfg() (*StatServJsonCfg, error) {
return cfg, nil
}
func (jsnCfg CgrJsonCfg) SarsJsonCfg() (*SarsJsonCfg, error) {
rawCfg, hasKey := jsnCfg[SARS_JSON]
if !hasKey {
return nil, nil
}
cfg := new(SarsJsonCfg)
if err := json.Unmarshal(*rawCfg, cfg); err != nil {
return nil, err
}
return cfg, nil
}
func (jsnCfg CgrJsonCfg) ThresholdSJsonCfg() (*ThresholdSJsonCfg, error) {
rawCfg, hasKey := jsnCfg[THRESHOLDS_JSON]
if !hasKey {

File diff suppressed because one or more lines are too long

View File

@@ -628,6 +628,17 @@ func (cfg *CGRConfig) checkConfigSanity() error {
}
}
}
//SarS checks
if cfg.sarsCfg.Enabled {
for _, connID := range cfg.sarsCfg.StatSConns {
if strings.HasPrefix(connID, utils.MetaInternal) && !cfg.statsCfg.Enabled {
return fmt.Errorf("<%s> not enabled but requested by <%s> component", utils.StatS, utils.SaRS)
}
if _, has := cfg.rpcConns[connID]; !has && !strings.HasPrefix(connID, utils.MetaInternal) {
return fmt.Errorf("<%s> connection with id: <%s> not defined", utils.SaRS, connID)
}
}
}
// RouteS checks
if cfg.routeSCfg.Enabled {
for _, connID := range cfg.routeSCfg.AttributeSConns {

View File

@@ -664,6 +664,11 @@ type StatServJsonCfg struct {
Opts *StatsOptsJson
}
type SarsJsonCfg struct {
Enabled *bool
Stats_conns *[]string
}
type ThresholdsOptsJson struct {
ProfileIDs *[]string `json:"*profileIDs"`
ProfileIgnoreFilters *bool `json:"*profileIgnoreFilters"`

74
config/sarscfg.go Normal file
View File

@@ -0,0 +1,74 @@
/*
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 <http://www.gnu.org/licenses/>
*/
package config
import "github.com/cgrates/cgrates/utils"
type SarSCfg struct {
Enabled bool
StatSConns []string
}
func (sa *SarSCfg) loadFromJSONCfg(jsnCfg *SarsJsonCfg) (err error) {
if jsnCfg == nil {
return
}
if jsnCfg.Enabled != nil {
sa.Enabled = *jsnCfg.Enabled
}
if jsnCfg.Stats_conns != nil {
sa.StatSConns = make([]string, len(*jsnCfg.Stats_conns))
for idx, conn := range *jsnCfg.Stats_conns {
sa.StatSConns[idx] = conn
if conn == utils.MetaInternal {
sa.StatSConns[idx] = utils.ConcatenatedKey(utils.MetaInternal, utils.MetaStats)
}
}
}
return
}
func (sa *SarSCfg) AsMapInterface() (initialMP map[string]any) {
initialMP = map[string]any{
utils.EnabledCfg: sa.Enabled,
}
if sa.StatSConns != nil {
statSConns := make([]string, len(sa.StatSConns))
for i, item := range sa.StatSConns {
statSConns[i] = item
if item == utils.ConcatenatedKey(utils.MetaInternal, utils.MetaStats) {
statSConns[i] = utils.MetaInternal
}
}
initialMP[utils.StatSConnsCfg] = statSConns
}
return
}
func (sa *SarSCfg) Clone() (cln *SarSCfg) {
cln = &SarSCfg{
Enabled: sa.Enabled,
}
if sa.StatSConns != nil {
cln.StatSConns = make([]string, len(sa.StatSConns))
copy(cln.StatSConns, sa.StatSConns)
}
return
}

105
services/sars.go Normal file
View File

@@ -0,0 +1,105 @@
/*
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 <http://www.gnu.org/licenses/>
*/
package services
import (
"fmt"
"sync"
"github.com/cgrates/birpc"
v1 "github.com/cgrates/cgrates/apier/v1"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/cores"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
type SaRService struct {
sync.RWMutex
cfg *config.CGRConfig
dm *DataDBService
cacheS *engine.CacheS
filterSChan chan *engine.FilterS
server *cores.Server
connMgr *engine.ConnManager
connChan chan birpc.ClientConnector
anz *AnalyzerService
srvDep map[string]*sync.WaitGroup
}
// Start should handle the sercive start
func (sa *SaRService) Start() error {
if sa.IsRunning() {
return utils.ErrServiceAlreadyRunning
}
sa.srvDep[utils.DataDB].Add(1)
<-sa.cacheS.GetPrecacheChannel(utils.CacheStatFilterIndexes)
filterS := <-sa.filterSChan
sa.filterSChan <- filterS
dbchan := sa.dm.GetDMChan()
datadb := <-dbchan
dbchan <- datadb
utils.Logger.Info(fmt.Sprintf("<%s> starting <%s> subsystem",
utils.CoreS, utils.SaRS))
srv, err := engine.NewService(v1.NewSArSv1())
if err != nil {
return err
}
if !sa.cfg.DispatcherSCfg().Enabled {
for _, s := range srv {
sa.server.RpcRegister(s)
}
}
sa.connChan <- sa.anz.GetInternalCodec(srv, utils.StatS)
return nil
}
// Reload handles the change of config
func (sa *SaRService) Reload() (err error) {
return
}
// Shutdown stops the service
func (sa *SaRService) Shutdown() (err error) {
defer sa.srvDep[utils.DataDB].Done()
sa.Lock()
defer sa.Unlock()
<-sa.connChan
return
}
// IsRunning returns if the service is running
func (sa *SaRService) IsRunning() bool {
sa.RLock()
defer sa.RUnlock()
return false
}
// ServiceName returns the service name
func (sa *SaRService) ServiceName() string {
return utils.SaRS
}
// ShouldRun returns if the service should be running
func (sa *SaRService) ShouldRun() bool {
return sa.cfg.SarSCfg().Enabled
}

View File

@@ -195,6 +195,8 @@ func (srvMngr *ServiceManager) handleReload() {
go srvMngr.reloadService(utils.ThresholdS)
case <-srvMngr.GetConfig().GetReloadChan(config.STATS_JSON):
go srvMngr.reloadService(utils.StatS)
case <-srvMngr.GetConfig().GetReloadChan(config.SARS_JSON):
go srvMngr.reloadService(utils.SaRS)
case <-srvMngr.GetConfig().GetReloadChan(config.RESOURCES_JSON):
go srvMngr.reloadService(utils.ResourceS)
case <-srvMngr.GetConfig().GetReloadChan(config.RouteSJson):

View File

@@ -1014,6 +1014,7 @@ const (
RouteS = "RouteS"
SessionS = "SessionS"
StatService = "StatS"
SaRS = "SaRS"
ThresholdS = "ThresholdS"
)