/* Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments Copyright (C) ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see */ package engine import ( "fmt" "strings" "github.com/cgrates/baningo" "github.com/cgrates/birpc/context" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/guardian" "github.com/cgrates/cgrates/utils" ) var ( filterIndexesPrefixMap = utils.StringSet{ utils.AttributeFilterIndexes: {}, utils.ResourceFilterIndexes: {}, utils.StatFilterIndexes: {}, utils.ThresholdFilterIndexes: {}, utils.RouteFilterIndexes: {}, utils.ChargerFilterIndexes: {}, utils.DispatcherFilterIndexes: {}, utils.RateProfilesFilterIndexPrfx: {}, utils.ActionProfilesFilterIndexPrfx: {}, utils.RateFilterIndexPrfx: {}, utils.ActionPlanIndexes: {}, utils.FilterIndexPrfx: {}, utils.AccountFilterIndexPrfx: {}, } cachePrefixMap = utils.StringSet{ utils.ResourceProfilesPrefix: {}, utils.ResourcesPrefix: {}, utils.StatQueuePrefix: {}, utils.StatQueueProfilePrefix: {}, utils.ThresholdPrefix: {}, utils.ThresholdProfilePrefix: {}, utils.FilterPrefix: {}, utils.RouteProfilePrefix: {}, utils.AttributeProfilePrefix: {}, utils.ChargerProfilePrefix: {}, utils.DispatcherProfilePrefix: {}, utils.DispatcherHostPrefix: {}, utils.MetaDispatchers: {}, // not realy a prefix as this is not stored in DB utils.AccountFilterIndexPrfx: {}, utils.AccountPrefix: {}, utils.RateProfilePrefix: {}, utils.ActionProfilePrefix: {}, utils.AttributeFilterIndexes: {}, utils.ResourceFilterIndexes: {}, utils.StatFilterIndexes: {}, utils.ThresholdFilterIndexes: {}, utils.RouteFilterIndexes: {}, utils.ChargerFilterIndexes: {}, utils.DispatcherFilterIndexes: {}, 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(dataDB DataDB, cacheCfg *config.CacheCfg, connMgr *ConnManager) *DataManager { ms, _ := utils.NewMarshaler(config.CgrConfig().GeneralCfg().DBDataEncoding) return &DataManager{ dataDB: dataDB, cacheCfg: cacheCfg, connMgr: connMgr, ms: ms, } } // DataManager is the data storage manager for CGRateS // transparently manages data retrieval, further serialization and caching type DataManager struct { dataDB DataDB cacheCfg *config.CacheCfg connMgr *ConnManager ms utils.Marshaler } // DataDB exports access to dataDB func (dm *DataManager) DataDB() DataDB { if dm != nil { return dm.dataDB } return nil } 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.cacheCfg.Partitions[utils.CachePrefixToInstance[prfx]].Limit == 0 { return } // *apiban and *dispatchers are not stored in database if prfx == utils.MetaAPIBan || prfx == utils.MetaDispatchers { // 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 { if ids, err = dm.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.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("", config.CgrConfig().GeneralCfg().LockingTimeout, 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("", config.CgrConfig().GeneralCfg().LockingTimeout, resourceLockKey(tntID.Tenant, tntID.ID)) _, err = dm.GetResource(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional) guardian.Guardian.UnguardIDs(lkID) case utils.StatQueueProfilePrefix: tntID := utils.NewTenantID(dataID) lkID := guardian.Guardian.GuardIDs("", config.CgrConfig().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("", config.CgrConfig().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("", config.CgrConfig().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.ThresholdPrefix: tntID := utils.NewTenantID(dataID) lkID := guardian.Guardian.GuardIDs("", config.CgrConfig().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.DispatcherProfilePrefix: tntID := utils.NewTenantID(dataID) _, err = dm.GetDispatcherProfile(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional) case utils.DispatcherHostPrefix: tntID := utils.NewTenantID(dataID) _, err = dm.GetDispatcherHost(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.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.DispatcherFilterIndexes: var tntCtx, idxKey string if tntCtx, idxKey, err = splitFilterIndex(dataID); err != nil { return } _, err = dm.GetIndexes(ctx, utils.CacheDispatcherFilterIndexes, 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, config.CgrConfig().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 { fltr, err = dm.DataDB().GetFilterDrv(ctx, tenant, id) if err != nil { if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaFilters]; err == utils.ErrNotFound && itm.Remote { if err = dm.connMgr.Call(ctx, config.CgrConfig().DataDbCfg().RmtConns, utils.ReplicatorSv1GetFilter, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{Tenant: tenant, ID: id}, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, utils.FirstNonEmpty(config.CgrConfig().DataDbCfg().RmtConnID, config.CgrConfig().GeneralCfg().NodeID)), }, &fltr); err == nil { err = dm.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 } } 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 } if err = dm.DataDB().SetFilterDrv(ctx, fltr); err != nil { return } if withIndex { if err = UpdateFilterIndex(ctx, dm, oldFlt, fltr); err != nil { return } } if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaFilters]; itm.Replicate { err = replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } 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)) } } if err = dm.DataDB().RemoveFilterDrv(ctx, tenant, id); err != nil { return } if oldFlt == nil { return utils.ErrNotFound } if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaFilters]; itm.Replicate { replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } 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 } th, err = dm.dataDB.GetThresholdDrv(ctx, tenant, id) if err != nil { if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaThresholds]; err == utils.ErrNotFound && itm.Remote { if err = dm.connMgr.Call(ctx, config.CgrConfig().DataDbCfg().RmtConns, utils.ReplicatorSv1GetThreshold, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{Tenant: tenant, ID: id}, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, utils.FirstNonEmpty(config.CgrConfig().DataDbCfg().RmtConnID, config.CgrConfig().GeneralCfg().NodeID)), }, &th); err == nil { err = dm.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 } if err = dm.DataDB().SetThresholdDrv(ctx, th); err != nil { return } if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaThresholds]; itm.Replicate { err = replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } return } func (dm *DataManager) RemoveThreshold(ctx *context.Context, tenant, id string) (err error) { if dm == nil { return utils.ErrNoDatabaseConn } if err = dm.DataDB().RemoveThresholdDrv(ctx, tenant, id); err != nil { return } if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaThresholds]; itm.Replicate { replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } 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 } th, err = dm.dataDB.GetThresholdProfileDrv(ctx, tenant, id) if err != nil { if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaThresholdProfiles]; err == utils.ErrNotFound && itm.Remote { if err = dm.connMgr.Call(ctx, config.CgrConfig().DataDbCfg().RmtConns, utils.ReplicatorSv1GetThresholdProfile, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{Tenant: tenant, ID: id}, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, utils.FirstNonEmpty(config.CgrConfig().DataDbCfg().RmtConnID, config.CgrConfig().GeneralCfg().NodeID)), }, &th); err == nil { err = dm.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 } if err = dm.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 := config.CgrConfig().DataDbCfg().Items[utils.MetaThresholdProfiles]; itm.Replicate { if err = replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}); 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 } if err = dm.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 := config.CgrConfig().DataDbCfg().Items[utils.MetaThresholdProfiles]; itm.Replicate { replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } 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 } sq, err = dm.dataDB.GetStatQueueDrv(ctx, tenant, id) if err != nil { if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaStatQueues]; err == utils.ErrNotFound && itm.Remote { if err = dm.connMgr.Call(ctx, config.CgrConfig().DataDbCfg().RmtConns, utils.ReplicatorSv1GetStatQueue, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{Tenant: tenant, ID: id}, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, utils.FirstNonEmpty(config.CgrConfig().DataDbCfg().RmtConnID, config.CgrConfig().GeneralCfg().NodeID)), }, &sq); err == nil { var ssq *StoredStatQueue if dm.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 = dm.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 } var ssq *StoredStatQueue if dm.dataDB.GetStorageType() != utils.MetaInternal { // in case of internal we don't marshal if ssq, err = NewStoredStatQueue(sq, dm.ms); err != nil { return } } if err = dm.dataDB.SetStatQueueDrv(ctx, ssq, sq); err != nil { return } if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaStatQueues]; itm.Replicate { err = replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } return } // RemoveStatQueue removes the StoredStatQueue func (dm *DataManager) RemoveStatQueue(ctx *context.Context, tenant, id string) (err error) { if dm == nil { return utils.ErrNoDatabaseConn } if err = dm.dataDB.RemStatQueueDrv(ctx, tenant, id); err != nil { return } if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaStatQueues]; itm.Replicate { replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } 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 } sqp, err = dm.dataDB.GetStatQueueProfileDrv(ctx, tenant, id) if err != nil { if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaStatQueueProfiles]; err == utils.ErrNotFound && itm.Remote { if err = dm.connMgr.Call(ctx, config.CgrConfig().DataDbCfg().RmtConns, utils.ReplicatorSv1GetStatQueueProfile, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{Tenant: tenant, ID: id}, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, utils.FirstNonEmpty(config.CgrConfig().DataDbCfg().RmtConnID, config.CgrConfig().GeneralCfg().NodeID)), }, &sqp); err == nil { err = dm.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 } if err = dm.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 := config.CgrConfig().DataDbCfg().Items[utils.MetaStatQueueProfiles]; itm.Replicate { if err = replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}); 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 }, config.CgrConfig().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 || !utils.SliceStringEqual(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 }, config.CgrConfig().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 } if err = dm.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 := config.CgrConfig().DataDbCfg().Items[utils.MetaStatQueueProfiles]; itm.Replicate { replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } return dm.RemoveStatQueue(ctx, tenant, id) } func (dm *DataManager) GetResource(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool, transactionID string) (rs *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.(*Resource), nil } } if dm == nil { err = utils.ErrNoDatabaseConn return } rs, err = dm.dataDB.GetResourceDrv(ctx, tenant, id) if err != nil { if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaResources]; err == utils.ErrNotFound && itm.Remote { if err = dm.connMgr.Call(ctx, config.CgrConfig().DataDbCfg().RmtConns, utils.ReplicatorSv1GetResource, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{Tenant: tenant, ID: id}, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, utils.FirstNonEmpty(config.CgrConfig().DataDbCfg().RmtConnID, config.CgrConfig().GeneralCfg().NodeID)), }, &rs); err == nil { err = dm.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 *Resource) (err error) { if dm == nil { return utils.ErrNoDatabaseConn } if err = dm.DataDB().SetResourceDrv(ctx, rs); err != nil { return } if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaResources]; itm.Replicate { err = replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, utils.ResourcesPrefix, rs.TenantID(), // this are used to get the host IDs from cache utils.ReplicatorSv1SetResource, &ResourceWithAPIOpts{ Resource: rs, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } return } func (dm *DataManager) RemoveResource(ctx *context.Context, tenant, id string) (err error) { if dm == nil { return utils.ErrNoDatabaseConn } if err = dm.DataDB().RemoveResourceDrv(ctx, tenant, id); err != nil { return } if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaResources]; itm.Replicate { replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } return } func (dm *DataManager) GetResourceProfile(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool, transactionID string) (rp *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.(*ResourceProfile), nil } } if dm == nil { err = utils.ErrNoDatabaseConn return } rp, err = dm.dataDB.GetResourceProfileDrv(ctx, tenant, id) if err != nil { if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaResourceProfile]; err == utils.ErrNotFound && itm.Remote { if err = dm.connMgr.Call(ctx, config.CgrConfig().DataDbCfg().RmtConns, utils.ReplicatorSv1GetResourceProfile, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{Tenant: tenant, ID: id}, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, utils.FirstNonEmpty(config.CgrConfig().DataDbCfg().RmtConnID, config.CgrConfig().GeneralCfg().NodeID)), }, &rp); err == nil { err = dm.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 *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 } if err = dm.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 := config.CgrConfig().DataDbCfg().Items[utils.MetaResourceProfile]; itm.Replicate { if err = replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, utils.ResourceProfilesPrefix, rp.TenantID(), // this are used to get the host IDs from cache utils.ReplicatorSv1SetResourceProfile, &ResourceProfileWithAPIOpts{ ResourceProfile: rp, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}); 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, &Resource{ Tenant: rp.Tenant, ID: rp.ID, Usages: make(map[string]*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, &Resource{ Tenant: rp.Tenant, ID: rp.ID, Usages: make(map[string]*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 } if err = dm.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 := config.CgrConfig().DataDbCfg().Items[utils.MetaResourceProfile]; itm.Replicate { replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } return dm.RemoveResource(ctx, tenant, id) } func (dm *DataManager) HasData(category, subject, tenant string) (has bool, err error) { if dm == nil { err = utils.ErrNoDatabaseConn return } return dm.DataDB().HasDataDrv(context.TODO(), category, subject, tenant) } func (dm *DataManager) GetRouteProfile(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool, transactionID string) (rpp *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.(*RouteProfile), nil } } if dm == nil { err = utils.ErrNoDatabaseConn return } rpp, err = dm.dataDB.GetRouteProfileDrv(ctx, tenant, id) if err != nil { if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaRouteProfiles]; err == utils.ErrNotFound && itm.Remote { if err = dm.connMgr.Call(ctx, config.CgrConfig().DataDbCfg().RmtConns, utils.ReplicatorSv1GetRouteProfile, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{Tenant: tenant, ID: id}, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, utils.FirstNonEmpty(config.CgrConfig().DataDbCfg().RmtConnID, config.CgrConfig().GeneralCfg().NodeID)), }, &rpp); err == nil { err = dm.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 *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 } if err = dm.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 := config.CgrConfig().DataDbCfg().Items[utils.MetaRouteProfiles]; itm.Replicate { err = replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, utils.RouteProfilePrefix, rpp.TenantID(), // this are used to get the host IDs from cache utils.ReplicatorSv1SetRouteProfile, &RouteProfileWithAPIOpts{ RouteProfile: rpp, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } 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 } if err = dm.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 := config.CgrConfig().DataDbCfg().Items[utils.MetaRouteProfiles]; itm.Replicate { replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } 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 *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.(*AttributeProfile), nil } } if strings.HasPrefix(id, utils.Meta) { attrPrfl, err = 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 { if attrPrfl, err = dm.dataDB.GetAttributeProfileDrv(ctx, tenant, id); err != nil { if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaAttributeProfiles]; err == utils.ErrNotFound && itm.Remote { if err = dm.connMgr.Call(ctx, config.CgrConfig().DataDbCfg().RmtConns, utils.ReplicatorSv1GetAttributeProfile, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{Tenant: tenant, ID: id}, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, utils.FirstNonEmpty(config.CgrConfig().DataDbCfg().RmtConnID, config.CgrConfig().GeneralCfg().NodeID)), }, &attrPrfl); err == nil { err = dm.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 *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(config.CgrConfig().GeneralCfg().RSRSep) if password, err = utils.ComputeHash(password); err != nil { return } if attribute.Value, err = config.NewRSRParsers(password, config.CgrConfig().GeneralCfg().RSRSep); err != nil { return } attribute.Type = utils.MetaConstant } } if err = dm.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 := config.CgrConfig().DataDbCfg().Items[utils.MetaAttributeProfiles]; itm.Replicate { err = replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, utils.AttributeProfilePrefix, ap.TenantID(), // this are used to get the host IDs from cache utils.ReplicatorSv1SetAttributeProfile, &AttributeProfileWithAPIOpts{ AttributeProfile: ap, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } 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 { return err } if err = dm.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 := config.CgrConfig().DataDbCfg().Items[utils.MetaAttributeProfiles]; itm.Replicate { replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } return } func (dm *DataManager) GetChargerProfile(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool, transactionID string) (cpp *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.(*ChargerProfile), nil } } if dm == nil { err = utils.ErrNoDatabaseConn return } cpp, err = dm.dataDB.GetChargerProfileDrv(ctx, tenant, id) if err != nil { if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaChargerProfiles]; err == utils.ErrNotFound && itm.Remote { if err = dm.connMgr.Call(ctx, config.CgrConfig().DataDbCfg().RmtConns, utils.ReplicatorSv1GetChargerProfile, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{Tenant: tenant, ID: id}, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, utils.FirstNonEmpty(config.CgrConfig().DataDbCfg().RmtConnID, config.CgrConfig().GeneralCfg().NodeID)), }, &cpp); err == nil { err = dm.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 *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 } if err = dm.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 := config.CgrConfig().DataDbCfg().Items[utils.MetaChargerProfiles]; itm.Replicate { err = replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, utils.ChargerProfilePrefix, cpp.TenantID(), // this are used to get the host IDs from cache utils.ReplicatorSv1SetChargerProfile, &ChargerProfileWithAPIOpts{ ChargerProfile: cpp, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } 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 } if err = dm.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 := config.CgrConfig().DataDbCfg().Items[utils.MetaChargerProfiles]; itm.Replicate { replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } return } func (dm *DataManager) GetDispatcherProfile(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool, transactionID string) (dpp *DispatcherProfile, err error) { tntID := utils.ConcatenatedKey(tenant, id) if cacheRead { if x, ok := Cache.Get(utils.CacheDispatcherProfiles, tntID); ok { if x == nil { return nil, utils.ErrDSPProfileNotFound } return x.(*DispatcherProfile), nil } } if dm == nil { err = utils.ErrNoDatabaseConn return } dpp, err = dm.dataDB.GetDispatcherProfileDrv(ctx, tenant, id) if err != nil { if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaDispatcherProfiles]; err == utils.ErrDSPProfileNotFound && itm.Remote { if err = dm.connMgr.Call(ctx, config.CgrConfig().DataDbCfg().RmtConns, utils.ReplicatorSv1GetDispatcherProfile, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{Tenant: tenant, ID: id}, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, utils.FirstNonEmpty(config.CgrConfig().DataDbCfg().RmtConnID, config.CgrConfig().GeneralCfg().NodeID)), }, &dpp); err == nil { err = dm.dataDB.SetDispatcherProfileDrv(ctx, dpp) } } if err != nil { err = utils.CastRPCErr(err) if err == utils.ErrDSPProfileNotFound && cacheWrite { if errCh := Cache.Set(ctx, utils.CacheDispatcherProfiles, tntID, nil, nil, cacheCommit(transactionID), transactionID); errCh != nil { return nil, errCh } } return nil, err } } if cacheWrite { if errCh := Cache.Set(ctx, utils.CacheDispatcherProfiles, tntID, dpp, nil, cacheCommit(transactionID), transactionID); errCh != nil { return nil, errCh } } return } func (dm *DataManager) SetDispatcherProfile(ctx *context.Context, dpp *DispatcherProfile, withIndex bool) (err error) { if dm == nil { return utils.ErrNoDatabaseConn } if withIndex { if err := dm.checkFilters(ctx, dpp.Tenant, dpp.FilterIDs); err != nil { // if we get a broken filter do not set the profile return fmt.Errorf("%+s for item with ID: %+v", err, dpp.TenantID()) } } oldDpp, err := dm.GetDispatcherProfile(ctx, dpp.Tenant, dpp.ID, true, false, utils.NonTransactional) if err != nil && err != utils.ErrDSPProfileNotFound { return err } if err = dm.DataDB().SetDispatcherProfileDrv(ctx, dpp); err != nil { return err } if withIndex { var oldFiltersIDs *[]string if oldDpp != nil { oldFiltersIDs = &oldDpp.FilterIDs } if err = updatedIndexes(ctx, dm, utils.CacheDispatcherFilterIndexes, dpp.Tenant, utils.EmptyString, dpp.ID, oldFiltersIDs, dpp.FilterIDs, false); err != nil { return } } if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaDispatcherProfiles]; itm.Replicate { err = replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, utils.DispatcherProfilePrefix, dpp.TenantID(), // this are used to get the host IDs from cache utils.ReplicatorSv1SetDispatcherProfile, &DispatcherProfileWithAPIOpts{ DispatcherProfile: dpp, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } return } func (dm *DataManager) RemoveDispatcherProfile(ctx *context.Context, tenant, id string, withIndex bool) (err error) { if dm == nil { return utils.ErrNoDatabaseConn } oldDpp, err := dm.GetDispatcherProfile(ctx, tenant, id, true, false, utils.NonTransactional) if err != nil && err != utils.ErrNotFound { return err } if err = dm.DataDB().RemoveDispatcherProfileDrv(ctx, tenant, id); err != nil { return } if oldDpp == nil { return utils.ErrDSPProfileNotFound } if withIndex { if err = removeIndexFiltersItem(ctx, dm, utils.CacheDispatcherFilterIndexes, tenant, id, oldDpp.FilterIDs); err != nil { return } if err = removeItemFromFilterIndex(ctx, dm, utils.CacheDispatcherFilterIndexes, tenant, utils.EmptyString, id, oldDpp.FilterIDs); err != nil { return } } if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaDispatcherProfiles]; itm.Replicate { replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, utils.DispatcherProfilePrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache utils.ReplicatorSv1RemoveDispatcherProfile, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{Tenant: tenant, ID: id}, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } return } func (dm *DataManager) GetDispatcherHost(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool, transactionID string) (dH *DispatcherHost, err error) { tntID := utils.ConcatenatedKey(tenant, id) if cacheRead { if x, ok := Cache.Get(utils.CacheDispatcherHosts, tntID); ok { if x == nil { return nil, utils.ErrDSPHostNotFound } return x.(*DispatcherHost), nil } } if dm == nil { err = utils.ErrNoDatabaseConn return } dH, err = dm.dataDB.GetDispatcherHostDrv(ctx, tenant, id) if err != nil { if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaDispatcherHosts]; err == utils.ErrDSPHostNotFound && itm.Remote { if err = dm.connMgr.Call(ctx, config.CgrConfig().DataDbCfg().RmtConns, utils.ReplicatorSv1GetDispatcherHost, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{Tenant: tenant, ID: id}, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, utils.FirstNonEmpty(config.CgrConfig().DataDbCfg().RmtConnID, config.CgrConfig().GeneralCfg().NodeID)), }, &dH); err == nil { err = dm.dataDB.SetDispatcherHostDrv(ctx, dH) } } if err != nil { err = utils.CastRPCErr(err) if err == utils.ErrDSPHostNotFound && cacheWrite { if errCh := Cache.Set(ctx, utils.CacheDispatcherHosts, tntID, nil, nil, cacheCommit(transactionID), transactionID); errCh != nil { return nil, errCh } } return nil, err } } if cacheWrite { if err = Cache.Set(ctx, utils.CacheDispatcherHosts, tntID, dH, nil, cacheCommit(transactionID), transactionID); err != nil { return nil, err } } return } func (dm *DataManager) SetDispatcherHost(ctx *context.Context, dpp *DispatcherHost) (err error) { if dm == nil { return utils.ErrNoDatabaseConn } if err = dm.DataDB().SetDispatcherHostDrv(ctx, dpp); err != nil { return } if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaDispatcherHosts]; itm.Replicate { err = replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, utils.DispatcherHostPrefix, dpp.TenantID(), // this are used to get the host IDs from cache utils.ReplicatorSv1SetDispatcherHost, &DispatcherHostWithAPIOpts{ DispatcherHost: dpp, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } return } func (dm *DataManager) RemoveDispatcherHost(ctx *context.Context, tenant, id string) (err error) { if dm == nil { return utils.ErrNoDatabaseConn } oldDpp, err := dm.GetDispatcherHost(ctx, tenant, id, true, false, utils.NonTransactional) if err != nil && err != utils.ErrDSPHostNotFound { return err } if err = dm.DataDB().RemoveDispatcherHostDrv(ctx, tenant, id); err != nil { return } if oldDpp == nil { return utils.ErrNotFound } if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaDispatcherHosts]; itm.Replicate { replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, utils.DispatcherHostPrefix, utils.ConcatenatedKey(tenant, id), // this are used to get the host IDs from cache utils.ReplicatorSv1RemoveDispatcherHost, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{Tenant: tenant, ID: id}, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } 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 } loadIDs, err = dm.DataDB().GetItemLoadIDsDrv(ctx, itemIDPrefix) if err != nil { if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaLoadIDs]; err == utils.ErrNotFound && itm.Remote { if err = dm.connMgr.Call(ctx, config.CgrConfig().DataDbCfg().RmtConns, utils.ReplicatorSv1GetItemLoadIDs, &utils.StringWithAPIOpts{ Arg: itemIDPrefix, Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, utils.FirstNonEmpty(config.CgrConfig().DataDbCfg().RmtConnID, config.CgrConfig().GeneralCfg().NodeID)), }, &loadIDs); err == nil { err = dm.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 } if err = dm.DataDB().SetLoadIDsDrv(ctx, loadIDs); err != nil { return } if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaLoadIDs]; itm.Replicate { objIDs := make([]string, 0, len(loadIDs)) for k := range loadIDs { objIDs = append(objIDs, k) } err = replicateMultipleIDs(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, utils.LoadIDPrefix, objIDs, // this are used to get the host IDs from cache utils.ReplicatorSv1SetLoadIDs, &utils.LoadIDsWithAPIOpts{ LoadIDs: loadIDs, Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, config.CgrConfig().DataDbCfg().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 } rpp, err = dm.dataDB.GetRateProfileDrv(ctx, tenant, id) if err != nil { if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaRateProfiles]; err == utils.ErrNotFound && itm.Remote { if err = dm.connMgr.Call(ctx, config.CgrConfig().DataDbCfg().RmtConns, utils.ReplicatorSv1GetRateProfile, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{Tenant: tenant, ID: id}, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, utils.FirstNonEmpty(config.CgrConfig().DataDbCfg().RmtConnID, config.CgrConfig().GeneralCfg().NodeID)), }, &rpp); err == nil { rpp.Sort() err = dm.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 } return dm.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 if err = dm.DataDB().SetRateProfileDrv(ctx, rpp, optOverwrite); err != nil { return err } if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaRateProfiles]; itm.Replicate { err = replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } 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 } if err = dm.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 := config.CgrConfig().DataDbCfg().Items[utils.MetaRateProfiles]; itm.Replicate { replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } 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) } } if err = dm.DataDB().RemoveRateProfileDrv(ctx, tenant, id, rateIDs); err != nil { return } if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaRateProfiles]; itm.Replicate { err = replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } return } func (dm *DataManager) GetActionProfile(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool, transactionID string) (ap *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.(*ActionProfile), nil } } if dm == nil { err = utils.ErrNoDatabaseConn return } ap, err = dm.dataDB.GetActionProfileDrv(ctx, tenant, id) if err != nil { if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaActionProfiles]; err == utils.ErrNotFound && itm.Remote { if err = dm.connMgr.Call(ctx, config.CgrConfig().DataDbCfg().RmtConns, utils.ReplicatorSv1GetActionProfile, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{Tenant: tenant, ID: id}, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, utils.FirstNonEmpty(config.CgrConfig().DataDbCfg().RmtConnID, config.CgrConfig().GeneralCfg().NodeID)), }, &ap); err == nil { err = dm.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 *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 } if err = dm.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 := config.CgrConfig().DataDbCfg().Items[utils.MetaActionProfiles]; itm.Replicate { err = replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, utils.ActionProfilePrefix, ap.TenantID(), // this are used to get the host IDs from cache utils.ReplicatorSv1SetActionProfile, &ActionProfileWithAPIOpts{ ActionProfile: ap, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } 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 } if err = dm.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 := config.CgrConfig().DataDbCfg().Items[utils.MetaActionProfiles]; itm.Replicate { replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } return } // Reconnect reconnects to the DB when the config was changed func (dm *DataManager) Reconnect(d DataDB) { // ToDo: consider locking dm.dataDB.Close() dm.dataDB = d } 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 } } if indexes, err = dm.DataDB().GetIndexesDrv(ctx, idxItmType, tntCtx, idxKey, transactionID); err != nil { if itm := config.CgrConfig().DataDbCfg().Items[idxItmType]; err == utils.ErrNotFound && itm.Remote { if err = dm.connMgr.Call(ctx, config.CgrConfig().DataDbCfg().RmtConns, utils.ReplicatorSv1GetIndexes, &utils.GetIndexesArg{ IdxItmType: idxItmType, TntCtx: tntCtx, IdxKey: idxKey, Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, utils.FirstNonEmpty(config.CgrConfig().DataDbCfg().RmtConnID, config.CgrConfig().GeneralCfg().NodeID)), }, &indexes); err == nil { err = dm.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 } if err = dm.DataDB().SetIndexesDrv(ctx, idxItmType, tntCtx, indexes, commit, transactionID); err != nil { return } if itm := config.CgrConfig().DataDbCfg().Items[idxItmType]; itm.Replicate { err = replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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: config.CgrConfig().GeneralCfg().DefaultTenant, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } return } func (dm *DataManager) RemoveIndexes(ctx *context.Context, idxItmType, tntCtx, idxKey string) (err error) { if dm == nil { return utils.ErrNoDatabaseConn } if err = dm.DataDB().RemoveIndexesDrv(ctx, idxItmType, tntCtx, idxKey); err != nil { return } if itm := config.CgrConfig().DataDbCfg().Items[idxItmType]; itm.Replicate { replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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: config.CgrConfig().GeneralCfg().DefaultTenant, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } 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) { 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 := dm.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 := config.CgrConfig().DataDbCfg().Items[utils.MetaFilters]; err == utils.ErrNotFound && itm.Remote { var fltr *Filter err = dm.connMgr.Call(ctx, config.CgrConfig().DataDbCfg().RmtConns, utils.ReplicatorSv1GetFilter, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{Tenant: tenant, ID: id}, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, utils.FirstNonEmpty(config.CgrConfig().DataDbCfg().RmtConnID, config.CgrConfig().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 } ap, err = dm.dataDB.GetAccountDrv(ctx, tenant, id) if err != nil { if itm := config.CgrConfig().DataDbCfg().Items[utils.MetaAccounts]; err == utils.ErrNotFound && itm.Remote { if err = dm.connMgr.Call(ctx, config.CgrConfig().DataDbCfg().RmtConns, utils.ReplicatorSv1GetAccount, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{Tenant: tenant, ID: id}, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, utils.EmptyString, utils.FirstNonEmpty(config.CgrConfig().DataDbCfg().RmtConnID, config.CgrConfig().GeneralCfg().NodeID)), }, &ap); err == nil { err = dm.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 } if err = dm.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 := config.CgrConfig().DataDbCfg().Items[utils.MetaAccounts]; itm.Replicate { err = replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } 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 } if err = dm.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 := config.CgrConfig().DataDbCfg().Items[utils.MetaAccounts]; itm.Replicate { replicate(ctx, dm.connMgr, config.CgrConfig().DataDbCfg().RplConns, config.CgrConfig().DataDbCfg().RplFiltered, 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, config.CgrConfig().DataDbCfg().RplCache, utils.EmptyString)}) } return }