Files
cgrates/engine/datamanager.go
2026-01-30 12:56:32 +01:00

3591 lines
117 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 engine
import (
"fmt"
"slices"
"strings"
"sync"
"github.com/cgrates/baningo"
"github.com/cgrates/birpc/context"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/utils"
"github.com/cgrates/guardian"
)
var (
filterIndexesPrefixMap = utils.StringSet{
utils.AttributeFilterIndexes: {},
utils.ResourceFilterIndexes: {},
utils.IPFilterIndexes: {},
utils.StatFilterIndexes: {},
utils.ThresholdFilterIndexes: {},
utils.RouteFilterIndexes: {},
utils.ChargerFilterIndexes: {},
utils.RateProfilesFilterIndexPrfx: {},
utils.ActionProfilesFilterIndexPrfx: {},
utils.RateFilterIndexPrfx: {},
utils.ActionPlanIndexes: {},
utils.FilterIndexPrfx: {},
utils.AccountFilterIndexPrfx: {},
}
cachePrefixMap = utils.StringSet{
utils.ResourceProfilesPrefix: {},
utils.ResourcesPrefix: {},
utils.IPProfilesPrefix: {},
utils.IPAllocationsPrefix: {},
utils.StatQueuePrefix: {},
utils.StatQueueProfilePrefix: {},
utils.ThresholdPrefix: {},
utils.ThresholdProfilePrefix: {},
utils.TrendPrefix: {},
utils.TrendProfilePrefix: {},
utils.RankingProfilePrefix: {},
utils.RankingPrefix: {},
utils.FilterPrefix: {},
utils.RouteProfilePrefix: {},
utils.AttributeProfilePrefix: {},
utils.ChargerProfilePrefix: {},
utils.AccountFilterIndexPrfx: {},
utils.AccountPrefix: {},
utils.RateProfilePrefix: {},
utils.ActionProfilePrefix: {},
utils.AttributeFilterIndexes: {},
utils.ResourceFilterIndexes: {},
utils.IPFilterIndexes: {},
utils.StatFilterIndexes: {},
utils.ThresholdFilterIndexes: {},
utils.RouteFilterIndexes: {},
utils.ChargerFilterIndexes: {},
utils.RateProfilesFilterIndexPrfx: {},
utils.ActionProfilesFilterIndexPrfx: {},
utils.RateFilterIndexPrfx: {},
utils.FilterIndexPrfx: {},
utils.MetaAPIBan: {}, // not realy a prefix as this is not stored in DB
}
)
// NewDataManager returns a new DataManager
func NewDataManager(dbConns *DBConnManager, cfg *config.CGRConfig, connMgr *ConnManager) *DataManager {
ms, _ := utils.NewMarshaler(cfg.GeneralCfg().DBDataEncoding)
dbConns.dataDBCfg = cfg.DbCfg()
dbConns.replicators = make(map[string]*replicator)
for key, dbConnCfg := range cfg.DbCfg().DBConns {
dbConns.replicators[key] = newReplicator(dbConnCfg, connMgr)
}
return &DataManager{
dbConns: dbConns,
cfg: cfg,
connMgr: connMgr,
ms: ms,
}
}
// DBConnManager is the storage manager for all CGRateS DataDBs
// transparently manages data retrieval, further serialization and caching
type DBConnManager struct {
sync.RWMutex // used for locking when using the dataDBs map, important for config reloading
dataDBs map[string]DataDB // Holds all DataDBs connected with CGRateS
replicators map[string]*replicator
dataDBCfg *config.DbCfg // Hold the data_db config
}
// NewDBConnManager returns a new DBConnManager
func NewDBConnManager(dataDBs map[string]DataDB, dataDBCfg *config.DbCfg) *DBConnManager {
return &DBConnManager{
dataDBs: dataDBs,
dataDBCfg: dataDBCfg,
}
}
// GetConn returns the DataDB and DBConn config where the provided itemID is stored.
// Returns the *default dataDB conn if it can't find the item's corresponding DB
func (dbConns *DBConnManager) GetConn(itemID string) (db DataDB, dbConnCfg *config.DBConn, err error) {
dbConns.RLock()
defer dbConns.RUnlock()
var ok bool
dbConnID, ok := dbConns.dataDBCfg.Items[itemID]
if !ok {
return nil, nil, fmt.Errorf("couldn't find item with ID: <%v>", itemID)
}
if db, ok = dbConns.dataDBs[dbConnID.DBConn]; !ok {
// return *default db if DBConn is not found
return dbConns.dataDBs[utils.MetaDefault], dbConns.dataDBCfg.DBConns[dbConnID.DBConn], nil
}
dbConnCfg = dbConns.dataDBCfg.DBConns[dbConnID.DBConn]
return
}
func (dbConns *DBConnManager) GetReplicator(dbConnID string) *replicator {
dbConns.RLock()
defer dbConns.RUnlock()
rpl, ok := dbConns.replicators[dbConnID]
if !ok {
return dbConns.replicators[utils.MetaDefault]
}
return rpl
}
// AddDataDBDriver adds a new DataDBDriver to DBConnManager
func (dbConns *DBConnManager) AddDataDBDriver(dbID string, d DataDBDriver) {
if dbConns.dataDBs == nil {
dbConns.dataDBs = make(map[string]DataDB)
}
dbConns.dataDBs[dbID] = d
}
// DataManager is the data storage manager for CGRateS
// transparently manages data retrieval, further serialization and caching
type DataManager struct {
dbConns *DBConnManager
cfg *config.CGRConfig
connMgr *ConnManager
ms utils.Marshaler
}
// DBConns exports access to DBConns
func (dm *DataManager) DBConns() *DBConnManager {
if dm != nil {
return dm.dbConns
}
return nil
}
// DataDB exports access to dataDB
func (dm *DataManager) DataDB() map[string]DataDB {
if dm != nil {
return dm.dbConns.dataDBs
}
return nil
}
func (dm *DataManager) Close() {
for dataDBKey := range dm.DataDB() {
dm.DataDB()[dataDBKey].Close()
dm.dbConns.replicators[dataDBKey].close()
}
}
// Will return the DataDB based on where the item with the prefix is stored
func (dbConns *DBConnManager) getDataDBForPrefix(prefix string) (dataDB DataDB, err error) {
var itemID string
switch prefix {
case utils.ResourceProfilesPrefix:
itemID = utils.MetaResourceProfiles
case utils.ResourcesPrefix:
itemID = utils.MetaResources
case utils.IPProfilesPrefix:
itemID = utils.MetaIPProfiles
case utils.IPAllocationsPrefix:
itemID = utils.MetaIPAllocations
case utils.StatQueueProfilePrefix:
itemID = utils.MetaStatQueueProfiles
case utils.StatQueuePrefix:
itemID = utils.MetaStatQueues
case utils.ThresholdProfilePrefix:
itemID = utils.MetaThresholdProfiles
case utils.RankingProfilePrefix:
itemID = utils.MetaRankingProfiles
case utils.RankingPrefix:
itemID = utils.MetaRankings
case utils.TrendProfilePrefix:
itemID = utils.MetaTrendProfiles
case utils.TrendPrefix:
itemID = utils.MetaTrends
case utils.ThresholdPrefix:
itemID = utils.MetaThresholds
case utils.FilterPrefix:
itemID = utils.MetaFilters
case utils.RouteProfilePrefix:
itemID = utils.MetaRouteProfiles
case utils.AttributeProfilePrefix:
itemID = utils.MetaAttributeProfiles
case utils.ChargerProfilePrefix:
itemID = utils.MetaChargerProfiles
case utils.AccountPrefix:
itemID = utils.MetaAccounts
case utils.RateProfilePrefix:
itemID = utils.MetaRateProfiles
case utils.ActionProfilePrefix:
itemID = utils.MetaActionProfiles
case utils.AttributeFilterIndexes:
itemID = utils.CacheAttributeFilterIndexes
case utils.ResourceFilterIndexes:
itemID = utils.CacheResourceFilterIndexes
case utils.IPFilterIndexes:
itemID = utils.CacheIPFilterIndexes
case utils.StatFilterIndexes:
itemID = utils.CacheStatFilterIndexes
case utils.ThresholdFilterIndexes:
itemID = utils.CacheThresholdFilterIndexes
case utils.RouteFilterIndexes:
itemID = utils.CacheRouteFilterIndexes
case utils.ChargerFilterIndexes:
itemID = utils.CacheChargerFilterIndexes
case utils.RateProfilesFilterIndexPrfx:
itemID = utils.CacheRateProfilesFilterIndexes
case utils.RateFilterIndexPrfx:
itemID = utils.CacheRateFilterIndexes
case utils.ActionProfilesFilterIndexPrfx:
itemID = utils.CacheActionProfilesFilterIndexes
case utils.AccountFilterIndexPrfx:
itemID = utils.CacheAccountsFilterIndexes
case utils.FilterIndexPrfx:
itemID = utils.CacheReverseFilterIndexes
case utils.LoadIDPrefix:
itemID = utils.MetaLoadIDs
default:
return nil, fmt.Errorf("unknown prefix: <%v>", prefix)
}
dataDB, _, err = dbConns.GetConn(itemID)
return
}
func (dm *DataManager) CacheDataFromDB(ctx *context.Context, prfx string, ids []string, mustBeCached bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
if !cachePrefixMap.Has(prfx) {
return utils.NewCGRError(utils.DataManager,
utils.MandatoryIEMissingCaps,
utils.UnsupportedCachePrefix,
fmt.Sprintf("prefix <%s> is not a supported cache prefix", prfx))
}
if dm.cfg.CacheCfg().Partitions[utils.CachePrefixToInstance[prfx]].Limit == 0 {
return
}
// *apiban and *dispatchers are not stored in database
if prfx == utils.MetaAPIBan || prfx == utils.MetaSentryPeer { // no need for ids in this case
ids = []string{utils.EmptyString}
} else if len(ids) != 0 && ids[0] == utils.MetaAny {
if mustBeCached {
ids = Cache.GetItemIDs(utils.CachePrefixToInstance[prfx], utils.EmptyString)
} else {
dataDB, err := dm.dbConns.getDataDBForPrefix(prfx)
if err != nil {
return utils.NewCGRError(utils.DataManager,
utils.ServerErrorCaps,
err.Error(),
fmt.Sprintf("DataManager error <%s> for prefix <%s>", err.Error(), prfx))
}
if ids, err = dataDB.GetKeysForPrefix(ctx, prfx); err != nil {
return utils.NewCGRError(utils.DataManager,
utils.ServerErrorCaps,
err.Error(),
fmt.Sprintf("DataManager error <%s> querying keys for prefix: <%s>", err.Error(), prfx))
}
if cCfg, has := dm.cfg.CacheCfg().Partitions[utils.CachePrefixToInstance[prfx]]; has &&
cCfg.Limit >= 0 &&
cCfg.Limit < len(ids) {
ids = ids[:cCfg.Limit]
}
for i := range ids {
ids[i] = strings.TrimPrefix(ids[i], prfx)
}
}
}
for _, dataID := range ids {
if mustBeCached &&
!Cache.HasItem(utils.CachePrefixToInstance[prfx], dataID) { // only cache if previously there
continue
}
switch prfx {
case utils.ResourceProfilesPrefix:
tntID := utils.NewTenantID(dataID)
lkID := guardian.Guardian.GuardIDs("", dm.cfg.GeneralCfg().LockingTimeout, utils.ResourceProfileLockKey(tntID.Tenant, tntID.ID))
_, err = dm.GetResourceProfile(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional)
guardian.Guardian.UnguardIDs(lkID)
case utils.ResourcesPrefix:
tntID := utils.NewTenantID(dataID)
lkID := guardian.Guardian.GuardIDs("", dm.cfg.GeneralCfg().LockingTimeout, utils.ResourceLockKey(tntID.Tenant, tntID.ID))
_, err = dm.GetResource(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional)
guardian.Guardian.UnguardIDs(lkID)
case utils.IPProfilesPrefix:
tntID := utils.NewTenantID(dataID)
lkID := guardian.Guardian.GuardIDs("", dm.cfg.GeneralCfg().LockingTimeout, utils.IPProfileLockKey(tntID.Tenant, tntID.ID))
_, err = dm.GetIPProfile(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional)
guardian.Guardian.UnguardIDs(lkID)
case utils.IPAllocationsPrefix:
tntID := utils.NewTenantID(dataID)
lkID := guardian.Guardian.GuardIDs("", dm.cfg.GeneralCfg().LockingTimeout, utils.IPAllocationsLockKey(tntID.Tenant, tntID.ID))
_, err = dm.GetIPAllocations(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional, nil)
guardian.Guardian.UnguardIDs(lkID)
case utils.StatQueueProfilePrefix:
tntID := utils.NewTenantID(dataID)
lkID := guardian.Guardian.GuardIDs("", dm.cfg.GeneralCfg().LockingTimeout, statQueueProfileLockKey(tntID.Tenant, tntID.ID))
_, err = dm.GetStatQueueProfile(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional)
guardian.Guardian.UnguardIDs(lkID)
case utils.StatQueuePrefix:
tntID := utils.NewTenantID(dataID)
lkID := guardian.Guardian.GuardIDs("", dm.cfg.GeneralCfg().LockingTimeout, statQueueLockKey(tntID.Tenant, tntID.ID))
_, err = dm.GetStatQueue(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional)
guardian.Guardian.UnguardIDs(lkID)
case utils.ThresholdProfilePrefix:
tntID := utils.NewTenantID(dataID)
lkID := guardian.Guardian.GuardIDs("", dm.cfg.GeneralCfg().LockingTimeout, thresholdProfileLockKey(tntID.Tenant, tntID.ID))
_, err = dm.GetThresholdProfile(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional)
guardian.Guardian.UnguardIDs(lkID)
case utils.RankingProfilePrefix:
tntID := utils.NewTenantID(dataID)
lkID := guardian.Guardian.GuardIDs("", dm.cfg.GeneralCfg().LockingTimeout, utils.RankingProfileLockKey(tntID.Tenant, tntID.ID))
_, err = dm.GetRankingProfile(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional)
guardian.Guardian.UnguardIDs(lkID)
case utils.TrendProfilePrefix:
tntID := utils.NewTenantID(dataID)
_, err = dm.GetTrendProfile(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional)
case utils.ThresholdPrefix:
tntID := utils.NewTenantID(dataID)
lkID := guardian.Guardian.GuardIDs("", dm.cfg.GeneralCfg().LockingTimeout, thresholdLockKey(tntID.Tenant, tntID.ID))
_, err = dm.GetThreshold(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional)
guardian.Guardian.UnguardIDs(lkID)
case utils.FilterPrefix:
tntID := utils.NewTenantID(dataID)
_, err = dm.GetFilter(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional)
case utils.RouteProfilePrefix:
tntID := utils.NewTenantID(dataID)
_, err = dm.GetRouteProfile(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional)
case utils.AttributeProfilePrefix:
tntID := utils.NewTenantID(dataID)
_, err = dm.GetAttributeProfile(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional)
case utils.ChargerProfilePrefix:
tntID := utils.NewTenantID(dataID)
_, err = dm.GetChargerProfile(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional)
case utils.RateProfilePrefix:
tntID := utils.NewTenantID(dataID)
_, err = dm.GetRateProfile(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional)
case utils.ActionProfilePrefix:
tntID := utils.NewTenantID(dataID)
_, err = dm.GetActionProfile(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional)
case utils.AttributeFilterIndexes:
var tntCtx, idxKey string
if tntCtx, idxKey, err = splitFilterIndex(dataID); err != nil {
return
}
_, err = dm.GetIndexes(ctx, utils.CacheAttributeFilterIndexes, tntCtx, idxKey, utils.NonTransactional, false, true)
case utils.ResourceFilterIndexes:
var tntCtx, idxKey string
if tntCtx, idxKey, err = splitFilterIndex(dataID); err != nil {
return
}
_, err = dm.GetIndexes(ctx, utils.CacheResourceFilterIndexes, tntCtx, idxKey, utils.NonTransactional, false, true)
case utils.IPFilterIndexes:
var tntCtx, idxKey string
if tntCtx, idxKey, err = splitFilterIndex(dataID); err != nil {
return
}
_, err = dm.GetIndexes(ctx, utils.CacheIPFilterIndexes, tntCtx, idxKey, utils.NonTransactional, false, true)
case utils.StatFilterIndexes:
var tntCtx, idxKey string
if tntCtx, idxKey, err = splitFilterIndex(dataID); err != nil {
return
}
_, err = dm.GetIndexes(ctx, utils.CacheStatFilterIndexes, tntCtx, idxKey, utils.NonTransactional, false, true)
case utils.ThresholdFilterIndexes:
var tntCtx, idxKey string
if tntCtx, idxKey, err = splitFilterIndex(dataID); err != nil {
return
}
_, err = dm.GetIndexes(ctx, utils.CacheThresholdFilterIndexes, tntCtx, idxKey, utils.NonTransactional, false, true)
case utils.RouteFilterIndexes:
var tntCtx, idxKey string
if tntCtx, idxKey, err = splitFilterIndex(dataID); err != nil {
return
}
_, err = dm.GetIndexes(ctx, utils.CacheRouteFilterIndexes, tntCtx, idxKey, utils.NonTransactional, false, true)
case utils.ChargerFilterIndexes:
var tntCtx, idxKey string
if tntCtx, idxKey, err = splitFilterIndex(dataID); err != nil {
return
}
_, err = dm.GetIndexes(ctx, utils.CacheChargerFilterIndexes, tntCtx, idxKey, utils.NonTransactional, false, true)
case utils.RateProfilesFilterIndexPrfx:
var tntCtx, idxKey string
if tntCtx, idxKey, err = splitFilterIndex(dataID); err != nil {
return
}
_, err = dm.GetIndexes(ctx, utils.CacheRateProfilesFilterIndexes, tntCtx, idxKey, utils.NonTransactional, false, true)
case utils.RateFilterIndexPrfx:
var tntCtx, idxKey string
if tntCtx, idxKey, err = splitFilterIndex(dataID); err != nil {
return
}
_, err = dm.GetIndexes(ctx, utils.CacheRateFilterIndexes, tntCtx, idxKey, utils.NonTransactional, false, true)
case utils.ActionProfilesFilterIndexPrfx:
var tntCtx, idxKey string
if tntCtx, idxKey, err = splitFilterIndex(dataID); err != nil {
return
}
_, err = dm.GetIndexes(ctx, utils.CacheActionProfilesFilterIndexes, tntCtx, idxKey, utils.NonTransactional, false, true)
case utils.AccountFilterIndexPrfx:
var tntCtx, idxKey string
if tntCtx, idxKey, err = splitFilterIndex(dataID); err != nil {
return
}
_, err = dm.GetIndexes(ctx, utils.CacheAccountsFilterIndexes, tntCtx, idxKey, utils.NonTransactional, false, true)
case utils.FilterIndexPrfx:
idx := strings.LastIndexByte(dataID, utils.InInFieldSep[0])
if idx < 0 {
err = fmt.Errorf("WRONG_IDX_KEY_FORMAT<%s>", dataID)
return
}
_, err = dm.GetIndexes(ctx, utils.CacheReverseFilterIndexes, dataID[:idx], dataID[idx+1:], utils.NonTransactional, false, true)
case utils.LoadIDPrefix:
_, err = dm.GetItemLoadIDs(ctx, utils.EmptyString, true)
case utils.MetaAPIBan:
_, err = GetAPIBan(ctx, utils.EmptyString, dm.cfg.APIBanCfg().Keys, false, false, true)
}
if err != nil {
if err != utils.ErrNotFound &&
err != utils.ErrDSPProfileNotFound &&
err != utils.ErrDSPHostNotFound {
return utils.NewCGRError(utils.DataManager,
utils.ServerErrorCaps,
err.Error(),
fmt.Sprintf("error <%s> querying DataManager for category: <%s>, dataID: <%s>", err.Error(), prfx, dataID))
}
err = nil
// if err = Cache.Remove(ctx, utils.CachePrefixToInstance[prfx], dataID,
// cacheCommit(utils.NonTransactional), utils.NonTransactional); err != nil {
// return
// }
}
}
return
}
// GetFilter returns a filter based on the given ID
func (dm *DataManager) GetFilter(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool,
transactionID string) (fltr *Filter, err error) {
tntID := utils.ConcatenatedKey(tenant, id)
if cacheRead {
if x, ok := Cache.Get(utils.CacheFilters, tntID); ok {
if x == nil {
return nil, utils.ErrNotFound
}
return x.(*Filter), nil
}
}
if strings.HasPrefix(id, utils.Meta) {
if fltr, err = NewFilterFromInline(tenant, id); err != nil {
return
}
} else if dm == nil { // in case we want the filter from dataDB but the connection to dataDB a optional (e.g. SessionS)
err = utils.ErrNoDatabaseConn
return
} else {
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaFilters)
if err != nil {
return nil, err
}
fltr, err = dataDB.GetFilterDrv(ctx, tenant, id)
if err != nil {
if itm := dm.cfg.DbCfg().Items[utils.MetaFilters]; err == utils.ErrNotFound && itm.Remote {
if err = dm.connMgr.Call(ctx, dbCfg.RmtConns, utils.ReplicatorSv1GetFilter,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &fltr); err == nil {
err = dataDB.SetFilterDrv(ctx, fltr)
}
}
if err != nil {
err = utils.CastRPCErr(err)
if err == utils.ErrNotFound && cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheFilters, tntID, nil, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return fltr, err
}
}
if err = fltr.Compile(); err != nil { // only compile the value when we get the filter from DB or from remote0
return nil, err
}
}
if cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheFilters, tntID, fltr, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return
}
func (dm *DataManager) SetFilter(ctx *context.Context, fltr *Filter, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
var oldFlt *Filter
if oldFlt, err = dm.GetFilter(ctx, fltr.Tenant, fltr.ID, true, false,
utils.NonTransactional); err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaFilters)
if err != nil {
return err
}
if err = dataDB.SetFilterDrv(ctx, fltr); err != nil {
return
}
if withIndex {
if err = UpdateFilterIndex(ctx, dm, oldFlt, fltr); err != nil {
return
}
}
if itm := dm.cfg.DbCfg().Items[utils.MetaFilters]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
err = rpl.replicate(ctx,
utils.FilterPrefix, fltr.TenantID(), // this are used to get the host IDs from cache
utils.ReplicatorSv1SetFilter,
&FilterWithAPIOpts{
Filter: fltr,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) RemoveFilter(ctx *context.Context, tenant, id string, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
var oldFlt *Filter
if oldFlt, err = dm.GetFilter(ctx, tenant, id, true, false,
utils.NonTransactional); err != nil && err != utils.ErrNotFound {
return err
}
var tntCtx string
if withIndex {
tntCtx = utils.ConcatenatedKey(tenant, id)
var rcvIndx map[string]utils.StringSet
if rcvIndx, err = dm.GetIndexes(ctx, utils.CacheReverseFilterIndexes, tntCtx,
utils.EmptyString, utils.NonTransactional, true, true); err != nil {
if err != utils.ErrNotFound {
return
}
err = nil // no index for this filter so no remove needed from index side
} else {
return fmt.Errorf("cannot remove filter <%s> because will broken the reference to following items: %s",
tntCtx, utils.ToJSON(rcvIndx))
}
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaFilters)
if err != nil {
return err
}
if err = dataDB.RemoveFilterDrv(ctx, tenant, id); err != nil {
return
}
if oldFlt == nil {
return utils.ErrNotFound
}
if itm := dm.cfg.DbCfg().Items[utils.MetaFilters]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
rpl.replicate(ctx,
utils.FilterPrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache
utils.ReplicatorSv1RemoveFilter,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) GetThreshold(ctx *context.Context, tenant, id string,
cacheRead, cacheWrite bool, transactionID string) (th *Threshold, err error) {
tntID := utils.ConcatenatedKey(tenant, id)
if cacheRead {
if x, ok := Cache.Get(utils.CacheThresholds, tntID); ok {
if x == nil {
return nil, utils.ErrNotFound
}
return x.(*Threshold), nil
}
}
if dm == nil {
err = utils.ErrNoDatabaseConn
return
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaThresholds)
if err != nil {
return nil, err
}
th, err = dataDB.GetThresholdDrv(ctx, tenant, id)
if err != nil {
if itm := dm.cfg.DbCfg().Items[utils.MetaThresholds]; err == utils.ErrNotFound && itm.Remote {
if err = dm.connMgr.Call(ctx, dbCfg.RmtConns,
utils.ReplicatorSv1GetThreshold, &utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &th); err == nil {
err = dataDB.SetThresholdDrv(ctx, th)
}
}
if err != nil {
err = utils.CastRPCErr(err)
if err == utils.ErrNotFound && cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheThresholds, tntID, nil, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return nil, err
}
}
if cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheThresholds, tntID, th, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return
}
func (dm *DataManager) SetThreshold(ctx *context.Context, th *Threshold) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaThresholds)
if err != nil {
return err
}
if err = dataDB.SetThresholdDrv(ctx, th); err != nil {
return
}
if itm := dm.cfg.DbCfg().Items[utils.MetaThresholds]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
err = rpl.replicate(ctx,
utils.ThresholdPrefix, th.TenantID(), // this are used to get the host IDs from cache
utils.ReplicatorSv1SetThreshold,
&ThresholdWithAPIOpts{
Threshold: th,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) RemoveThreshold(ctx *context.Context, tenant, id string) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaThresholds)
if err != nil {
return err
}
if err = dataDB.RemoveThresholdDrv(ctx, tenant, id); err != nil {
return
}
if itm := dm.cfg.DbCfg().Items[utils.MetaThresholds]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
rpl.replicate(ctx,
utils.ThresholdPrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache
utils.ReplicatorSv1RemoveThreshold,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) GetThresholdProfile(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool,
transactionID string) (th *ThresholdProfile, err error) {
tntID := utils.ConcatenatedKey(tenant, id)
if cacheRead {
if x, ok := Cache.Get(utils.CacheThresholdProfiles, tntID); ok {
if x == nil {
return nil, utils.ErrNotFound
}
return x.(*ThresholdProfile), nil
}
}
if dm == nil {
err = utils.ErrNoDatabaseConn
return
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaThresholdProfiles)
if err != nil {
return nil, err
}
th, err = dataDB.GetThresholdProfileDrv(ctx, tenant, id)
if err != nil {
if itm := dm.cfg.DbCfg().Items[utils.MetaThresholdProfiles]; err == utils.ErrNotFound && itm.Remote {
if err = dm.connMgr.Call(ctx, dbCfg.RmtConns,
utils.ReplicatorSv1GetThresholdProfile,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &th); err == nil {
err = dataDB.SetThresholdProfileDrv(ctx, th)
}
}
if err != nil {
err = utils.CastRPCErr(err)
if err == utils.ErrNotFound && cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheThresholdProfiles, tntID, nil, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return nil, err
}
}
if cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheThresholdProfiles, tntID, th, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return
}
func (dm *DataManager) SetThresholdProfile(ctx *context.Context, th *ThresholdProfile, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
if withIndex {
if err := dm.checkFilters(ctx, th.Tenant, th.FilterIDs); err != nil {
// if we get a broken filter do not set the profile
return fmt.Errorf("%+s for item with ID: %+v",
err, th.TenantID())
}
}
oldTh, err := dm.GetThresholdProfile(ctx, th.Tenant, th.ID, true, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaThresholdProfiles)
if err != nil {
return err
}
if err = dataDB.SetThresholdProfileDrv(ctx, th); err != nil {
return err
}
if withIndex {
var oldFiltersIDs *[]string
if oldTh != nil {
oldFiltersIDs = &oldTh.FilterIDs
}
if err := updatedIndexes(ctx, dm, utils.CacheThresholdFilterIndexes, th.Tenant,
utils.EmptyString, th.ID, oldFiltersIDs, th.FilterIDs, false); err != nil {
return err
}
}
if itm := dm.cfg.DbCfg().Items[utils.MetaThresholdProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
if err = rpl.replicate(ctx,
utils.ThresholdProfilePrefix, th.TenantID(), // this are used to get the host IDs from cache
utils.ReplicatorSv1SetThresholdProfile,
&ThresholdProfileWithAPIOpts{
ThresholdProfile: th,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm); err != nil {
return
}
}
if oldTh == nil || // create the threshold if it didn't exist before
oldTh.MaxHits != th.MaxHits ||
oldTh.MinHits != th.MinHits ||
oldTh.MinSleep != th.MinSleep { // reset the threshold if the profile changed this fields
err = dm.SetThreshold(ctx, &Threshold{
Tenant: th.Tenant,
ID: th.ID,
Hits: 0,
})
} else if _, errTh := dm.GetThreshold(ctx, th.Tenant, th.ID, // do not try to get the threshold if the configuration changed
true, false, utils.NonTransactional); errTh == utils.ErrNotFound { // the threshold does not exist
err = dm.SetThreshold(ctx, &Threshold{
Tenant: th.Tenant,
ID: th.ID,
Hits: 0,
})
}
return
}
func (dm *DataManager) RemoveThresholdProfile(ctx *context.Context, tenant, id string, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
oldTh, err := dm.GetThresholdProfile(ctx, tenant, id, true, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaThresholdProfiles)
if err != nil {
return err
}
if err = dataDB.RemThresholdProfileDrv(ctx, tenant, id); err != nil {
return
}
if oldTh == nil {
return utils.ErrNotFound
}
if withIndex {
if err = removeIndexFiltersItem(ctx, dm, utils.CacheThresholdFilterIndexes, tenant, id, oldTh.FilterIDs); err != nil {
return
}
if err = removeItemFromFilterIndex(ctx, dm, utils.CacheThresholdFilterIndexes,
tenant, utils.EmptyString, id, oldTh.FilterIDs); err != nil {
return
}
}
if itm := dm.cfg.DbCfg().Items[utils.MetaThresholdProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
rpl.replicate(ctx,
utils.ThresholdProfilePrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache
utils.ReplicatorSv1RemoveThresholdProfile,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return dm.RemoveThreshold(ctx, tenant, id) // remove the threshold
}
// GetStatQueue retrieves a StatQueue from dataDB
// handles caching and deserialization of metrics
func (dm *DataManager) GetStatQueue(ctx *context.Context, tenant, id string,
cacheRead, cacheWrite bool, transactionID string) (sq *StatQueue, err error) {
tntID := utils.ConcatenatedKey(tenant, id)
if cacheRead {
if x, ok := Cache.Get(utils.CacheStatQueues, tntID); ok {
if x == nil {
return nil, utils.ErrNotFound
}
return x.(*StatQueue), nil
}
}
if dm == nil {
err = utils.ErrNoDatabaseConn
return
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaStatQueues)
if err != nil {
return nil, err
}
sq, err = dataDB.GetStatQueueDrv(ctx, tenant, id)
if err != nil {
if itm := dm.cfg.DbCfg().Items[utils.MetaStatQueues]; err == utils.ErrNotFound && itm.Remote {
if err = dm.connMgr.Call(ctx, dbCfg.RmtConns, utils.ReplicatorSv1GetStatQueue,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &sq); err == nil {
var ssq *StoredStatQueue
if dataDB.GetStorageType() != utils.MetaInternal {
// in case of internal we don't marshal
if ssq, err = NewStoredStatQueue(sq, dm.ms); err != nil {
return nil, err
}
}
err = dataDB.SetStatQueueDrv(ctx, ssq, sq)
}
}
if err != nil {
if err = utils.CastRPCErr(err); err == utils.ErrNotFound && cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheStatQueues, tntID, nil, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return nil, err
}
}
if cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheStatQueues, tntID, sq, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return
}
// SetStatQueue converts to StoredStatQueue and stores the result in dataDB
func (dm *DataManager) SetStatQueue(ctx *context.Context, sq *StatQueue) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaStatQueues)
if err != nil {
return err
}
var ssq *StoredStatQueue
if dataDB.GetStorageType() != utils.MetaInternal {
// in case of internal we don't marshal
if ssq, err = NewStoredStatQueue(sq, dm.ms); err != nil {
return
}
}
if err = dataDB.SetStatQueueDrv(ctx, ssq, sq); err != nil {
return
}
if itm := dm.cfg.DbCfg().Items[utils.MetaStatQueues]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
err = rpl.replicate(ctx,
utils.StatQueuePrefix, sq.TenantID(), // this are used to get the host IDs from cache
utils.ReplicatorSv1SetStatQueue,
&StatQueueWithAPIOpts{
StatQueue: sq,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
// RemoveStatQueue removes the StoredStatQueue
func (dm *DataManager) RemoveStatQueue(ctx *context.Context, tenant, id string) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaStatQueues)
if err != nil {
return err
}
if err = dataDB.RemStatQueueDrv(ctx, tenant, id); err != nil {
return
}
if itm := dm.cfg.DbCfg().Items[utils.MetaStatQueues]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
rpl.replicate(ctx,
utils.StatQueuePrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache
utils.ReplicatorSv1RemoveStatQueue,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) GetStatQueueProfile(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool,
transactionID string) (sqp *StatQueueProfile, err error) {
tntID := utils.ConcatenatedKey(tenant, id)
if cacheRead {
if x, ok := Cache.Get(utils.CacheStatQueueProfiles, tntID); ok {
if x == nil {
return nil, utils.ErrNotFound
}
return x.(*StatQueueProfile), nil
}
}
if dm == nil {
err = utils.ErrNoDatabaseConn
return
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaStatQueueProfiles)
if err != nil {
return nil, err
}
sqp, err = dataDB.GetStatQueueProfileDrv(ctx, tenant, id)
if err != nil {
if itm := dm.cfg.DbCfg().Items[utils.MetaStatQueueProfiles]; err == utils.ErrNotFound && itm.Remote {
if err = dm.connMgr.Call(ctx, dbCfg.RmtConns,
utils.ReplicatorSv1GetStatQueueProfile,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &sqp); err == nil {
err = dataDB.SetStatQueueProfileDrv(ctx, sqp)
}
}
if err != nil {
err = utils.CastRPCErr(err)
if err == utils.ErrNotFound && cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheStatQueueProfiles, tntID, nil, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return nil, err
}
}
if cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheStatQueueProfiles, tntID, sqp, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return
}
func (dm *DataManager) SetStatQueueProfile(ctx *context.Context, sqp *StatQueueProfile, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
if withIndex {
if err := dm.checkFilters(ctx, sqp.Tenant, sqp.FilterIDs); err != nil {
// if we get a broken filter do not set the profile
return fmt.Errorf("%+s for item with ID: %+v",
err, sqp.TenantID())
}
}
oldSts, err := dm.GetStatQueueProfile(ctx, sqp.Tenant, sqp.ID, true, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaStatQueueProfiles)
if err != nil {
return err
}
if err = dataDB.SetStatQueueProfileDrv(ctx, sqp); err != nil {
return err
}
if withIndex {
var oldFiltersIDs *[]string
if oldSts != nil {
oldFiltersIDs = &oldSts.FilterIDs
}
if err := updatedIndexes(ctx, dm, utils.CacheStatFilterIndexes, sqp.Tenant,
utils.EmptyString, sqp.ID, oldFiltersIDs, sqp.FilterIDs, false); err != nil {
return err
}
}
if itm := dm.cfg.DbCfg().Items[utils.MetaStatQueueProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
if err = rpl.replicate(ctx,
utils.StatQueueProfilePrefix, sqp.TenantID(), // this are used to get the host IDs from cache
utils.ReplicatorSv1SetStatQueueProfile,
&StatQueueProfileWithAPIOpts{
StatQueueProfile: sqp,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm); err != nil {
return
}
}
if oldSts == nil || // create the stats queue if it didn't exist before
oldSts.QueueLength != sqp.QueueLength ||
oldSts.TTL != sqp.TTL ||
oldSts.MinItems != sqp.MinItems ||
oldSts.Stored != sqp.Stored && oldSts.Stored { // reset the stats queue if the profile changed this fields
guardian.Guardian.Guard(ctx, func(ctx *context.Context) (_ error) { // we change the queue so lock it
var sq *StatQueue
if sq, err = NewStatQueue(sqp.Tenant, sqp.ID, sqp.Metrics,
uint64(sqp.MinItems)); err != nil {
return
}
err = dm.SetStatQueue(ctx, sq)
return
}, dm.cfg.GeneralCfg().LockingTimeout, utils.StatQueuePrefix+sqp.TenantID())
} else {
guardian.Guardian.Guard(ctx, func(ctx *context.Context) (_ error) { // we change the queue so lock it
oSq, errRs := dm.GetStatQueue(ctx, sqp.Tenant, sqp.ID, // do not try to get the stats queue if the configuration changed
true, false, utils.NonTransactional)
if errRs == utils.ErrNotFound { // the stats queue does not exist
var sq *StatQueue
if sq, err = NewStatQueue(sqp.Tenant, sqp.ID, sqp.Metrics,
uint64(sqp.MinItems)); err != nil {
return
}
err = dm.SetStatQueue(ctx, sq)
return
} else if errRs != nil {
return
}
// update the metrics if needed
cMetricIDs := utils.StringSet{}
for _, metric := range sqp.Metrics { // add missing metrics and recreate the old metrics that changed
cMetricIDs.Add(metric.MetricID)
if oSqMetric, has := oSq.SQMetrics[metric.MetricID]; !has ||
!slices.Equal(oSqMetric.GetFilterIDs(), metric.FilterIDs) { // recreate it if the filter changed
if oSq.SQMetrics[metric.MetricID], err = NewStatMetric(metric.MetricID,
uint64(sqp.MinItems), metric.FilterIDs); err != nil {
return
}
}
}
for sqMetricID := range oSq.SQMetrics { // remove the old metrics
if !cMetricIDs.Has(sqMetricID) {
delete(oSq.SQMetrics, sqMetricID)
}
}
if sqp.Stored { // already changed the value in cache
err = dm.SetStatQueue(ctx, oSq) // only set it in DB if Stored is true
}
return
}, dm.cfg.GeneralCfg().LockingTimeout, utils.StatQueuePrefix+sqp.TenantID())
}
return
}
func (dm *DataManager) RemoveStatQueueProfile(ctx *context.Context, tenant, id string, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
oldSts, err := dm.GetStatQueueProfile(ctx, tenant, id, true, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaStatQueueProfiles)
if err != nil {
return err
}
if err = dataDB.RemStatQueueProfileDrv(ctx, tenant, id); err != nil {
return
}
if oldSts == nil {
return utils.ErrNotFound
}
if withIndex {
if err = removeIndexFiltersItem(ctx, dm, utils.CacheStatFilterIndexes, tenant, id, oldSts.FilterIDs); err != nil {
return
}
if err = removeItemFromFilterIndex(ctx, dm, utils.CacheStatFilterIndexes,
tenant, utils.EmptyString, id, oldSts.FilterIDs); err != nil {
return
}
}
if itm := dm.cfg.DbCfg().Items[utils.MetaStatQueueProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
rpl.replicate(ctx,
utils.StatQueueProfilePrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache
utils.ReplicatorSv1RemoveStatQueueProfile,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return dm.RemoveStatQueue(ctx, tenant, id)
}
// GetTrend retrieves a Trend from dataDB
func (dm *DataManager) GetTrend(ctx *context.Context, tenant, id string,
cacheRead, cacheWrite bool, transactionID string) (tr *utils.Trend, err error) {
tntID := utils.ConcatenatedKey(tenant, id)
if cacheRead {
if x, ok := Cache.Get(utils.CacheTrends, tntID); ok {
if x == nil {
return nil, utils.ErrNotFound
}
return x.(*utils.Trend), nil
}
}
if dm == nil {
err = utils.ErrNoDatabaseConn
return
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaTrends)
if err != nil {
return nil, err
}
if tr, err = dataDB.GetTrendDrv(ctx, tenant, id); err != nil {
if err != utils.ErrNotFound { // database error
return
}
// ErrNotFound
if itm := dm.cfg.DbCfg().Items[utils.MetaTrends]; itm.Remote {
if err = dm.connMgr.Call(context.TODO(), dbCfg.RmtConns,
utils.ReplicatorSv1GetTrend, &utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &tr); err != nil {
err = utils.CastRPCErr(err)
if err != utils.ErrNotFound { // RPC error
return
}
} else if err = dataDB.SetTrendDrv(ctx, tr); err != nil {
return
}
}
// have Trend or ErrNotFound
if err == utils.ErrNotFound {
if cacheWrite {
if errCache := Cache.Set(ctx, utils.CacheTrends, tntID, nil, nil,
cacheCommit(transactionID), transactionID); errCache != nil {
return nil, errCache
}
}
return
}
}
if err = tr.Uncompress(dm.ms); err != nil {
return nil, err
}
if cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheTrends, tntID, tr, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return
}
// SetTrend stores Trend in dataDB
func (dm *DataManager) SetTrend(ctx *context.Context, tr *utils.Trend) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaTrends)
if err != nil {
return err
}
if dataDB.GetStorageType() != utils.MetaInternal {
if tr, err = tr.Compress(dm.ms, dm.cfg.TrendSCfg().StoreUncompressedLimit); err != nil {
return
}
}
if err = dataDB.SetTrendDrv(ctx, tr); err != nil {
return
}
if itm := dm.cfg.DbCfg().Items[utils.MetaTrends]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
if err = rpl.replicate(ctx,
utils.TrendPrefix, tr.TenantID(), // this are used to get the host IDs from cache
utils.ReplicatorSv1SetTrend,
&utils.TrendWithAPIOpts{
Trend: tr,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm); err != nil {
return
}
}
return
}
// RemoveTrend removes the stored Trend
func (dm *DataManager) RemoveTrend(ctx *context.Context, tenant, id string) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaTrends)
if err != nil {
return err
}
if err = dataDB.RemoveTrendDrv(ctx, tenant, id); err != nil {
return
}
if itm := dm.cfg.DbCfg().Items[utils.MetaTrends]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
rpl.replicate(ctx,
utils.TrendPrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache
utils.ReplicatorSv1RemoveTrend,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) GetTrendProfile(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool,
transactionID string) (trp *utils.TrendProfile, err error) {
tntID := utils.ConcatenatedKey(tenant, id)
if cacheRead {
if x, ok := Cache.Get(utils.CacheTrendProfiles, tntID); ok {
if x == nil {
return nil, utils.ErrNotFound
}
return x.(*utils.TrendProfile), nil
}
}
if dm == nil {
err = utils.ErrNoDatabaseConn
return
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaTrendProfiles)
if err != nil {
return nil, err
}
trp, err = dataDB.GetTrendProfileDrv(ctx, tenant, id)
if err != nil {
if itm := dm.cfg.DbCfg().Items[utils.MetaTrendProfiles]; err == utils.ErrNotFound && itm.Remote {
if err = dm.connMgr.Call(ctx, dbCfg.RmtConns,
utils.ReplicatorSv1GetTrendProfile,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &trp); err == nil {
err = dataDB.SetTrendProfileDrv(ctx, trp)
}
}
if err != nil {
err = utils.CastRPCErr(err)
if err == utils.ErrNotFound && cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheTrendProfiles, tntID, nil, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return nil, err
}
}
if cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheTrendProfiles, tntID, trp, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return
}
func (dm *DataManager) GetTrendProfileIDs(ctx *context.Context, tenants []string) (tps map[string][]string, err error) {
prfx := utils.TrendProfilePrefix
var keys []string
dataDB, _, err := dm.dbConns.GetConn(utils.MetaTrendProfiles)
if err != nil {
return nil, err
}
if len(tenants) == 0 {
keys, err = dataDB.GetKeysForPrefix(ctx, prfx)
if err != nil {
return
}
} else {
for _, tenant := range tenants {
var tntkeys []string
tntPrfx := prfx + tenant + utils.ConcatenatedKeySep
tntkeys, err = dataDB.GetKeysForPrefix(ctx, tntPrfx)
if err != nil {
return
}
keys = append(keys, tntkeys...)
}
}
// if len(keys) == 0 {
// return nil, utils.ErrNotFound
// }
tps = make(map[string][]string)
for _, key := range keys {
indx := strings.Index(key, utils.ConcatenatedKeySep)
tenant := key[len(utils.TrendProfilePrefix):indx]
id := key[indx+1:]
tps[tenant] = append(tps[tenant], id)
}
return
}
func (dm *DataManager) SetTrendProfile(ctx *context.Context, trp *utils.TrendProfile) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
oldTrd, err := dm.GetTrendProfile(ctx, trp.Tenant, trp.ID, true, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaTrendProfiles)
if err != nil {
return err
}
if err = dataDB.SetTrendProfileDrv(ctx, trp); err != nil {
return err
}
if itm := dm.cfg.DbCfg().Items[utils.MetaTrendProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
err = rpl.replicate(ctx,
utils.TrendProfilePrefix, trp.TenantID(),
utils.ReplicatorSv1SetTrendProfile,
&utils.TrendProfileWithAPIOpts{
TrendProfile: trp,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
if oldTrd == nil ||
oldTrd.QueueLength != trp.QueueLength ||
oldTrd.Schedule != trp.Schedule {
if err = dm.SetTrend(ctx, utils.NewTrendFromProfile(trp)); err != nil {
return
}
}
return
}
func (dm *DataManager) RemoveTrendProfile(ctx *context.Context, tenant, id string) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
oldTrs, err := dm.GetTrendProfile(ctx, tenant, id, true, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaTrendProfiles)
if err != nil {
return err
}
if err = dataDB.RemTrendProfileDrv(ctx, tenant, id); err != nil {
return
}
if oldTrs == nil {
return utils.ErrNotFound
}
if itm := dm.cfg.DbCfg().Items[utils.MetaTrendProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
rpl.replicate(ctx,
utils.TrendProfilePrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache
utils.ReplicatorSv1RemoveTrendProfile,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return dm.RemoveTrend(ctx, tenant, id)
}
func (dm *DataManager) GetRankingProfile(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool, transactionID string) (rgp *utils.RankingProfile, err error) {
tntID := utils.ConcatenatedKey(tenant, id)
if cacheRead {
if x, ok := Cache.Get(utils.CacheRankingProfiles, tntID); ok {
if x == nil {
return nil, utils.ErrNotFound
}
return x.(*utils.RankingProfile), nil
}
}
if dm == nil {
err = utils.ErrNoDatabaseConn
return
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaRankingProfiles)
if err != nil {
return nil, err
}
rgp, err = dataDB.GetRankingProfileDrv(ctx, tenant, id)
if err != nil {
if itm := dm.cfg.DbCfg().Items[utils.MetaRankingProfiles]; err == utils.ErrNotFound && itm.Remote {
if err = dm.connMgr.Call(context.TODO(), dbCfg.RmtConns,
utils.ReplicatorSv1GetRankingProfile,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &rgp); err == nil {
err = dataDB.SetRankingProfileDrv(ctx, rgp)
}
}
if err != nil {
err = utils.CastRPCErr(err)
if err == utils.ErrNotFound && cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheRankingProfiles, tntID, nil, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return nil, err
}
}
if cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheRankingProfiles, tntID, rgp, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return
}
func (dm *DataManager) GetRankingProfileIDs(ctx *context.Context, tenants []string) (rns map[string][]string, err error) {
prfx := utils.RankingProfilePrefix
var keys []string
dataDB, _, err := dm.dbConns.GetConn(utils.MetaRankingProfiles)
if err != nil {
return nil, err
}
if len(tenants) == 0 {
keys, err = dataDB.GetKeysForPrefix(ctx, prfx)
if err != nil {
return
}
} else {
for _, tenant := range tenants {
var tntkeys []string
tntPrfx := prfx + tenant + utils.ConcatenatedKeySep
tntkeys, err = dataDB.GetKeysForPrefix(ctx, tntPrfx)
if err != nil {
return
}
keys = append(keys, tntkeys...)
}
}
// if len(keys) == 0 {
// return nil, utils.ErrNotFound
// }
rns = make(map[string][]string)
for _, key := range keys {
indx := strings.Index(key, utils.ConcatenatedKeySep)
tenant := key[len(utils.RankingProfilePrefix):indx]
id := key[indx+1:]
rns[tenant] = append(rns[tenant], id)
}
return
}
func (dm *DataManager) SetRankingProfile(ctx *context.Context, rnp *utils.RankingProfile) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
oldRnk, err := dm.GetRankingProfile(ctx, rnp.Tenant, rnp.ID, true, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaRankingProfiles)
if err != nil {
return err
}
if err = dataDB.SetRankingProfileDrv(ctx, rnp); err != nil {
return
}
if itm := dm.cfg.DbCfg().Items[utils.MetaRankingProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
err = rpl.replicate(ctx,
utils.RankingProfilePrefix, rnp.TenantID(),
utils.ReplicatorSv1SetRankingProfile,
&utils.RankingProfileWithAPIOpts{
RankingProfile: rnp,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
if oldRnk == nil || oldRnk.Sorting != rnp.Sorting ||
oldRnk.Schedule != rnp.Schedule {
if err = dm.SetRanking(ctx, utils.NewRankingFromProfile(rnp)); err != nil {
return
}
}
return
}
func (dm *DataManager) RemoveRankingProfile(ctx *context.Context, tenant, id string) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
oldSgs, err := dm.GetRankingProfile(ctx, tenant, id, false, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaRankingProfiles)
if err != nil {
return err
}
if err = dataDB.RemRankingProfileDrv(ctx, tenant, id); err != nil {
return
}
if oldSgs == nil {
return utils.ErrNotFound
}
if itm := dm.cfg.DbCfg().Items[utils.MetaRankingProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
rpl.replicate(ctx,
utils.RankingProfilePrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache
utils.ReplicatorSv1RemoveRankingProfile,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return dm.RemoveRanking(ctx, tenant, id)
}
func (dm *DataManager) GetRanking(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool, transactionID string) (rn *utils.Ranking, err error) {
tntID := utils.ConcatenatedKey(tenant, id)
if cacheRead {
if x, ok := Cache.Get(utils.CacheRankings, tntID); ok {
if x == nil {
return nil, utils.ErrNotFound
}
return x.(*utils.Ranking), nil
}
}
if dm == nil {
err = utils.ErrNoDatabaseConn
return
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaRankings)
if err != nil {
return nil, err
}
if rn, err = dataDB.GetRankingDrv(ctx, tenant, id); err != nil {
if err != utils.ErrNotFound { // database error
return
}
// ErrNotFound
if itm := dm.cfg.DbCfg().Items[utils.MetaRankings]; itm.Remote {
if err = dm.connMgr.Call(context.TODO(), dbCfg.RmtConns,
utils.ReplicatorSv1GetRanking, &utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &rn); err == nil {
err = dataDB.SetRankingDrv(ctx, rn)
}
}
if err != nil {
err = utils.CastRPCErr(err)
if err == utils.ErrNotFound && cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheRankings, tntID, nil, nil, cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return nil, err
}
}
if cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheRankings, tntID, rn, nil, cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return
}
// SetRanking stores Ranking in dataDB
func (dm *DataManager) SetRanking(ctx *context.Context, rn *utils.Ranking) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaRankings)
if err != nil {
return err
}
if err = dataDB.SetRankingDrv(ctx, rn); err != nil {
return
}
if itm := dm.cfg.DbCfg().Items[utils.MetaRankings]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
if err = rpl.replicate(ctx,
utils.RankingPrefix, rn.TenantID(), // this are used to get the host IDs from cache
utils.ReplicatorSv1SetRanking,
&utils.RankingWithAPIOpts{
Ranking: rn,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm); err != nil {
return
}
}
return
}
// RemoveRanking removes the stored Ranking
func (dm *DataManager) RemoveRanking(ctx *context.Context, tenant, id string) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaRankings)
if err != nil {
return err
}
if err = dataDB.RemoveRankingDrv(ctx, tenant, id); err != nil {
return
}
if itm := dm.cfg.DbCfg().Items[utils.MetaRankings]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
rpl.replicate(ctx,
utils.RankingPrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache
utils.ReplicatorSv1RemoveRanking,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) GetResource(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool,
transactionID string) (rs *utils.Resource, err error) {
tntID := utils.ConcatenatedKey(tenant, id)
if cacheRead {
if x, ok := Cache.Get(utils.CacheResources, tntID); ok {
if x == nil {
return nil, utils.ErrNotFound
}
return x.(*utils.Resource), nil
}
}
if dm == nil {
err = utils.ErrNoDatabaseConn
return
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaResources)
if err != nil {
return nil, err
}
rs, err = dataDB.GetResourceDrv(ctx, tenant, id)
if err != nil {
if itm := dm.cfg.DbCfg().Items[utils.MetaResources]; err == utils.ErrNotFound && itm.Remote {
if err = dm.connMgr.Call(ctx, dbCfg.RmtConns,
utils.ReplicatorSv1GetResource,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &rs); err == nil {
err = dataDB.SetResourceDrv(ctx, rs)
}
}
if err != nil {
err = utils.CastRPCErr(err)
if err == utils.ErrNotFound && cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheResources, tntID, nil, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return nil, err
}
}
if cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheResources, tntID, rs, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return
}
func (dm *DataManager) SetResource(ctx *context.Context, rs *utils.Resource) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaResources)
if err != nil {
return err
}
if err = dataDB.SetResourceDrv(ctx, rs); err != nil {
return
}
if itm := dm.cfg.DbCfg().Items[utils.MetaResources]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
err = rpl.replicate(ctx,
utils.ResourcesPrefix, rs.TenantID(), // this are used to get the host IDs from cache
utils.ReplicatorSv1SetResource,
&utils.ResourceWithAPIOpts{
Resource: rs,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) RemoveResource(ctx *context.Context, tenant, id string) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaResources)
if err != nil {
return err
}
if err = dataDB.RemoveResourceDrv(ctx, tenant, id); err != nil {
return
}
if itm := dm.cfg.DbCfg().Items[utils.MetaResources]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
rpl.replicate(ctx,
utils.ResourcesPrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache
utils.ReplicatorSv1RemoveResource,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) GetResourceProfile(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool,
transactionID string) (rp *utils.ResourceProfile, err error) {
tntID := utils.ConcatenatedKey(tenant, id)
if cacheRead {
if x, ok := Cache.Get(utils.CacheResourceProfiles, tntID); ok {
if x == nil {
return nil, utils.ErrNotFound
}
return x.(*utils.ResourceProfile), nil
}
}
if dm == nil {
err = utils.ErrNoDatabaseConn
return
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaResourceProfiles)
if err != nil {
return nil, err
}
rp, err = dataDB.GetResourceProfileDrv(ctx, tenant, id)
if err != nil {
if itm := dm.cfg.DbCfg().Items[utils.MetaResourceProfiles]; err == utils.ErrNotFound && itm.Remote {
if err = dm.connMgr.Call(ctx, dbCfg.RmtConns,
utils.ReplicatorSv1GetResourceProfile, &utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &rp); err == nil {
err = dataDB.SetResourceProfileDrv(ctx, rp)
}
}
if err != nil {
err = utils.CastRPCErr(err)
if err == utils.ErrNotFound && cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheResourceProfiles, tntID, nil, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return nil, err
}
}
if cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheResourceProfiles, tntID, rp, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return
}
func (dm *DataManager) SetResourceProfile(ctx *context.Context, rp *utils.ResourceProfile, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
if withIndex {
if err := dm.checkFilters(ctx, rp.Tenant, rp.FilterIDs); err != nil {
// if we get a broken filter do not set the profile
return fmt.Errorf("%+s for item with ID: %+v",
err, rp.TenantID())
}
}
oldRes, err := dm.GetResourceProfile(ctx, rp.Tenant, rp.ID, true, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaResourceProfiles)
if err != nil {
return err
}
if err = dataDB.SetResourceProfileDrv(ctx, rp); err != nil {
return err
}
if withIndex {
var oldFiltersIDs *[]string
if oldRes != nil {
oldFiltersIDs = &oldRes.FilterIDs
}
if err := updatedIndexes(ctx, dm, utils.CacheResourceFilterIndexes, rp.Tenant,
utils.EmptyString, rp.ID, oldFiltersIDs, rp.FilterIDs, false); err != nil {
return err
}
Cache.Clear([]string{utils.CacheEventResources})
}
if itm := dm.cfg.DbCfg().Items[utils.MetaResourceProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
if err = rpl.replicate(ctx,
utils.ResourceProfilesPrefix, rp.TenantID(), // this are used to get the host IDs from cache
utils.ReplicatorSv1SetResourceProfile,
&utils.ResourceProfileWithAPIOpts{
ResourceProfile: rp,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm); err != nil {
return
}
}
if oldRes == nil || // create the resource if it didn't exist before
oldRes.UsageTTL != rp.UsageTTL ||
oldRes.Limit != rp.Limit ||
oldRes.Stored != rp.Stored && oldRes.Stored { // reset the resource if the profile changed this fields
err = dm.SetResource(ctx, &utils.Resource{
Tenant: rp.Tenant,
ID: rp.ID,
Usages: make(map[string]*utils.ResourceUsage),
})
} else if _, errRs := dm.GetResource(ctx, rp.Tenant, rp.ID, // do not try to get the resource if the configuration changed
true, false, utils.NonTransactional); errRs == utils.ErrNotFound { // the resource does not exist
err = dm.SetResource(ctx, &utils.Resource{
Tenant: rp.Tenant,
ID: rp.ID,
Usages: make(map[string]*utils.ResourceUsage),
})
}
return
}
func (dm *DataManager) RemoveResourceProfile(ctx *context.Context, tenant, id string, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
oldRes, err := dm.GetResourceProfile(ctx, tenant, id, true, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaResourceProfiles)
if err != nil {
return err
}
if err = dataDB.RemoveResourceProfileDrv(ctx, tenant, id); err != nil {
return
}
if oldRes == nil {
return utils.ErrNotFound
}
if withIndex {
if err = removeIndexFiltersItem(ctx, dm, utils.CacheResourceFilterIndexes, tenant, id, oldRes.FilterIDs); err != nil {
return
}
if err = removeItemFromFilterIndex(ctx, dm, utils.CacheResourceFilterIndexes,
tenant, utils.EmptyString, id, oldRes.FilterIDs); err != nil {
return
}
}
if itm := dm.cfg.DbCfg().Items[utils.MetaResourceProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
rpl.replicate(ctx,
utils.ResourceProfilesPrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache
utils.ReplicatorSv1RemoveResourceProfile,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return dm.RemoveResource(ctx, tenant, id)
}
func (dm *DataManager) GetIPAllocations(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool,
transactionID string, prfl *utils.IPProfile) (ip *utils.IPAllocations, err error) {
tntID := utils.ConcatenatedKey(tenant, id)
if cacheRead {
if x, ok := Cache.Get(utils.CacheIPAllocations, tntID); ok {
if x == nil {
return nil, utils.ErrNotFound
}
ip = x.(*utils.IPAllocations)
if err = ip.ComputeUnexported(prfl); err != nil {
return nil, err
}
return ip, nil
}
}
if dm == nil {
err = utils.ErrNoDatabaseConn
return
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaIPAllocations)
if err != nil {
return nil, err
}
ip, err = dataDB.GetIPAllocationsDrv(ctx, tenant, id)
if err != nil {
if itm := dm.cfg.DbCfg().Items[utils.MetaIPAllocations]; err == utils.ErrNotFound && itm.Remote {
if err = dm.connMgr.Call(ctx, dbCfg.RmtConns,
utils.ReplicatorSv1GetIPAllocations,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &ip); err == nil {
err = dataDB.SetIPAllocationsDrv(ctx, ip)
}
}
if err != nil {
err = utils.CastRPCErr(err)
if err == utils.ErrNotFound && cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheIPAllocations, tntID, nil, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return nil, err
}
}
if err = ip.ComputeUnexported(prfl); err != nil {
return nil, err
}
if cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheIPAllocations, tntID, ip, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return
}
func (dm *DataManager) SetIPAllocations(ctx *context.Context, ip *utils.IPAllocations) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaIPAllocations)
if err != nil {
return err
}
if err = dataDB.SetIPAllocationsDrv(ctx, ip); err != nil {
return
}
if itm := dm.cfg.DbCfg().Items[utils.MetaIPAllocations]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
err = rpl.replicate(ctx,
utils.IPAllocationsPrefix, ip.TenantID(), // this are used to get the host IDs from cache
utils.ReplicatorSv1SetIPAllocations,
&utils.IPAllocationsWithAPIOpts{
IPAllocations: ip,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) RemoveIPAllocations(ctx *context.Context, tenant, id string) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaIPAllocations)
if err != nil {
return err
}
if err = dataDB.RemoveIPAllocationsDrv(ctx, tenant, id); err != nil {
return
}
if itm := dm.cfg.DbCfg().Items[utils.MetaIPAllocations]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
rpl.replicate(ctx,
utils.IPAllocationsPrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache
utils.ReplicatorSv1RemoveIPAllocations,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) GetIPProfile(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool,
transactionID string) (ipp *utils.IPProfile, err error) {
tntID := utils.ConcatenatedKey(tenant, id)
if cacheRead {
if x, ok := Cache.Get(utils.CacheIPProfiles, tntID); ok {
if x == nil {
return nil, utils.ErrNotFound
}
return x.(*utils.IPProfile), nil
}
}
if dm == nil {
err = utils.ErrNoDatabaseConn
return
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaIPProfiles)
if err != nil {
return nil, err
}
ipp, err = dataDB.GetIPProfileDrv(ctx, tenant, id)
if err != nil {
if itm := dm.cfg.DbCfg().Items[utils.MetaIPProfiles]; err == utils.ErrNotFound && itm.Remote {
if err = dm.connMgr.Call(ctx, dbCfg.RmtConns,
utils.ReplicatorSv1GetIPProfile, &utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &ipp); err == nil {
err = dataDB.SetIPProfileDrv(ctx, ipp)
}
}
if err != nil {
err = utils.CastRPCErr(err)
if err == utils.ErrNotFound && cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheIPProfiles, tntID, nil, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return nil, err
}
}
if cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheIPProfiles, tntID, ipp, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return
}
func (dm *DataManager) SetIPProfile(ctx *context.Context, ipp *utils.IPProfile, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
if withIndex {
if err := dm.checkFilters(ctx, ipp.Tenant, ipp.FilterIDs); err != nil {
// if we get a broken filter do not set the profile
return fmt.Errorf("%+s for item with ID: %+v",
err, ipp.TenantID())
}
}
oldIPP, err := dm.GetIPProfile(ctx, ipp.Tenant, ipp.ID, true, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaIPProfiles)
if err != nil {
return err
}
if err = dataDB.SetIPProfileDrv(ctx, ipp); err != nil {
return err
}
if withIndex {
var oldFiltersIDs *[]string
if oldIPP != nil {
oldFiltersIDs = &oldIPP.FilterIDs
}
if err := updatedIndexes(ctx, dm, utils.CacheIPFilterIndexes, ipp.Tenant,
utils.EmptyString, ipp.ID, oldFiltersIDs, ipp.FilterIDs, false); err != nil {
return err
}
Cache.Clear([]string{utils.CacheEventIPs})
}
if itm := dm.cfg.DbCfg().Items[utils.MetaIPProfiles]; itm.Replicate {
var rpl *replicator
rpl = dm.dbConns.GetReplicator(itm.DBConn)
if err = rpl.replicate(ctx,
utils.IPProfilesPrefix, ipp.TenantID(), // this are used to get the host IDs from cache
utils.ReplicatorSv1SetIPProfile,
&utils.IPProfileWithAPIOpts{
IPProfile: ipp,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm); err != nil {
return
}
}
if oldIPP == nil || // create the resource if it didn't exist before
oldIPP.TTL != ipp.TTL ||
oldIPP.Stored != ipp.Stored && oldIPP.Stored { // reset the resource if the profile changed this fields
err = dm.SetIPAllocations(ctx, &utils.IPAllocations{
Tenant: ipp.Tenant,
ID: ipp.ID,
Allocations: make(map[string]*utils.PoolAllocation),
})
} else if _, errGet := dm.GetIPAllocations(ctx, ipp.Tenant, ipp.ID, // do not try to get the resource if the configuration changed
true, false, utils.NonTransactional, nil); errGet == utils.ErrNotFound { // the resource does not exist
err = dm.SetIPAllocations(ctx, &utils.IPAllocations{
Tenant: ipp.Tenant,
ID: ipp.ID,
Allocations: make(map[string]*utils.PoolAllocation),
})
}
return
}
func (dm *DataManager) RemoveIPProfile(ctx *context.Context, tenant, id string, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
oldIPP, err := dm.GetIPProfile(ctx, tenant, id, true, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaIPProfiles)
if err != nil {
return err
}
if err = dataDB.RemoveIPProfileDrv(ctx, tenant, id); err != nil {
return
}
if oldIPP == nil {
return utils.ErrNotFound
}
if withIndex {
if err = removeIndexFiltersItem(ctx, dm, utils.CacheIPFilterIndexes, tenant, id, oldIPP.FilterIDs); err != nil {
return
}
if err = removeItemFromFilterIndex(ctx, dm, utils.CacheIPFilterIndexes,
tenant, utils.EmptyString, id, oldIPP.FilterIDs); err != nil {
return
}
}
if itm := dm.cfg.DbCfg().Items[utils.MetaIPProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
rpl.replicate(ctx,
utils.IPProfilesPrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache
utils.ReplicatorSv1RemoveIPProfile,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return dm.RemoveIPAllocations(ctx, tenant, id)
}
func (dm *DataManager) HasData(category, subject, tenant string) (has bool, err error) {
if dm == nil {
err = utils.ErrNoDatabaseConn
return
}
for _, dataDB := range dm.DataDB() {
if has, err = dataDB.HasDataDrv(context.TODO(), category, subject, tenant); has {
return
}
}
return
}
func (dm *DataManager) GetRouteProfile(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool,
transactionID string) (rpp *utils.RouteProfile, err error) {
tntID := utils.ConcatenatedKey(tenant, id)
if cacheRead {
if x, ok := Cache.Get(utils.CacheRouteProfiles, tntID); ok {
if x == nil {
return nil, utils.ErrNotFound
}
return x.(*utils.RouteProfile), nil
}
}
if dm == nil {
err = utils.ErrNoDatabaseConn
return
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaRouteProfiles)
if err != nil {
return nil, err
}
rpp, err = dataDB.GetRouteProfileDrv(ctx, tenant, id)
if err != nil {
if itm := dm.cfg.DbCfg().Items[utils.MetaRouteProfiles]; err == utils.ErrNotFound && itm.Remote {
if err = dm.connMgr.Call(ctx, dbCfg.RmtConns, utils.ReplicatorSv1GetRouteProfile,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &rpp); err == nil {
err = dataDB.SetRouteProfileDrv(ctx, rpp)
}
}
if err != nil {
err = utils.CastRPCErr(err)
if err == utils.ErrNotFound && cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheRouteProfiles, tntID, nil, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return nil, err
}
}
// populate cache will compute specific config parameters
if err = rpp.Compile(); err != nil {
return nil, err
}
if cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheRouteProfiles, tntID, rpp, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return
}
func (dm *DataManager) SetRouteProfile(ctx *context.Context, rpp *utils.RouteProfile, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
if withIndex {
if err := dm.checkFilters(ctx, rpp.Tenant, rpp.FilterIDs); err != nil {
// if we get a broken filter do not set the profile
return fmt.Errorf("%+s for item with ID: %+v",
err, rpp.TenantID())
}
}
oldRpp, err := dm.GetRouteProfile(ctx, rpp.Tenant, rpp.ID, true, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaRouteProfiles)
if err != nil {
return err
}
if err = dataDB.SetRouteProfileDrv(ctx, rpp); err != nil {
return err
}
if withIndex {
var oldFiltersIDs *[]string
if oldRpp != nil {
oldFiltersIDs = &oldRpp.FilterIDs
}
if err := updatedIndexes(ctx, dm, utils.CacheRouteFilterIndexes, rpp.Tenant,
utils.EmptyString, rpp.ID, oldFiltersIDs, rpp.FilterIDs, false); err != nil {
return err
}
}
if itm := dm.cfg.DbCfg().Items[utils.MetaRouteProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
err = rpl.replicate(ctx,
utils.RouteProfilePrefix, rpp.TenantID(), // this are used to get the host IDs from cache
utils.ReplicatorSv1SetRouteProfile,
&utils.RouteProfileWithAPIOpts{
RouteProfile: rpp,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) RemoveRouteProfile(ctx *context.Context, tenant, id string, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
oldRpp, err := dm.GetRouteProfile(ctx, tenant, id, true, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaRouteProfiles)
if err != nil {
return err
}
if err = dataDB.RemoveRouteProfileDrv(ctx, tenant, id); err != nil {
return
}
if oldRpp == nil {
return utils.ErrNotFound
}
if withIndex {
if err = removeIndexFiltersItem(ctx, dm, utils.CacheRouteFilterIndexes, tenant, id, oldRpp.FilterIDs); err != nil {
return
}
if err = removeItemFromFilterIndex(ctx, dm, utils.CacheRouteFilterIndexes,
tenant, utils.EmptyString, id, oldRpp.FilterIDs); err != nil {
return
}
}
if itm := dm.cfg.DbCfg().Items[utils.MetaRouteProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
rpl.replicate(ctx,
utils.RouteProfilePrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache
utils.ReplicatorSv1RemoveRouteProfile,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
// GetAttributeProfile returns the AttributeProfile with the given id
func (dm *DataManager) GetAttributeProfile(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool,
transactionID string) (attrPrfl *utils.AttributeProfile, err error) {
tntID := utils.ConcatenatedKey(tenant, id)
if cacheRead {
if x, ok := Cache.Get(utils.CacheAttributeProfiles, tntID); ok {
if x == nil {
return nil, utils.ErrNotFound
}
return x.(*utils.AttributeProfile), nil
}
}
if strings.HasPrefix(id, utils.Meta) {
attrPrfl, err = utils.NewAttributeFromInline(tenant, id)
return // do not set inline attributes in cache it breaks the interanal db matching
} else if dm == nil {
err = utils.ErrNoDatabaseConn
return
} else {
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaAttributeProfiles)
if err != nil {
return nil, err
}
if attrPrfl, err = dataDB.GetAttributeProfileDrv(ctx, tenant, id); err != nil {
if itm := dm.cfg.DbCfg().Items[utils.MetaAttributeProfiles]; err == utils.ErrNotFound && itm.Remote {
if err = dm.connMgr.Call(ctx, dbCfg.RmtConns,
utils.ReplicatorSv1GetAttributeProfile,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &attrPrfl); err == nil {
err = dataDB.SetAttributeProfileDrv(ctx, attrPrfl)
}
}
if err != nil {
err = utils.CastRPCErr(err)
if err == utils.ErrNotFound && cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheAttributeProfiles, tntID, nil, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return nil, err
}
}
if err = attrPrfl.Compile(); err != nil {
return nil, err
}
}
if cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheAttributeProfiles, tntID, attrPrfl, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return
}
func (dm *DataManager) SetAttributeProfile(ctx *context.Context, ap *utils.AttributeProfile, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
if withIndex {
if err := dm.checkFilters(ctx, ap.Tenant, ap.FilterIDs); err != nil {
// if we get a broken filter do not set the profile
return fmt.Errorf("%+s for item with ID: %+v",
err, ap.TenantID())
}
}
oldAP, err := dm.GetAttributeProfile(ctx, ap.Tenant, ap.ID, true, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return err
}
for _, attribute := range ap.Attributes {
if attribute.Type == utils.MetaPassword {
password := attribute.Value.GetRule()
if password, err = utils.ComputeHash(password); err != nil {
return
}
if attribute.Value, err = utils.NewRSRParsers(password, utils.RSRSep); err != nil {
return
}
attribute.Type = utils.MetaConstant
}
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaAttributeProfiles)
if err != nil {
return err
}
if err = dataDB.SetAttributeProfileDrv(ctx, ap); err != nil {
return err
}
if withIndex {
var oldFiltersIDs *[]string
if oldAP != nil {
oldFiltersIDs = &oldAP.FilterIDs
}
if err := updatedIndexes(ctx, dm, utils.CacheAttributeFilterIndexes, ap.Tenant,
utils.EmptyString, ap.ID, oldFiltersIDs, ap.FilterIDs, false); err != nil {
return err
}
}
if itm := dm.cfg.DbCfg().Items[utils.MetaAttributeProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
err = rpl.replicate(ctx,
utils.AttributeProfilePrefix, ap.TenantID(), // this are used to get the host IDs from cache
utils.ReplicatorSv1SetAttributeProfile,
&utils.AttributeProfileWithAPIOpts{
AttributeProfile: ap,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) RemoveAttributeProfile(ctx *context.Context, tenant, id string, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
oldAttr, err := dm.GetAttributeProfile(ctx, tenant, id, true, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaAttributeProfiles)
if err != nil {
return err
}
if err = dataDB.RemoveAttributeProfileDrv(ctx, tenant, id); err != nil {
return
}
if oldAttr == nil {
return utils.ErrNotFound
}
if withIndex {
if err = removeIndexFiltersItem(ctx, dm, utils.CacheAttributeFilterIndexes, tenant, id, oldAttr.FilterIDs); err != nil {
return
}
if err = removeItemFromFilterIndex(ctx, dm, utils.CacheAttributeFilterIndexes,
tenant, utils.EmptyString, id, oldAttr.FilterIDs); err != nil {
return
}
}
if itm := dm.cfg.DbCfg().Items[utils.MetaAttributeProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
rpl.replicate(ctx,
utils.AttributeProfilePrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache
utils.ReplicatorSv1RemoveAttributeProfile,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) GetChargerProfile(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool,
transactionID string) (cpp *utils.ChargerProfile, err error) {
tntID := utils.ConcatenatedKey(tenant, id)
if cacheRead {
if x, ok := Cache.Get(utils.CacheChargerProfiles, tntID); ok {
if x == nil {
return nil, utils.ErrNotFound
}
return x.(*utils.ChargerProfile), nil
}
}
if dm == nil {
err = utils.ErrNoDatabaseConn
return
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaChargerProfiles)
if err != nil {
return nil, err
}
cpp, err = dataDB.GetChargerProfileDrv(ctx, tenant, id)
if err != nil {
if itm := dm.cfg.DbCfg().Items[utils.MetaChargerProfiles]; err == utils.ErrNotFound && itm.Remote {
if err = dm.connMgr.Call(ctx, dbCfg.RmtConns,
utils.ReplicatorSv1GetChargerProfile,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &cpp); err == nil {
err = dataDB.SetChargerProfileDrv(ctx, cpp)
}
}
if err != nil {
err = utils.CastRPCErr(err)
if err == utils.ErrNotFound && cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheChargerProfiles, tntID, nil, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return nil, err
}
}
if cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheChargerProfiles, tntID, cpp, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return
}
func (dm *DataManager) SetChargerProfile(ctx *context.Context, cpp *utils.ChargerProfile, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
if withIndex {
if err := dm.checkFilters(ctx, cpp.Tenant, cpp.FilterIDs); err != nil {
// if we get a broken filter do not set the profile
return fmt.Errorf("%+s for item with ID: %+v",
err, cpp.TenantID())
}
}
oldCpp, err := dm.GetChargerProfile(ctx, cpp.Tenant, cpp.ID, true, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaChargerProfiles)
if err != nil {
return err
}
if err = dataDB.SetChargerProfileDrv(ctx, cpp); err != nil {
return err
}
if withIndex {
var oldFiltersIDs *[]string
if oldCpp != nil {
oldFiltersIDs = &oldCpp.FilterIDs
}
if err := updatedIndexes(ctx, dm, utils.CacheChargerFilterIndexes, cpp.Tenant,
utils.EmptyString, cpp.ID, oldFiltersIDs, cpp.FilterIDs, false); err != nil {
return err
}
}
if itm := dm.cfg.DbCfg().Items[utils.MetaChargerProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
err = rpl.replicate(ctx,
utils.ChargerProfilePrefix, cpp.TenantID(), // this are used to get the host IDs from cache
utils.ReplicatorSv1SetChargerProfile,
&utils.ChargerProfileWithAPIOpts{
ChargerProfile: cpp,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) RemoveChargerProfile(ctx *context.Context, tenant, id string, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
oldCpp, err := dm.GetChargerProfile(ctx, tenant, id, true, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaChargerProfiles)
if err != nil {
return err
}
if err = dataDB.RemoveChargerProfileDrv(ctx, tenant, id); err != nil {
return
}
if oldCpp == nil {
return utils.ErrNotFound
}
if withIndex {
if err = removeIndexFiltersItem(ctx, dm, utils.CacheChargerFilterIndexes, tenant, id, oldCpp.FilterIDs); err != nil {
return
}
if err = removeItemFromFilterIndex(ctx, dm, utils.CacheChargerFilterIndexes,
tenant, utils.EmptyString, id, oldCpp.FilterIDs); err != nil {
return
}
}
if itm := dm.cfg.DbCfg().Items[utils.MetaChargerProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
rpl.replicate(ctx,
utils.ChargerProfilePrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache
utils.ReplicatorSv1RemoveChargerProfile,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) GetItemLoadIDs(ctx *context.Context, itemIDPrefix string, cacheWrite bool) (loadIDs map[string]int64, err error) {
if dm == nil {
err = utils.ErrNoDatabaseConn
return
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaLoadIDs)
if err != nil {
return nil, err
}
loadIDs, err = dataDB.GetItemLoadIDsDrv(ctx, itemIDPrefix)
if err != nil {
if itm := dm.cfg.DbCfg().Items[utils.MetaLoadIDs]; err == utils.ErrNotFound && itm.Remote {
if err = dm.connMgr.Call(ctx, dbCfg.RmtConns,
utils.ReplicatorSv1GetItemLoadIDs,
&utils.StringWithAPIOpts{
Arg: itemIDPrefix,
Tenant: dm.cfg.GeneralCfg().DefaultTenant,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &loadIDs); err == nil {
err = dataDB.SetLoadIDsDrv(ctx, loadIDs)
}
}
if err != nil {
err = utils.CastRPCErr(err)
if err == utils.ErrNotFound && cacheWrite {
for key := range loadIDs {
if errCh := Cache.Set(ctx, utils.CacheLoadIDs, key, nil, nil,
cacheCommit(utils.NonTransactional), utils.NonTransactional); errCh != nil {
return nil, errCh
}
}
}
return nil, err
}
}
if cacheWrite {
for key, val := range loadIDs {
if errCh := Cache.Set(ctx, utils.CacheLoadIDs, key, val, nil,
cacheCommit(utils.NonTransactional), utils.NonTransactional); errCh != nil {
return nil, errCh
}
}
}
return
}
// SetLoadIDs sets the loadIDs in the DB
func (dm *DataManager) SetLoadIDs(ctx *context.Context, loadIDs map[string]int64) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaLoadIDs)
if err != nil {
return err
}
if err = dataDB.SetLoadIDsDrv(ctx, loadIDs); err != nil {
return
}
if itm := dm.cfg.DbCfg().Items[utils.MetaLoadIDs]; itm.Replicate {
objIDs := make([]string, 0, len(loadIDs))
for k := range loadIDs {
objIDs = append(objIDs, k)
}
err = replicateMultipleIDs(ctx, dm.connMgr, dbCfg.RplConns,
dbCfg.RplFiltered,
utils.LoadIDPrefix, objIDs, // this are used to get the host IDs from cache
utils.ReplicatorSv1SetLoadIDs,
&utils.LoadIDsWithAPIOpts{
LoadIDs: loadIDs,
Tenant: dm.cfg.GeneralCfg().DefaultTenant,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)})
}
return
}
func (dm *DataManager) GetRateProfile(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool,
transactionID string) (rpp *utils.RateProfile, err error) {
tntID := utils.ConcatenatedKey(tenant, id)
if cacheRead {
if x, ok := Cache.Get(utils.CacheRateProfiles, tntID); ok {
if x == nil {
return nil, utils.ErrNotFound
}
return x.(*utils.RateProfile), nil
}
}
if dm == nil {
err = utils.ErrNoDatabaseConn
return
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaRateProfiles)
if err != nil {
return nil, err
}
rpp, err = dataDB.GetRateProfileDrv(ctx, tenant, id)
if err != nil {
if itm := dm.cfg.DbCfg().Items[utils.MetaRateProfiles]; err == utils.ErrNotFound && itm.Remote {
if err = dm.connMgr.Call(ctx, dbCfg.RmtConns,
utils.ReplicatorSv1GetRateProfile,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &rpp); err == nil {
rpp.Sort()
err = dataDB.SetRateProfileDrv(ctx, rpp, false)
}
}
if err != nil {
err = utils.CastRPCErr(err)
if err == utils.ErrNotFound && cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheRateProfiles, tntID, nil, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return nil, err
}
}
if err = rpp.Compile(); err != nil {
return nil, err
}
if cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheRateProfiles, tntID, rpp, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return
}
func (dm *DataManager) GetRateProfileRates(ctx *context.Context, args *utils.ArgsSubItemIDs, needIDs bool) (rateIDs []string, rates []*utils.Rate, err error) {
if dm == nil {
return nil, nil, utils.ErrNoDatabaseConn
}
dataDB, _, err := dm.dbConns.GetConn(utils.MetaRateProfiles)
if err != nil {
return
}
return dataDB.GetRateProfileRatesDrv(ctx, args.Tenant, args.ProfileID, args.ItemsPrefix, needIDs)
}
func (dm *DataManager) SetRateProfile(ctx *context.Context, rpp *utils.RateProfile, optOverwrite, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
// check if the filters are valid, this can be inline or filter object
if len(rpp.FilterIDs) != 0 {
if err := dm.checkFilters(ctx, rpp.Tenant, rpp.FilterIDs); err != nil {
// if we get a broken filter do not set the profile
return fmt.Errorf("%+s for item with ID: %+v",
err, rpp.TenantID())
}
for _, rate := range rpp.Rates {
if err := dm.checkFilters(ctx, rpp.Tenant, rate.FilterIDs); err != nil {
// if we get a broken filter do not update the rates
return fmt.Errorf("%+s for item with ID: %+v",
err, rate.ID)
}
}
}
rpp.Sort()
// get the old RateProfile in case of updating fields
oldRpp, err := dm.GetRateProfile(ctx, rpp.Tenant, rpp.ID, true, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return err
}
if withIndex {
// remove indexes for old rates if the rates does not exist in new rate profile
var oldRpFltrs *[]string
if oldRpp != nil {
oldRpFltrs = &oldRpp.FilterIDs
}
// create index for our profile
if err := updatedIndexes(ctx, dm, utils.CacheRateProfilesFilterIndexes, rpp.Tenant,
utils.EmptyString, rpp.ID, oldRpFltrs, rpp.FilterIDs, false); err != nil {
return err
}
// create index for each rate
for key, rate := range rpp.Rates {
var oldRateFiltersIDs *[]string
if oldRpp != nil {
if oldRate, has := oldRpp.Rates[key]; has {
oldRateFiltersIDs = &oldRate.FilterIDs
}
}
// when we create the indexes for rates we use RateProfile ID as context
if err := updatedIndexes(ctx, dm, utils.CacheRateFilterIndexes, rpp.Tenant,
rpp.ID, key, oldRateFiltersIDs, rate.FilterIDs, true); err != nil {
return err
}
}
}
// if not overwriting, we will add the rates in case the profile is already in database, also the fields of the profile are changed too in case of the same tenantID
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaRateProfiles)
if err != nil {
return err
}
if err = dataDB.SetRateProfileDrv(ctx, rpp, optOverwrite); err != nil {
return err
}
if itm := dm.cfg.DbCfg().Items[utils.MetaRateProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
err = rpl.replicate(ctx,
utils.RateProfilePrefix, rpp.TenantID(), // this are used to get the host IDs from cache
utils.ReplicatorSv1SetRateProfile,
&utils.RateProfileWithAPIOpts{
RateProfile: rpp,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) RemoveRateProfile(ctx *context.Context, tenant, id string, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
var oldRpp *utils.RateProfile
oldRpp, err = dm.GetRateProfile(ctx, tenant, id, true, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaRateProfiles)
if err != nil {
return err
}
if err = dataDB.RemoveRateProfileDrv(ctx, tenant, id, nil); err != nil {
return
}
if oldRpp == nil {
return utils.ErrNotFound
}
if withIndex {
for key, rate := range oldRpp.Rates {
if err = removeIndexFiltersItem(ctx, dm, utils.CacheRateFilterIndexes, tenant, utils.ConcatenatedKey(key, oldRpp.ID), rate.FilterIDs); err != nil {
return
}
if err = removeItemFromFilterIndex(ctx, dm, utils.CacheRateFilterIndexes,
oldRpp.Tenant, oldRpp.ID, key, rate.FilterIDs); err != nil {
return
}
}
if err = removeIndexFiltersItem(ctx, dm, utils.CacheRateProfilesFilterIndexes, tenant, id, oldRpp.FilterIDs); err != nil {
return
}
if err = removeItemFromFilterIndex(ctx, dm, utils.CacheRateProfilesFilterIndexes,
tenant, utils.EmptyString, id, oldRpp.FilterIDs); err != nil {
return
}
}
if itm := dm.cfg.DbCfg().Items[utils.MetaRateProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
rpl.replicate(ctx,
utils.RateProfilePrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache
utils.ReplicatorSv1RemoveRateProfile,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) RemoveRateProfileRates(ctx *context.Context, tenant, id string, rateIDs *[]string, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
oldRpp, err := dm.GetRateProfile(ctx, tenant, id, true, false, utils.NonTransactional)
if err != nil {
return err
}
if rateIDs == nil {
if withIndex {
for key, rate := range oldRpp.Rates {
if err = removeItemFromFilterIndex(ctx, dm, utils.CacheRateFilterIndexes,
tenant, id, key, rate.FilterIDs); err != nil {
return
}
}
}
oldRpp.Rates = map[string]*utils.Rate{}
} else {
for _, rateID := range *rateIDs {
if _, has := oldRpp.Rates[rateID]; !has {
continue
}
if withIndex {
for key, rate := range oldRpp.Rates {
if err = removeIndexFiltersItem(ctx, dm, utils.CacheRateFilterIndexes,
tenant, utils.ConcatenatedKey(key, oldRpp.ID), rate.FilterIDs); err != nil {
return
}
if err = removeItemFromFilterIndex(ctx, dm, utils.CacheRateFilterIndexes,
tenant, id, rateID, rate.FilterIDs); err != nil {
return
}
}
}
delete(oldRpp.Rates, rateID)
}
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaRateProfiles)
if err != nil {
return err
}
if err = dataDB.RemoveRateProfileDrv(ctx, tenant, id, rateIDs); err != nil {
return
}
if itm := dm.cfg.DbCfg().Items[utils.MetaRateProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
err = rpl.replicate(ctx,
utils.RateProfilePrefix, oldRpp.TenantID(), // this are used to get the host IDs from cache
utils.ReplicatorSv1SetRateProfile,
&utils.RateProfileWithAPIOpts{
RateProfile: oldRpp,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) GetActionProfile(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool,
transactionID string) (ap *utils.ActionProfile, err error) {
tntID := utils.ConcatenatedKey(tenant, id)
if cacheRead {
if x, ok := Cache.Get(utils.CacheActionProfiles, tntID); ok {
if x == nil {
return nil, utils.ErrNotFound
}
return x.(*utils.ActionProfile), nil
}
}
if dm == nil {
err = utils.ErrNoDatabaseConn
return
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaActionProfiles)
if err != nil {
return nil, err
}
ap, err = dataDB.GetActionProfileDrv(ctx, tenant, id)
if err != nil {
if itm := dm.cfg.DbCfg().Items[utils.MetaActionProfiles]; err == utils.ErrNotFound && itm.Remote {
if err = dm.connMgr.Call(ctx, dbCfg.RmtConns,
utils.ReplicatorSv1GetActionProfile,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &ap); err == nil {
err = dataDB.SetActionProfileDrv(ctx, ap)
}
}
if err != nil {
err = utils.CastRPCErr(err)
if err == utils.ErrNotFound && cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheActionProfiles, tntID, nil, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return nil, err
}
}
if cacheWrite {
if errCh := Cache.Set(ctx, utils.CacheActionProfiles, tntID, ap, nil,
cacheCommit(transactionID), transactionID); errCh != nil {
return nil, errCh
}
}
return
}
func (dm *DataManager) SetActionProfile(ctx *context.Context, ap *utils.ActionProfile, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
if withIndex {
if err := dm.checkFilters(ctx, ap.Tenant, ap.FilterIDs); err != nil {
// if we get a broken filter do not set the profile
return fmt.Errorf("%+s for item with ID: %+v",
err, ap.TenantID())
}
}
oldRpp, err := dm.GetActionProfile(ctx, ap.Tenant, ap.ID, true, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaActionProfiles)
if err != nil {
return err
}
if err = dataDB.SetActionProfileDrv(ctx, ap); err != nil {
return err
}
if withIndex {
var oldFiltersIDs *[]string
if oldRpp != nil {
oldFiltersIDs = &oldRpp.FilterIDs
}
if err := updatedIndexes(ctx, dm, utils.CacheActionProfilesFilterIndexes, ap.Tenant,
utils.EmptyString, ap.ID, oldFiltersIDs, ap.FilterIDs, false); err != nil {
return err
}
}
if itm := dm.cfg.DbCfg().Items[utils.MetaActionProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
err = rpl.replicate(ctx,
utils.ActionProfilePrefix, ap.TenantID(), // this are used to get the host IDs from cache
utils.ReplicatorSv1SetActionProfile,
&utils.ActionProfileWithAPIOpts{
ActionProfile: ap,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) RemoveActionProfile(ctx *context.Context, tenant, id string, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
oldAct, err := dm.GetActionProfile(ctx, tenant, id, true, false, utils.NonTransactional)
if err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaActionProfiles)
if err != nil {
return err
}
if err = dataDB.RemoveActionProfileDrv(ctx, tenant, id); err != nil {
return
}
if oldAct == nil {
return utils.ErrNotFound
}
if withIndex {
if err = removeIndexFiltersItem(ctx, dm, utils.CacheActionProfilesFilterIndexes, tenant, id, oldAct.FilterIDs); err != nil {
return
}
if err = removeItemFromFilterIndex(ctx, dm, utils.CacheActionProfilesFilterIndexes,
tenant, utils.EmptyString, id, oldAct.FilterIDs); err != nil {
return
}
}
if itm := dm.cfg.DbCfg().Items[utils.MetaActionProfiles]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
rpl.replicate(ctx,
utils.ActionProfilePrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache
utils.ReplicatorSv1RemoveActionProfile,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
// ReconnectAll reconnects all dataDBs when the config was changed. d implements all the new dataDB conns taken from the new config
func (dm *DataManager) ReconnectAll(cfg *config.CGRConfig) (err error) {
dm.dbConns.Lock()
defer dm.dbConns.Unlock()
d := make(map[string]DataDBDriver)
r := make(map[string]*replicator)
for dbKey, dbConnCfg := range cfg.DbCfg().DBConns {
if dbConnCfg.Type == utils.MetaInternal {
// close internalDB before creating a new one
if datadb, ok := dm.DBConns().dataDBs[dbKey]; !ok {
return fmt.Errorf("couldnt find DataDB with dbConnID: <%s>", dbKey)
} else {
datadb.Close()
}
}
d[dbKey], err = NewDataDBConn(dbConnCfg.Type,
dbConnCfg.Host, dbConnCfg.Port, dbConnCfg.Name, dbConnCfg.User,
dbConnCfg.Password, cfg.GeneralCfg().DBDataEncoding,
dbConnCfg.StringIndexedFields, dbConnCfg.PrefixIndexedFields,
dbConnCfg.Opts, cfg.DbCfg().Items)
r[dbKey] = newReplicator(dbConnCfg, dm.connMgr)
if err != nil {
return
}
for dataDBKey := range dm.dbConns.dataDBs { // close old DataManager conns
if dbConnCfg.Type != utils.MetaInternal { // old internalDB is already closed
dm.dbConns.dataDBs[dataDBKey].Close() // close all connections
}
delete(dm.dbConns.dataDBs, dataDBKey) // remove old keys so they dont get left over
}
for repl := range dm.dbConns.replicators {
dm.dbConns.replicators[repl].close()
delete(dm.dbConns.replicators, repl)
}
for driverKey := range d { // put all the newly crated DataDBDrivers in DataManager Conns
dm.dbConns.dataDBs[driverKey] = d[driverKey]
}
for replKey := range r {
dm.dbConns.replicators[replKey] = r[replKey]
}
}
return
}
func (dm *DataManager) GetIndexes(ctx *context.Context, idxItmType, tntCtx, idxKey, transactionID string,
cacheRead, cacheWrite bool) (indexes map[string]utils.StringSet, err error) {
if dm == nil {
err = utils.ErrNoDatabaseConn
return
}
if cacheRead && idxKey != utils.EmptyString { // do not check cache if we want all the indexes
if x, ok := Cache.Get(idxItmType, utils.ConcatenatedKey(tntCtx, idxKey)); ok { // Attempt to find in cache first
if x == nil {
return nil, utils.ErrNotFound
}
return map[string]utils.StringSet{
idxKey: x.(utils.StringSet),
}, nil
}
}
dataDB, dbCfg, err := dm.dbConns.GetConn(idxItmType)
if err != nil {
return nil, err
}
if indexes, err = dataDB.GetIndexesDrv(ctx, idxItmType, tntCtx, idxKey, transactionID); err != nil {
if itm := dm.cfg.DbCfg().Items[idxItmType]; err == utils.ErrNotFound && itm.Remote {
if err = dm.connMgr.Call(ctx, dbCfg.RmtConns,
utils.ReplicatorSv1GetIndexes,
&utils.GetIndexesArg{
IdxItmType: idxItmType,
TntCtx: tntCtx,
IdxKey: idxKey,
Tenant: dm.cfg.GeneralCfg().DefaultTenant,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &indexes); err == nil {
err = dataDB.SetIndexesDrv(ctx, idxItmType, tntCtx, indexes, true, utils.NonTransactional)
}
}
if err != nil {
err = utils.CastRPCErr(err)
if err == utils.ErrNotFound && cacheWrite &&
idxKey != utils.EmptyString {
if errCh := Cache.Set(ctx, idxItmType, utils.ConcatenatedKey(tntCtx, idxKey), nil, []string{tntCtx},
true, utils.NonTransactional); errCh != nil {
return nil, errCh
}
}
return nil, err
}
}
if cacheWrite {
for k, v := range indexes {
if err = Cache.Set(ctx, idxItmType, utils.ConcatenatedKey(tntCtx, k), v, []string{tntCtx},
true, utils.NonTransactional); err != nil {
return nil, err
}
}
}
return
}
func (dm *DataManager) SetIndexes(ctx *context.Context, idxItmType, tntCtx string,
indexes map[string]utils.StringSet, commit bool, transactionID string) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
dataDB, dbCfg, err := dm.dbConns.GetConn(idxItmType)
if err != nil {
return err
}
if err = dataDB.SetIndexesDrv(ctx, idxItmType, tntCtx,
indexes, commit, transactionID); err != nil {
return
}
if itm := dm.cfg.DbCfg().Items[idxItmType]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
err = rpl.replicate(ctx,
utils.CacheInstanceToPrefix[idxItmType], tntCtx, // this are used to get the host IDs from cache
utils.ReplicatorSv1SetIndexes,
&utils.SetIndexesArg{
IdxItmType: idxItmType,
TntCtx: tntCtx,
Indexes: indexes,
Tenant: dm.cfg.GeneralCfg().DefaultTenant,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) RemoveIndexes(ctx *context.Context, idxItmType, tntCtx, idxKey string) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
dataDB, dbCfg, err := dm.dbConns.GetConn(idxItmType)
if err != nil {
return err
}
if err = dataDB.RemoveIndexesDrv(ctx, idxItmType, tntCtx, idxKey); err != nil {
return
}
if itm := dm.cfg.DbCfg().Items[idxItmType]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
rpl.replicate(ctx,
utils.CacheInstanceToPrefix[idxItmType], tntCtx, // this are used to get the host IDs from cache
utils.ReplicatorSv1RemoveIndexes,
&utils.GetIndexesArg{
IdxItmType: idxItmType,
TntCtx: tntCtx,
IdxKey: idxKey,
Tenant: dm.cfg.GeneralCfg().DefaultTenant,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func GetAPIBan(ctx *context.Context, ip string, apiKeys []string, single, cacheRead, cacheWrite bool) (banned bool, err error) {
if cacheRead {
if x, ok := Cache.Get(utils.MetaAPIBan, ip); ok && x != nil { // Attempt to find in cache first
return x.(bool), nil
}
}
if single {
if banned, err = baningo.CheckIP(ctx, ip, apiKeys...); err != nil {
return
}
if cacheWrite {
if err = Cache.Set(ctx, utils.MetaAPIBan, ip, banned, nil, true, utils.NonTransactional); err != nil {
return false, err
}
}
return
}
var bannedIPs []string
if bannedIPs, err = baningo.GetBannedIPs(ctx, apiKeys...); err != nil {
return
}
for _, bannedIP := range bannedIPs {
if bannedIP == ip {
banned = true
}
if cacheWrite {
if err = Cache.Set(ctx, utils.MetaAPIBan, bannedIP, true, nil, true, utils.NonTransactional); err != nil {
return false, err
}
}
}
if len(ip) != 0 && !banned && cacheWrite {
if err = Cache.Set(ctx, utils.MetaAPIBan, ip, false, nil, true, utils.NonTransactional); err != nil {
return false, err
}
}
return
}
// checkFilters returns the id of the first Filter that is not valid
// it should be called after the dm nil check
func (dm *DataManager) checkFilters(ctx *context.Context, tenant string, ids []string) (err error) {
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaFilters)
if err != nil {
return err
}
for _, id := range ids {
// in case of inline filter we try to build them
// if they are not correct it should fail here not in indexes
if strings.HasPrefix(id, utils.Meta) {
if fltr, err := NewFilterFromInline(tenant, id); err != nil {
return fmt.Errorf("broken reference to filter: <%s>", id)
} else if err := CheckFilter(fltr); err != nil {
return err
}
} else if x, has := Cache.Get(utils.CacheFilters, // because the method HasDataDrv doesn't use cache
utils.ConcatenatedKey(tenant, id)); has && x == nil { // check to see if filter is already in cache
return fmt.Errorf("broken reference to filter: <%s>", id)
} else if has, err := dataDB.HasDataDrv(ctx, utils.FilterPrefix, // check in local DB if we have the filter
id, tenant); err != nil || !has {
// in case we can not find it localy try to find it in the remote DB
if itm := dm.cfg.DbCfg().Items[utils.MetaFilters]; err == utils.ErrNotFound && itm.Remote {
var fltr *Filter
err = dm.connMgr.Call(ctx, dbCfg.RmtConns, utils.ReplicatorSv1GetFilter,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &fltr)
has = fltr == nil
}
// not in local DB and not in remote DB
if err != nil || !has {
return fmt.Errorf("broken reference to filter: <%s>", id)
}
}
}
return
}
func (dm *DataManager) GetAccount(ctx *context.Context, tenant, id string) (ap *utils.Account, err error) {
if dm == nil {
err = utils.ErrNoDatabaseConn
return
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaAccounts)
if err != nil {
return nil, err
}
ap, err = dataDB.GetAccountDrv(ctx, tenant, id)
if err != nil {
if itm := dm.cfg.DbCfg().Items[utils.MetaAccounts]; err == utils.ErrNotFound && itm.Remote {
if err = dm.connMgr.Call(ctx, dbCfg.RmtConns,
utils.ReplicatorSv1GetAccount,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString,
utils.FirstNonEmpty(dbCfg.RmtConnID,
dm.cfg.GeneralCfg().NodeID)),
}, &ap); err == nil {
err = dataDB.SetAccountDrv(ctx, ap)
}
}
if err != nil {
return nil, err
}
}
return
}
func (dm *DataManager) SetAccount(ctx *context.Context, ap *utils.Account, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
if withIndex {
if err := dm.checkFilters(ctx, ap.Tenant, ap.FilterIDs); err != nil {
// if we get a broken filter do not set the profile
return fmt.Errorf("%+s for item with ID: %+v",
err, ap.TenantID())
}
}
oldRpp, err := dm.GetAccount(ctx, ap.Tenant, ap.ID)
if err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaAccounts)
if err != nil {
return err
}
if err = dataDB.SetAccountDrv(ctx, ap); err != nil {
return err
}
if withIndex {
var oldFiltersIDs *[]string
if oldRpp != nil {
oldFiltersIDs = &oldRpp.FilterIDs
}
if err := updatedIndexes(ctx, dm, utils.CacheAccountsFilterIndexes, ap.Tenant,
utils.EmptyString, ap.ID, oldFiltersIDs, ap.FilterIDs, false); err != nil {
return err
}
}
if itm := dm.cfg.DbCfg().Items[utils.MetaAccounts]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
err = rpl.replicate(ctx,
utils.AccountPrefix, ap.TenantID(), // this are used to get the host IDs from cache
utils.ReplicatorSv1SetAccount,
&utils.AccountWithAPIOpts{
Account: ap,
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
func (dm *DataManager) RemoveAccount(ctx *context.Context, tenant, id string, withIndex bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
oldRpp, err := dm.GetAccount(ctx, tenant, id)
if err != nil && err != utils.ErrNotFound {
return err
}
dataDB, dbCfg, err := dm.dbConns.GetConn(utils.MetaAccounts)
if err != nil {
return err
}
if err = dataDB.RemoveAccountDrv(ctx, tenant, id); err != nil {
return
}
if oldRpp == nil {
return utils.ErrNotFound
}
if withIndex {
if err = removeIndexFiltersItem(ctx, dm, utils.CacheAccountsFilterIndexes, tenant, id, oldRpp.FilterIDs); err != nil {
return
}
if err = removeItemFromFilterIndex(ctx, dm, utils.CacheAccountsFilterIndexes,
tenant, utils.EmptyString, id, oldRpp.FilterIDs); err != nil {
return
}
}
if itm := dm.cfg.DbCfg().Items[utils.MetaAccounts]; itm.Replicate {
rpl := dm.dbConns.GetReplicator(itm.DBConn)
rpl.replicate(ctx,
utils.AccountPrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache
utils.ReplicatorSv1RemoveAccount,
&utils.TenantIDWithAPIOpts{
TenantID: &utils.TenantID{Tenant: tenant, ID: id},
APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID,
dbCfg.RplCache, utils.EmptyString)}, itm)
}
return
}
// DumpDataDB will dump all of datadb from memory to a file. Only available for internal DBs
func (dm *DataManager) DumpDataDB() error {
for _, db := range dm.DBConns().dataDBs {
if err := db.DumpDataDB(); err != nil && err != utils.ErrNotImplemented {
return err
}
}
return nil
}
// Will rewrite every dump file of DataDB. Only available for internal DBs
func (dm *DataManager) RewriteDataDB() error {
for _, db := range dm.DBConns().dataDBs {
if err := db.RewriteDataDB(); err != nil && err != utils.ErrNotImplemented {
return err
}
}
return nil
}
// BackupDataDB will momentarely stop any dumping and rewriting in dataDB, until dump folder is backed up in folder path backupFolderPath. Making zip true will create a zip file in the path instead. Only available for internal DBs
func (dm *DataManager) BackupDataDB(backupFolderPath string, zip bool) error {
for _, db := range dm.DBConns().dataDBs {
if err := db.BackupDataDB(backupFolderPath, zip); err != nil && err != utils.ErrNotImplemented {
return err
}
}
return nil
}
func (dm *DataManager) SetCDR(ctx *context.Context, cdr *utils.CGREvent, allowUpdate bool) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
dataDB, _, err := dm.dbConns.GetConn(utils.MetaCDRs)
if err != nil {
return err
}
return dataDB.SetCDR(ctx, cdr, allowUpdate)
}
func (dm *DataManager) GetCDRs(ctx *context.Context, qryFltr []*Filter, opts map[string]any) ([]*utils.CDR, error) {
if dm == nil {
return nil, utils.ErrNoDatabaseConn
}
dataDB, _, err := dm.dbConns.GetConn(utils.MetaCDRs)
if err != nil {
return nil, err
}
return dataDB.GetCDRs(ctx, qryFltr, opts)
}
func (dm *DataManager) RemoveCDRs(ctx *context.Context, qryFltr []*Filter) (err error) {
if dm == nil {
return utils.ErrNoDatabaseConn
}
dataDB, _, err := dm.dbConns.GetConn(utils.MetaCDRs)
if err != nil {
return err
}
return dataDB.RemoveCDRs(ctx, qryFltr)
}